Overview
S Balance
0 S
S Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
Flags
Compiler Version
v0.8.6+commit.11564f7e
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; import "../SimpleReadAccessController.sol"; import "../interfaces/AccessControllerInterface.sol"; import "../interfaces/TypeAndVersionInterface.sol"; /* dev dependencies - to be re/moved after audit */ import "./interfaces/FlagsInterface.sol"; /** * @title The Flags contract * @notice Allows flags to signal to any reader on the access control list. * The owner can set flags, or designate other addresses to set flags. * Raise flag actions are controlled by its own access controller. * Lower flag actions are controlled by its own access controller. * An expected pattern is to allow addresses to raise flags on themselves, so if you are subscribing to * FlagOn events you should filter for addresses you care about. */ contract Flags is TypeAndVersionInterface, FlagsInterface, SimpleReadAccessController { AccessControllerInterface public raisingAccessController; AccessControllerInterface public loweringAccessController; mapping(address => bool) private flags; event FlagRaised(address indexed subject); event FlagLowered(address indexed subject); event RaisingAccessControllerUpdated(address indexed previous, address indexed current); event LoweringAccessControllerUpdated(address indexed previous, address indexed current); /** * @param racAddress address for the raising access controller. * @param lacAddress address for the lowering access controller. */ constructor(address racAddress, address lacAddress) { setRaisingAccessController(racAddress); setLoweringAccessController(lacAddress); } /** * @notice versions: * * - Flags 1.1.0: upgraded to solc 0.8, added lowering access controller * - Flags 1.0.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "Flags 1.1.0"; } /** * @notice read the warning flag status of a contract address. * @param subject The contract address being checked for a flag. * @return A true value indicates that a flag was raised and a * false value indicates that no flag was raised. */ function getFlag(address subject) external view override checkAccess returns (bool) { return flags[subject]; } /** * @notice read the warning flag status of a contract address. * @param subjects An array of addresses being checked for a flag. * @return An array of bools where a true value for any flag indicates that * a flag was raised and a false value indicates that no flag was raised. */ function getFlags(address[] calldata subjects) external view override checkAccess returns (bool[] memory) { bool[] memory responses = new bool[](subjects.length); for (uint256 i = 0; i < subjects.length; i++) { responses[i] = flags[subjects[i]]; } return responses; } /** * @notice enable the warning flag for an address. * Access is controlled by raisingAccessController, except for owner * who always has access. * @param subject The contract address whose flag is being raised */ function raiseFlag(address subject) external override { require(_allowedToRaiseFlags(), "Not allowed to raise flags"); _tryToRaiseFlag(subject); } /** * @notice enable the warning flags for multiple addresses. * Access is controlled by raisingAccessController, except for owner * who always has access. * @param subjects List of the contract addresses whose flag is being raised */ function raiseFlags(address[] calldata subjects) external override { require(_allowedToRaiseFlags(), "Not allowed to raise flags"); for (uint256 i = 0; i < subjects.length; i++) { _tryToRaiseFlag(subjects[i]); } } /** * @notice allows owner to disable the warning flags for an addresses. * Access is controlled by loweringAccessController, except for owner * who always has access. * @param subject The contract address whose flag is being lowered */ function lowerFlag(address subject) external override { require(_allowedToLowerFlags(), "Not allowed to lower flags"); _tryToLowerFlag(subject); } /** * @notice allows owner to disable the warning flags for multiple addresses. * Access is controlled by loweringAccessController, except for owner * who always has access. * @param subjects List of the contract addresses whose flag is being lowered */ function lowerFlags(address[] calldata subjects) external override { require(_allowedToLowerFlags(), "Not allowed to lower flags"); for (uint256 i = 0; i < subjects.length; i++) { address subject = subjects[i]; _tryToLowerFlag(subject); } } /** * @notice allows owner to change the access controller for raising flags. * @param racAddress new address for the raising access controller. */ function setRaisingAccessController(address racAddress) public override onlyOwner { address previous = address(raisingAccessController); if (previous != racAddress) { raisingAccessController = AccessControllerInterface(racAddress); emit RaisingAccessControllerUpdated(previous, racAddress); } } function setLoweringAccessController(address lacAddress) public override onlyOwner { address previous = address(loweringAccessController); if (previous != lacAddress) { loweringAccessController = AccessControllerInterface(lacAddress); emit LoweringAccessControllerUpdated(previous, lacAddress); } } // PRIVATE function _allowedToRaiseFlags() private view returns (bool) { return msg.sender == owner() || raisingAccessController.hasAccess(msg.sender, msg.data); } function _allowedToLowerFlags() private view returns (bool) { return msg.sender == owner() || loweringAccessController.hasAccess(msg.sender, msg.data); } function _tryToRaiseFlag(address subject) private { if (!flags[subject]) { flags[subject] = true; emit FlagRaised(subject); } } function _tryToLowerFlag(address subject) private { if (flags[subject]) { flags[subject] = false; emit FlagLowered(subject); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {CBORChainlink} from "./vendor/CBORChainlink.sol"; import {BufferChainlink} from "./vendor/BufferChainlink.sol"; /** * @title Library for common Chainlink functions * @dev Uses imported CBOR library for encoding to buffer */ library Chainlink { uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase using CBORChainlink for BufferChainlink.buffer; struct Request { bytes32 id; address callbackAddress; bytes4 callbackFunctionId; uint256 nonce; BufferChainlink.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 jobId The Job Specification ID * @param callbackAddr The callback address * @param callbackFunc The callback function signature * @return The initialized request */ function initialize( Request memory self, bytes32 jobId, address callbackAddr, bytes4 callbackFunc ) internal pure returns (Chainlink.Request memory) { BufferChainlink.init(self.buf, defaultBufferSize); self.id = jobId; self.callbackAddress = callbackAddr; self.callbackFunctionId = callbackFunc; 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 memory data) internal pure { BufferChainlink.init(self.buf, data.length); BufferChainlink.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 memory key, string memory 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 memory key, bytes memory 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 memory 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 memory 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 memory 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(); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.19; import {BufferChainlink} from "./BufferChainlink.sol"; library CBORChainlink { using BufferChainlink for BufferChainlink.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_TAG = 6; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; uint8 private constant TAG_TYPE_BIGNUM = 2; uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 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 { buf.appendUint8(uint8((major << 5) | 27)); buf.appendInt(value, 8); } } function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure { buf.appendUint8(uint8((major << 5) | 31)); } function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure { if(value > 0xFFFFFFFFFFFFFFFF) { encodeBigNum(buf, value); } else { encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); } } function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure { if(value < -0x10000000000000000) { encodeSignedBigNum(buf, value); } else if(value > 0xFFFFFFFFFFFFFFFF) { encodeBigNum(buf, uint(value)); } else if(value >= 0) { encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value))); } else { encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value))); } } function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure { encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); buf.append(value); } function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure { buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); encodeBytes(buf, abi.encode(value)); } function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure { buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); encodeBytes(buf, abi.encode(uint256(-1 - input))); } function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure { encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); buf.append(bytes(value)); } function startArray(BufferChainlink.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); } function startMap(BufferChainlink.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); } function endSequence(BufferChainlink.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @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 BufferChainlink { /** * @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; uint256 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, uint256 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, uint256 capacity) private pure { bytes memory oldbuf = buf.buf; init(buf, capacity); append(buf, oldbuf); } function max(uint256 a, uint256 b) private pure returns (uint256) { 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, uint256 off, bytes memory data, uint256 len ) internal pure returns (buffer memory) { require(len <= data.length); if (off + len > buf.capacity) { resize(buf, max(buf.capacity, len + off) * 2); } uint256 dest; uint256 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 unchecked { uint256 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, uint256 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, uint256 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, uint256 off, bytes32 data, uint256 len ) private pure returns (buffer memory) { if (len + off > buf.capacity) { resize(buf, (len + off) * 2); } unchecked { uint256 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, uint256 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, uint256 off, uint256 data, uint256 len ) private pure returns (buffer memory) { if (len + off > buf.capacity) { resize(buf, (len + off) * 2); } uint256 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, uint256 data, uint256 len ) internal pure returns (buffer memory) { return writeInt(buf, buf.buf.length, data, len); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../Chainlink.sol"; import "../vendor/CBORChainlink.sol"; import "../vendor/BufferChainlink.sol"; contract ChainlinkTestHelper { using Chainlink for Chainlink.Request; using CBORChainlink for BufferChainlink.buffer; Chainlink.Request private req; event RequestData(bytes payload); function closeEvent() public { emit RequestData(req.buf.buf); } function setBuffer(bytes memory data) public { Chainlink.Request memory r2 = req; r2.setBuffer(data); req = r2; } function add(string memory _key, string memory _value) public { Chainlink.Request memory r2 = req; r2.add(_key, _value); req = r2; } function addBytes(string memory _key, bytes memory _value) public { Chainlink.Request memory r2 = req; r2.addBytes(_key, _value); req = r2; } function addInt(string memory _key, int256 _value) public { Chainlink.Request memory r2 = req; r2.addInt(_key, _value); req = r2; } function addUint(string memory _key, uint256 _value) public { Chainlink.Request memory r2 = req; r2.addUint(_key, _value); req = r2; } // Temporarily have method receive bytes32[] memory until experimental // string[] memory can be invoked from truffle tests. function addStringArray(string memory _key, string[] memory _values) public { Chainlink.Request memory r2 = req; r2.addStringArray(_key, _values); req = r2; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./Chainlink.sol"; import "./interfaces/ENSInterface.sol"; import "./interfaces/LinkTokenInterface.sol"; import "./interfaces/ChainlinkRequestInterface.sol"; import "./interfaces/OperatorInterface.sol"; import "./interfaces/PointerInterface.sol"; import {ENSResolver as ENSResolver_Chainlink} from "./vendor/ENSResolver.sol"; /** * @title The ChainlinkClient contract * @notice Contract writers can inherit this contract in order to create requests for the * Chainlink network */ abstract contract ChainlinkClient { using Chainlink for Chainlink.Request; uint256 internal constant LINK_DIVISIBILITY = 10**18; uint256 private constant AMOUNT_OVERRIDE = 0; address private constant SENDER_OVERRIDE = address(0); uint256 private constant ORACLE_ARGS_VERSION = 1; uint256 private constant OPERATOR_ARGS_VERSION = 2; bytes32 private constant ENS_TOKEN_SUBNAME = keccak256("link"); bytes32 private constant ENS_ORACLE_SUBNAME = keccak256("oracle"); address private constant LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; ENSInterface private s_ens; bytes32 private s_ensNode; LinkTokenInterface private s_link; OperatorInterface private s_oracle; uint256 private s_requestCount = 1; mapping(bytes32 => address) private s_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 callbackAddr address to operate the callback on * @param callbackFunctionSignature function signature to use for the callback * @return A Chainlink Request struct in memory */ function buildChainlinkRequest( bytes32 specId, address callbackAddr, bytes4 callbackFunctionSignature ) internal pure returns (Chainlink.Request memory) { Chainlink.Request memory req; return req.initialize(specId, callbackAddr, callbackFunctionSignature); } /** * @notice Creates a request that can hold additional parameters * @param specId The Job Specification ID that the request will be created for * @param callbackFunctionSignature function signature to use for the callback * @return A Chainlink Request struct in memory */ function buildOperatorRequest(bytes32 specId, bytes4 callbackFunctionSignature) internal view returns (Chainlink.Request memory) { Chainlink.Request memory req; return req.initialize(specId, address(this), 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 requestId The request ID */ function sendChainlinkRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) { return sendChainlinkRequestTo(address(s_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 oracleAddress 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 requestId The request ID */ function sendChainlinkRequestTo( address oracleAddress, Chainlink.Request memory req, uint256 payment ) internal returns (bytes32 requestId) { uint256 nonce = s_requestCount; s_requestCount = nonce + 1; bytes memory encodedRequest = abi.encodeWithSelector( ChainlinkRequestInterface.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, address(this), req.callbackFunctionId, nonce, ORACLE_ARGS_VERSION, req.buf.buf ); return _rawRequest(oracleAddress, nonce, payment, encodedRequest); } /** * @notice Creates a Chainlink request to the stored oracle address * @dev This function supports multi-word response * @dev Calls `sendOperatorRequestTo` with the stored oracle address * @param req The initialized Chainlink Request * @param payment The amount of LINK to send for the request * @return requestId The request ID */ function sendOperatorRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) { return sendOperatorRequestTo(address(s_oracle), req, payment); } /** * @notice Creates a Chainlink request to the specified oracle address * @dev This function supports multi-word response * @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 oracleAddress 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 requestId The request ID */ function sendOperatorRequestTo( address oracleAddress, Chainlink.Request memory req, uint256 payment ) internal returns (bytes32 requestId) { uint256 nonce = s_requestCount; s_requestCount = nonce + 1; bytes memory encodedRequest = abi.encodeWithSelector( OperatorInterface.operatorRequest.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.callbackFunctionId, nonce, OPERATOR_ARGS_VERSION, req.buf.buf ); return _rawRequest(oracleAddress, nonce, payment, encodedRequest); } /** * @notice Make a request to an oracle * @param oracleAddress The address of the oracle for the request * @param nonce used to generate the request ID * @param payment The amount of LINK to send for the request * @param encodedRequest data encoded for request type specific format * @return requestId The request ID */ function _rawRequest( address oracleAddress, uint256 nonce, uint256 payment, bytes memory encodedRequest ) private returns (bytes32 requestId) { requestId = keccak256(abi.encodePacked(this, nonce)); s_pendingRequests[requestId] = oracleAddress; emit ChainlinkRequested(requestId); require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle"); } /** * @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 { OperatorInterface requested = OperatorInterface(s_pendingRequests[requestId]); delete s_pendingRequests[requestId]; emit ChainlinkCancelled(requestId); requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration); } /** * @notice the next request count to be used in generating a nonce * @dev starts at 1 in order to ensure consistent gas cost * @return returns the next request count to be used in a nonce */ function getNextRequestCount() internal view returns (uint256) { return s_requestCount; } /** * @notice Sets the stored oracle address * @param oracleAddress The address of the oracle contract */ function setChainlinkOracle(address oracleAddress) internal { s_oracle = OperatorInterface(oracleAddress); } /** * @notice Sets the LINK token address * @param linkAddress The address of the LINK token contract */ function setChainlinkToken(address linkAddress) internal { s_link = LinkTokenInterface(linkAddress); } /** * @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(s_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(s_oracle); } /** * @notice Allows for a request which was created on another contract to be fulfilled * on this contract * @param oracleAddress The address of the oracle contract that will fulfill the request * @param requestId The request ID used for the response */ function addChainlinkExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) { s_pendingRequests[requestId] = oracleAddress; } /** * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS * @dev Accounts for subnodes having different resolvers * @param ensAddress The address of the ENS contract * @param node The ENS node hash */ function useChainlinkWithENS(address ensAddress, bytes32 node) internal { s_ens = ENSInterface(ensAddress); s_ensNode = node; bytes32 linkSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_TOKEN_SUBNAME)); ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_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(s_ensNode, ENS_ORACLE_SUBNAME)); ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(oracleSubnode)); setChainlinkOracle(resolver.addr(oracleSubnode)); } /** * @notice Ensures that the fulfillment is valid for this contract * @dev Use if the contract developer prefers methods instead of modifiers for validation * @param requestId The request ID for fulfillment */ function validateChainlinkCallback(bytes32 requestId) internal recordChainlinkFulfillment(requestId) // solhint-disable-next-line no-empty-blocks { } /** * @dev Reverts if the sender is not the oracle of the request. * Emits ChainlinkFulfilled event. * @param requestId The request ID for fulfillment */ modifier recordChainlinkFulfillment(bytes32 requestId) { require(msg.sender == s_pendingRequests[requestId], "Source must be the oracle of the request"); delete s_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(s_pendingRequests[requestId] == address(0), "Request is already pending"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; 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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface LinkTokenInterface { function allowance(address owner, address spender) external view returns (uint256 remaining); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external view returns (uint256 balance); function decimals() external view returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external view returns (string memory tokenName); function symbol() external view returns (string memory tokenSymbol); function totalSupply() external view returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall( address to, uint256 value, bytes calldata data ) external returns (bool success); function transferFrom( address from, address to, uint256 value ) external returns (bool success); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 requestPrice, bytes32 serviceAgreementID, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 dataVersion, bytes calldata data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./OracleInterface.sol"; import "./ChainlinkRequestInterface.sol"; interface OperatorInterface is OracleInterface, ChainlinkRequestInterface { function operatorRequest( address sender, uint256 payment, bytes32 specId, bytes4 callbackFunctionId, uint256 nonce, uint256 dataVersion, bytes calldata data ) external; function fulfillOracleRequest2( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data ) external returns (bool); function ownerTransferAndCall( address to, uint256 value, bytes calldata data ) external returns (bool success); function distributeFunds(address payable[] calldata receivers, uint256[] calldata amounts) external payable; function getAuthorizedSenders() external returns (address[] memory); function setAuthorizedSenders(address[] calldata senders) external; function getForwarder() external returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface PointerInterface { function getAddress() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; abstract contract ENSResolver { function addr(bytes32 node) public view virtual returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function isAuthorizedSender(address node) external view returns (bool); function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../ChainlinkClient.sol"; contract ChainlinkClientTestHelper is ChainlinkClient { constructor(address _link, address _oracle) { setChainlinkToken(_link); setChainlinkOracle(_oracle); } event Request(bytes32 id, address callbackAddress, bytes4 callbackfunctionSelector, bytes data); event LinkAmount(uint256 amount); function publicNewRequest( bytes32 _id, address _address, bytes memory _fulfillmentSignature ) public { Chainlink.Request memory req = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); emit Request(req.id, req.callbackAddress, req.callbackFunctionId, req.buf.buf); } function publicRequest( bytes32 _id, address _address, bytes memory _fulfillmentSignature, uint256 _wei ) public { Chainlink.Request memory req = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); sendChainlinkRequest(req, _wei); } function publicRequestRunTo( address _oracle, bytes32 _id, address _address, bytes memory _fulfillmentSignature, uint256 _wei ) public { Chainlink.Request memory run = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); sendChainlinkRequestTo(_oracle, run, _wei); } function publicRequestOracleData( bytes32 _id, bytes memory _fulfillmentSignature, uint256 _wei ) public { Chainlink.Request memory req = buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature))); sendOperatorRequest(req, _wei); } function publicRequestOracleDataFrom( address _oracle, bytes32 _id, address _address, bytes memory _fulfillmentSignature, uint256 _wei ) public { Chainlink.Request memory run = buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature))); sendOperatorRequestTo(_oracle, run, _wei); } function publicCancelRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunctionId, uint256 _expiration ) public { cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); } function publicChainlinkToken() public view returns (address) { return chainlinkTokenAddress(); } function publicFulfillChainlinkRequest(bytes32 _requestId, bytes32) public { fulfillRequest(_requestId, bytes32(0)); } function fulfillRequest(bytes32 _requestId, bytes32) public { validateChainlinkCallback(_requestId); } function publicLINK(uint256 _amount) public { emit LinkAmount(LINK_DIVISIBILITY * _amount); } function publicOracleAddress() public view returns (address) { return chainlinkOracleAddress(); } function publicAddExternalRequest(address _oracle, bytes32 _requestId) public { addChainlinkExternalRequest(_oracle, _requestId); } }
// SPDX-License-Identifier: MIT // Example of a single consumer contract which owns the subscription. pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../dev/VRFConsumerBaseV2.sol"; contract VRFSingleConsumerExample is VRFConsumerBaseV2 { VRFCoordinatorV2Interface COORDINATOR; LinkTokenInterface LINKTOKEN; struct RequestConfig { uint64 subId; uint32 callbackGasLimit; uint16 requestConfirmations; uint32 numWords; bytes32 keyHash; } RequestConfig public s_requestConfig; uint256[] public s_randomWords; uint256 public s_requestId; constructor( address vrfCoordinator, address link, uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords, bytes32 keyHash ) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); s_requestConfig = RequestConfig({ subId: 0, // Unset callbackGasLimit: callbackGasLimit, requestConfirmations: requestConfirmations, numWords: numWords, keyHash: keyHash }); } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { s_randomWords = randomWords; } function requestRandomWords() external { RequestConfig memory rc = s_requestConfig; // Will revert if subscription is not set and funded. s_requestId = COORDINATOR.requestRandomWords( rc.keyHash, rc.subId, rc.requestConfirmations, rc.callbackGasLimit, rc.numWords ); } // Assumes this contract owns link function topUpSubscription(uint256 amount) external { LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_requestConfig.subId)); } function unsubscribe() external { // Returns funds to this address COORDINATOR.cancelSubscription(s_requestConfig.subId, address(this)); s_requestConfig.subId = 0; } function subscribe() external { address[] memory consumers = new address[](1); consumers[0] = address(this); s_requestConfig.subId = COORDINATOR.createSubscription(); COORDINATOR.addConsumer(s_requestConfig.subId, consumers[0]); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface VRFCoordinatorV2Interface { /** * @notice Returns the global config that applies to all VRF requests. * @return minimumRequestBlockConfirmations - A minimum number of confirmation * blocks on VRF requests before oracles should respond. * @return fulfillmentFlatFeeLinkPPM - The charge per request on top of the gas fees. * Its flat fee specified in millionths of LINK. * @return maxGasLimit - The maximum gas limit supported for a fulfillRandomWords callback. * @return stalenessSeconds - How long we wait until we consider the ETH/LINK price * (used for converting gas costs to LINK) is stale and use `fallbackWeiPerUnitLink` * @return gasAfterPaymentCalculation - How much gas is used outside of the payment calculation, * i.e. the gas overhead of actually making the payment to oracles. * @return minimumSubscriptionBalance - The minimum subscription balance required to make a request. Its set to be about 300% * of the cost of a single request to handle in ETH/LINK price between request and fulfillment time. * @return fallbackWeiPerUnitLink - fallback ETH/LINK price in the case of a stale feed. */ function getConfig() external view returns ( uint16 minimumRequestBlockConfirmations, uint32 fulfillmentFlatFeeLinkPPM, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, uint96 minimumSubscriptionBalance, int256 fallbackWeiPerUnitLink ); /** * @notice Request a set of random words. * @param keyHash - Corresponds to a particular oracle job which uses * that key for generating the VRF proof. Different keyHash's have different gas price * ceilings, so you can select a specific one to bound your maximum per request cost. * @param subId - The ID of the VRF subscription. Must be funded * with at least minimumSubscriptionBalance (see getConfig) LINK * before making a request. * @param minimumRequestConfirmations - How many blocks you'd like the * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS * for why you may want to request more. The acceptable range is * [minimumRequestBlockConfirmations, 200]. * @param callbackGasLimit - How much gas you'd like to receive in your * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords * may be slightly less than this amount because of gas used calling the function * (argument decoding etc.), so you may need to request slightly more than you expect * to have inside fulfillRandomWords. The acceptable range is * [5000, maxGasLimit]. * @param numWords - The number of uint256 random values you'd like to receive * in your fulfillRandomWords callback. Note these numbers are expanded in a * secure way by the VRFCoordinator from a single random value supplied by the oracle. * @return requestId - A unique identifier of the request. Can be used to match * a request to a response in fulfillRandomWords. */ function requestRandomWords( bytes32 keyHash, uint64 subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external returns (uint256 requestId); /** * @notice Create a VRF subscription. * @return subId - A unique subscription id. * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. * @dev Note to fund the subscription, use transferAndCall. For example * @dev LINKTOKEN.transferAndCall( * @dev address(COORDINATOR), * @dev amount, * @dev abi.encode(subId)); */ function createSubscription() external returns (uint64 subId); /** * @notice Get a VRF subscription. * @param subId - ID of the subscription * @return balance - LINK balance of the subscription in juels. * @return owner - Owner of the subscription * @return consumers - List of consumer address which are able to use this subscription. */ function getSubscription(uint64 subId) external view returns ( uint96 balance, address owner, address[] memory consumers ); /** * @notice Request subscription owner transfer. * @param subId - ID of the subscription * @param newOwner - proposed new owner of the subscription */ function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external; /** * @notice Request subscription owner transfer. * @param subId - ID of the subscription * @dev will revert if original owner of subId has * not requested that msg.sender become the new owner. */ function acceptSubscriptionOwnerTransfer(uint64 subId) external; /** * @notice Add a consumer to a VRF subscription. * @param subId - ID of the subscription * @param consumer - New consumer which can use the subscription */ function addConsumer(uint64 subId, address consumer) external; /** * @notice Remove a consumer from a VRF subscription. * @param subId - ID of the subscription * @param consumer - Consumer to remove from the subscription */ function removeConsumer(uint64 subId, address consumer) external; /** * @notice Withdraw funds from a VRF subscription * @param subId - ID of the subscription * @param to - Where to send the withdrawn LINK to * @param amount - How much to withdraw in juels */ function defundSubscription( uint64 subId, address to, uint96 amount ) external; /** * @notice Cancel a subscription * @param subId - ID of the subscription * @param to - Where to send the remaining LINK to */ function cancelSubscription(uint64 subId, address to) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** **************************************************************************** * @notice Interface for contracts using VRF randomness * ***************************************************************************** * @dev PURPOSE * * @dev Reggie the Random Oracle (not his real job) wants to provide randomness * @dev to Vera the verifier in such a way that Vera can be sure he's not * @dev making his output up to suit himself. Reggie provides Vera a public key * @dev to which he knows the secret key. Each time Vera provides a seed to * @dev Reggie, he gives back a value which is computed completely * @dev deterministically from the seed and the secret key. * * @dev Reggie provides a proof by which Vera can verify that the output was * @dev correctly computed once Reggie tells it to her, but without that proof, * @dev the output is indistinguishable to her from a uniform random sample * @dev from the output space. * * @dev The purpose of this contract is to make it easy for unrelated contracts * @dev to talk to Vera the verifier about the work Reggie is doing, to provide * @dev simple access to a verifiable source of randomness. It ensures 2 things: * @dev 1. The fulfillment came from the VRFCoordinator * @dev 2. The consumer contract implements fulfillRandomWords. * ***************************************************************************** * @dev USAGE * * @dev Calling contracts must inherit from VRFConsumerBase, and can * @dev initialize VRFConsumerBase's attributes in their constructor as * @dev shown: * * @dev contract VRFConsumer { * @dev constuctor(<other arguments>, address _vrfCoordinator, address _link) * @dev VRFConsumerBase(_vrfCoordinator) public { * @dev <initialization with other arguments goes here> * @dev } * @dev } * * @dev The oracle will have given you an ID for the VRF keypair they have * @dev committed to (let's call it keyHash). Create subscription, fund it * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface * @dev subscription management functions). * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations, * @dev callbackGasLimit, numWords), * @dev see (VRFCoordinatorInterface for a description of the arguments). * * @dev Once the VRFCoordinator has received and validated the oracle's response * @dev to your request, it will call your contract's fulfillRandomWords method. * * @dev The randomness argument to fulfillRandomWords is a set of random words * @dev generated from your requestId and the blockHash of the request. * * @dev If your contract could have concurrent requests open, you can use the * @dev requestId returned from requestRandomWords to track which response is associated * @dev with which randomness request. * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind, * @dev if your contract could have multiple requests in flight simultaneously. * * @dev Colliding `requestId`s are cryptographically impossible as long as seeds * @dev differ. * * ***************************************************************************** * @dev SECURITY CONSIDERATIONS * * @dev A method with the ability to call your fulfillRandomness method directly * @dev could spoof a VRF response with any random value, so it's critical that * @dev it cannot be directly called by anything other than this base contract * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). * * @dev For your users to trust that your contract's random behavior is free * @dev from malicious interference, it's best if you can write it so that all * @dev behaviors implied by a VRF response are executed *during* your * @dev fulfillRandomness method. If your contract must store the response (or * @dev anything derived from it) and use it later, you must ensure that any * @dev user-significant behavior which depends on that stored value cannot be * @dev manipulated by a subsequent VRF request. * * @dev Similarly, both miners and the VRF oracle itself have some influence * @dev over the order in which VRF responses appear on the blockchain, so if * @dev your contract could have multiple VRF requests in flight simultaneously, * @dev you must ensure that the order in which the VRF responses arrive cannot * @dev be used to manipulate your contract's user-significant behavior. * * @dev Since the block hash of the block which contains the requestRandomness * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful * @dev miner could, in principle, fork the blockchain to evict the block * @dev containing the request, forcing the request to be included in a * @dev different block with a different hash, and therefore a different input * @dev to the VRF. However, such an attack would incur a substantial economic * @dev cost. This cost scales with the number of blocks the VRF oracle waits * @dev until it calls responds to a request. It is for this reason that * @dev that you can signal to an oracle you'd like them to wait longer before * @dev responding to the request (however this is not enforced in the contract * @dev and so remains effective only in the case of unmodified oracle software). */ abstract contract VRFConsumerBaseV2 { error OnlyCoordinatorCanFulfill(address have, address want); address private immutable vrfCoordinator; /** * @param _vrfCoordinator address of VRFCoordinator contract */ constructor(address _vrfCoordinator) { vrfCoordinator = _vrfCoordinator; } /** * @notice fulfillRandomness handles the VRF response. Your contract must * @notice implement it. See "SECURITY CONSIDERATIONS" above for important * @notice principles to keep in mind when implementing your fulfillRandomness * @notice method. * * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this * @dev signature, and will call it once it has verified the proof * @dev associated with the randomness. (It is triggered via a call to * @dev rawFulfillRandomness, below.) * * @param requestId The Id initially returned by requestRandomness * @param randomWords the VRF output expanded to the requested number of words */ function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual; // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF // proof. rawFulfillRandomness then calls fulfillRandomness, after validating // the origin of the call function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external { if (msg.sender != vrfCoordinator) { revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator); } fulfillRandomWords(requestId, randomWords); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../dev/VRFConsumerBaseV2.sol"; contract VRFMaliciousConsumerV2 is VRFConsumerBaseV2 { uint256[] public s_randomWords; uint256 public s_requestId; VRFCoordinatorV2Interface COORDINATOR; LinkTokenInterface LINKTOKEN; uint64 public s_subId; uint256 public s_gasAvailable; bytes32 s_keyHash; constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); } function setKeyHash(bytes32 keyHash) public { s_keyHash = keyHash; } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { s_gasAvailable = gasleft(); s_randomWords = randomWords; s_requestId = requestId; // Should revert COORDINATOR.requestRandomWords(s_keyHash, s_subId, 1, 200000, 1); } function testCreateSubscriptionAndFund(uint96 amount) external { if (s_subId == 0) { s_subId = COORDINATOR.createSubscription(); COORDINATOR.addConsumer(s_subId, address(this)); } // Approve the link transfer. LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); } function updateSubscription(address[] memory consumers) external { require(s_subId != 0, "subID not set"); for (uint256 i = 0; i < consumers.length; i++) { COORDINATOR.addConsumer(s_subId, consumers[i]); } } function testRequestRandomness() external returns (uint256) { return COORDINATOR.requestRandomWords(s_keyHash, s_subId, 1, 500000, 1); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../dev/VRFConsumerBaseV2.sol"; contract VRFConsumerV2 is VRFConsumerBaseV2 { uint256[] public s_randomWords; uint256 public s_requestId; VRFCoordinatorV2Interface COORDINATOR; LinkTokenInterface LINKTOKEN; uint64 public s_subId; uint256 public s_gasAvailable; constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { s_gasAvailable = gasleft(); s_randomWords = randomWords; s_requestId = requestId; } function testCreateSubscriptionAndFund(uint96 amount) external { if (s_subId == 0) { s_subId = COORDINATOR.createSubscription(); COORDINATOR.addConsumer(s_subId, address(this)); } // Approve the link transfer. LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); } function updateSubscription(address[] memory consumers) external { require(s_subId != 0, "subID not set"); for (uint256 i = 0; i < consumers.length; i++) { COORDINATOR.addConsumer(s_subId, consumers[i]); } } function testRequestRandomness( bytes32 keyHash, uint64 subId, uint16 minReqConfs, uint32 callbackGasLimit, uint32 numWords ) external returns (uint256) { return COORDINATOR.requestRandomWords(keyHash, subId, minReqConfs, callbackGasLimit, numWords); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../dev/VRFConsumerBaseV2.sol"; contract VRFConsumerExternalSubOwnerExample is VRFConsumerBaseV2 { VRFCoordinatorV2Interface COORDINATOR; LinkTokenInterface LINKTOKEN; struct RequestConfig { uint64 subId; uint32 callbackGasLimit; uint16 requestConfirmations; uint32 numWords; bytes32 keyHash; } RequestConfig s_requestConfig; uint256[] s_randomWords; uint256 s_requestId; constructor( address vrfCoordinator, address link, uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords, bytes32 keyHash ) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); s_requestConfig = RequestConfig({ subId: 0, // Initially unset callbackGasLimit: callbackGasLimit, requestConfirmations: requestConfirmations, numWords: numWords, keyHash: keyHash }); } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { s_randomWords = randomWords; } function requestRandomWords() external { RequestConfig memory rc = s_requestConfig; // Will revert if subscription is not set and funded. s_requestId = COORDINATOR.requestRandomWords( rc.keyHash, rc.subId, rc.requestConfirmations, rc.callbackGasLimit, rc.numWords ); } function setSubscriptionID(uint64 subId) public { s_requestConfig.subId = subId; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/BlockhashStoreInterface.sol"; import "../interfaces/AggregatorV3Interface.sol"; import "../interfaces/TypeAndVersionInterface.sol"; import "./VRF.sol"; import "../ConfirmedOwner.sol"; import "./VRFConsumerBaseV2.sol"; contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { LinkTokenInterface public immutable LINK; AggregatorV3Interface public immutable LINK_ETH_FEED; BlockhashStoreInterface public immutable BLOCKHASH_STORE; // We need to maintain a list of consuming addresses. // This bound ensures we are able to loop over them as needed. // Should a user require more consumers, they can use multiple subscriptions. uint16 public constant MAX_CONSUMERS = 100; error TooManyConsumers(); error InsufficientBalance(); error InvalidConsumer(uint64 subId, address consumer); error InvalidSubscription(); error OnlyCallableFromLink(); error InvalidCalldata(); error MustBeSubOwner(address owner); error MustBeRequestedOwner(address proposedOwner); error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen event FundsRecovered(address to, uint256 amount); struct Subscription { // There are only 1e9*1e18 = 1e27 juels in existence, so the balance can fit in uint96 (2^96 ~ 7e28) uint96 balance; // Common link balance used for all consumer requests. address owner; // Owner can fund/withdraw/cancel the sub. address requestedOwner; // For safely transferring sub ownership. // Maintains the list of keys in s_consumers. // We do this for 2 reasons: // 1. To be able to clean up all keys from s_consumers when canceling a subscription. // 2. To be able to return the list of all consumers in getSubscription. // Note that we need the s_consumers map to be able to directly check if a // consumer is valid without reading all the consumers from storage. address[] consumers; } struct Consumer { uint64 subId; uint64 nonce; } mapping(address => mapping(uint64 => Consumer)) /* consumer */ /* subId */ private s_consumers; mapping(uint64 => Subscription) /* subId */ /* subscription */ private s_subscriptions; uint64 private s_currentSubId; // s_totalBalance tracks the total link sent to/from // this contract through onTokenTransfer, defundSubscription, cancelSubscription and oracleWithdraw. // A discrepancy with this contract's link balance indicates someone // sent tokens using transfer and so we may need to use recoverFunds. uint96 public s_totalBalance; event SubscriptionCreated(uint64 indexed subId, address owner); event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); event SubscriptionConsumerAdded(uint64 indexed subId, address consumer); event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer); event SubscriptionDefunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to); event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to); // Set this maximum to 200 to give us a 56 block window to fulfill // the request before requiring the block hash feeder. uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; uint32 public constant MAX_NUM_WORDS = 500; // The minimum gas limit that could be requested for a callback. // Set to 5k to ensure plenty of room to make the call itself. uint256 public constant MIN_GAS_LIMIT = 5_000; error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); error GasLimitTooBig(uint32 have, uint32 want); error NumWordsTooBig(uint32 have, uint32 want); error ProvingKeyAlreadyRegistered(bytes32 keyHash); error NoSuchProvingKey(bytes32 keyHash); error InvalidLinkWeiPrice(int256 linkWei); error InsufficientGasForConsumer(uint256 have, uint256 want); error NoCorrespondingRequest(); error IncorrectCommitment(); error BlockhashNotInStore(uint256 blockNum); error PaymentTooLarge(); error Reentrant(); struct RequestCommitment { uint64 blockNum; uint64 subId; uint32 callbackGasLimit; uint32 numWords; address sender; } mapping(bytes32 => address) /* keyHash */ /* oracle */ private s_provingKeys; mapping(address => uint96) /* oracle */ /* LINK balance */ private s_withdrawableTokens; mapping(uint256 => bytes32) /* requestID */ /* commitment */ private s_requestCommitments; event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle); event RandomWordsRequested( bytes32 indexed keyHash, uint256 requestId, uint256 preSeed, uint64 subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords, address indexed sender ); event RandomWordsFulfilled(uint256 indexed requestId, uint256[] output, bool success); struct Config { uint16 minimumRequestConfirmations; // Flat fee charged per fulfillment in millionths of link // So fee range is [0, 2^32/10^6]. uint32 fulfillmentFlatFeeLinkPPM; uint32 maxGasLimit; // stalenessSeconds is how long before we consider the feed price to be stale // and fallback to fallbackWeiPerUnitLink. uint32 stalenessSeconds; // Gas to cover oracle payment after we calculate the payment. // We make it configurable in case those operations are repriced. uint32 gasAfterPaymentCalculation; uint96 minimumSubscriptionBalance; // Re-entrancy protection. bool reentrancyLock; } int256 internal s_fallbackWeiPerUnitLink; Config private s_config; event ConfigSet( uint16 minimumRequestConfirmations, uint32 fulfillmentFlatFeeLinkPPM, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, uint96 minimumSubscriptionBalance, int256 fallbackWeiPerUnitLink ); constructor( address link, address blockhashStore, address linkEthFeed ) ConfirmedOwner(msg.sender) { LINK = LinkTokenInterface(link); LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); BLOCKHASH_STORE = BlockhashStoreInterface(blockhashStore); } /** * @notice Registers a proving key to an oracle. * @param oracle address of the oracle * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); if (s_provingKeys[kh] != address(0)) { revert ProvingKeyAlreadyRegistered(kh); } s_provingKeys[kh] = oracle; emit ProvingKeyRegistered(kh, oracle); } /** * @notice Deregisters a proving key to an oracle. * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); address oracle = s_provingKeys[kh]; if (oracle == address(0)) { revert NoSuchProvingKey(kh); } delete s_provingKeys[kh]; emit ProvingKeyDeregistered(kh, oracle); } /** * @notice Returns the serviceAgreements key associated with this public key * @param publicKey the key to return the address for */ function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { return keccak256(abi.encode(publicKey)); } function setConfig( uint16 minimumRequestConfirmations, uint32 fulfillmentFlatFeeLinkPPM, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, uint96 minimumSubscriptionBalance, int256 fallbackWeiPerUnitLink ) external onlyOwner { if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { revert InvalidRequestConfirmations( minimumRequestConfirmations, minimumRequestConfirmations, MAX_REQUEST_CONFIRMATIONS ); } if (fallbackWeiPerUnitLink <= 0) { revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink); } s_config = Config({ minimumRequestConfirmations: minimumRequestConfirmations, fulfillmentFlatFeeLinkPPM: fulfillmentFlatFeeLinkPPM, maxGasLimit: maxGasLimit, stalenessSeconds: stalenessSeconds, gasAfterPaymentCalculation: gasAfterPaymentCalculation, minimumSubscriptionBalance: minimumSubscriptionBalance, reentrancyLock: false }); s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; emit ConfigSet( minimumRequestConfirmations, fulfillmentFlatFeeLinkPPM, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, minimumSubscriptionBalance, fallbackWeiPerUnitLink ); } /** * @notice read the current configuration of the coordinator. */ function getConfig() external view returns ( uint16 minimumRequestConfirmations, uint32 fulfillmentFlatFeeLinkPPM, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, uint96 minimumSubscriptionBalance, int256 fallbackWeiPerUnitLink ) { Config memory config = s_config; return ( config.minimumRequestConfirmations, config.fulfillmentFlatFeeLinkPPM, config.maxGasLimit, config.stalenessSeconds, config.gasAfterPaymentCalculation, config.minimumSubscriptionBalance, s_fallbackWeiPerUnitLink ); } function recoverFunds(address to) external onlyOwner { uint256 externalBalance = LINK.balanceOf(address(this)); uint256 internalBalance = uint256(s_totalBalance); if (internalBalance > externalBalance) { revert BalanceInvariantViolated(internalBalance, externalBalance); } if (internalBalance < externalBalance) { uint256 amount = externalBalance - internalBalance; LINK.transfer(to, amount); emit FundsRecovered(to, amount); } // If the balances are equal, nothing to be done. } // Want to ensure these arguments can fit inside of 2 words // so in the worst case where the consuming contract has to read all of them // from storage, it only has to read 2 words. function requestRandomWords( bytes32 keyHash, // Corresponds to a particular offchain job which uses that key for the proofs uint64 subId, uint16 requestConfirmations, uint32 callbackGasLimit, uint32 numWords // Desired number of random words ) external nonReentrant returns (uint256) { // Input validation using the subscription storage. if (s_subscriptions[subId].owner == address(0)) { revert InvalidSubscription(); } // Its important to ensure that the consumer is in fact who they say they // are, otherwise they could use someone else's subscription balance. Consumer memory consumer = s_consumers[msg.sender][subId]; if (consumer.subId == 0) { revert InvalidConsumer(subId, msg.sender); } // Input validation using the config storage word. if ( requestConfirmations < s_config.minimumRequestConfirmations || requestConfirmations > MAX_REQUEST_CONFIRMATIONS ) { revert InvalidRequestConfirmations( requestConfirmations, s_config.minimumRequestConfirmations, MAX_REQUEST_CONFIRMATIONS ); } if (s_subscriptions[subId].balance < s_config.minimumSubscriptionBalance) { revert InsufficientBalance(); } if (callbackGasLimit > s_config.maxGasLimit) { revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit); } if (numWords > MAX_NUM_WORDS) { revert NumWordsTooBig(numWords, MAX_NUM_WORDS); } // Note we do not check whether the keyHash is valid to save gas. // The consequence for users is that they can send requests // for invalid keyHashes which will simply not be fulfilled. uint64 nonce = consumer.nonce + 1; uint256 preSeed = uint256(keccak256(abi.encode(keyHash, msg.sender, subId, nonce))); uint256 requestId = uint256(keccak256(abi.encode(keyHash, preSeed))); s_requestCommitments[requestId] = keccak256( abi.encode(requestId, block.number, subId, callbackGasLimit, numWords, msg.sender) ); emit RandomWordsRequested( keyHash, requestId, preSeed, subId, requestConfirmations, callbackGasLimit, numWords, msg.sender ); s_consumers[msg.sender][subId].nonce = nonce; return requestId; } function getCommitment(uint256 requestId) external view returns (bytes32) { return s_requestCommitments[requestId]; } /** * @dev calls target address with exactly gasAmount gas and data as calldata * or reverts if at least gasAmount gas is not available. * The maximum amount of gasAmount is all gas available but 1/64th. * The minimum amount of gasAmount is MIN_GAS_LIMIT. */ function callWithExactGas( uint256 gasAmount, address target, bytes memory data ) private returns (bool success) { // solhint-disable-next-line no-inline-assembly assembly { let g := gas() // Compute g -= MIN_GAS_LIMIT and check for underflow if lt(g, MIN_GAS_LIMIT) { revert(0, 0) } g := sub(g, MIN_GAS_LIMIT) // if g - g//64 <= gasAmount, revert // (we subtract g//64 because of EIP-150) if iszero(gt(sub(g, div(g, 64)), gasAmount)) { revert(0, 0) } // solidity calls check that a contract actually exists at the destination, so we do the same if iszero(extcodesize(target)) { revert(0, 0) } // call and return whether we succeeded. ignore return data success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) } return success; } function getRandomnessFromProof(Proof memory proof, RequestCommitment memory rc) private view returns ( bytes32 keyHash, uint256 requestId, uint256 randomness ) { keyHash = hashOfKey(proof.pk); // Only registered proving keys are permitted. address oracle = s_provingKeys[keyHash]; if (oracle == address(0)) { revert NoSuchProvingKey(keyHash); } requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); bytes32 commitment = s_requestCommitments[requestId]; if (commitment == 0) { revert NoCorrespondingRequest(); } if ( commitment != keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender)) ) { revert IncorrectCommitment(); } bytes32 blockHash = blockhash(rc.blockNum); if (blockHash == bytes32(0)) { blockHash = BLOCKHASH_STORE.getBlockhash(rc.blockNum); if (blockHash == bytes32(0)) { revert BlockhashNotInStore(rc.blockNum); } } // The seed actually used by the VRF machinery, mixing in the blockhash uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); randomness = VRF.randomValueFromVRFProof(proof, actualSeed); // Reverts on failure } function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant { uint256 startGas = gasleft(); (bytes32 keyHash, uint256 requestId, uint256 randomness) = getRandomnessFromProof(proof, rc); uint256[] memory randomWords = new uint256[](rc.numWords); for (uint256 i = 0; i < rc.numWords; i++) { randomWords[i] = uint256(keccak256(abi.encode(randomness, i))); } delete s_requestCommitments[requestId]; VRFConsumerBaseV2 v; bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, proof.seed, randomWords); uint256 gasPreCallback = gasleft(); if (gasPreCallback < rc.callbackGasLimit) { revert InsufficientGasForConsumer(gasPreCallback, rc.callbackGasLimit); } // Call with explicitly the amount of callback gas requested // Important to not let them exhaust the gas budget and avoid oracle payment. // Do not allow any non-view/non-pure coordinator functions to be called // during the consumers callback code via reentrancyLock. s_config.reentrancyLock = true; bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp); emit RandomWordsFulfilled(requestId, randomWords, success); s_config.reentrancyLock = false; // We want to charge users exactly for how much gas they use in their callback. // The gasAfterPaymentCalculation is meant to cover these additional operations where we // decrement the subscription balance and increment the oracles withdrawable balance. // We also add the flat link fee to the payment amount. // Its specified in millionths of link, if s_config.fulfillmentFlatFeeLinkPPM = 1 // 1 link / 1e6 = 1e18 juels / 1e6 = 1e12 juels. uint96 payment = calculatePaymentAmount( startGas, s_config.gasAfterPaymentCalculation, s_config.fulfillmentFlatFeeLinkPPM, tx.gasprice ); if (s_subscriptions[rc.subId].balance < payment) { revert InsufficientBalance(); } s_subscriptions[rc.subId].balance -= payment; s_withdrawableTokens[s_provingKeys[keyHash]] += payment; } // Get the amount of gas used for fulfillment function calculatePaymentAmount( uint256 startGas, uint256 gasAfterPaymentCalculation, uint32 fulfillmentFlatFeeLinkPPM, uint256 weiPerUnitGas ) internal view returns (uint96) { int256 weiPerUnitLink; weiPerUnitLink = getFeedData(); if (weiPerUnitLink <= 0) { revert InvalidLinkWeiPrice(weiPerUnitLink); } // (1e18 juels/link) (wei/gas * gas) / (wei/link) = juels uint256 paymentNoFee = (1e18 * weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft())) / uint256(weiPerUnitLink); uint256 fee = 1e12 * uint256(fulfillmentFlatFeeLinkPPM); if (paymentNoFee > (1e27 - fee)) { revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. } return uint96(paymentNoFee + fee); } function getFeedData() private view returns (int256) { uint32 stalenessSeconds = s_config.stalenessSeconds; bool staleFallback = stalenessSeconds > 0; uint256 timestamp; int256 weiPerUnitLink; (, weiPerUnitLink, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); // solhint-disable-next-line not-rely-on-time if (staleFallback && stalenessSeconds < block.timestamp - timestamp) { weiPerUnitLink = s_fallbackWeiPerUnitLink; } return weiPerUnitLink; } function oracleWithdraw(address recipient, uint96 amount) external nonReentrant { if (s_withdrawableTokens[msg.sender] < amount) { revert InsufficientBalance(); } s_withdrawableTokens[msg.sender] -= amount; s_totalBalance -= amount; if (!LINK.transfer(recipient, amount)) { revert InsufficientBalance(); } } function onTokenTransfer( address sender, uint256 amount, bytes calldata data ) external nonReentrant { if (msg.sender != address(LINK)) { revert OnlyCallableFromLink(); } if (data.length != 32) { revert InvalidCalldata(); } uint64 subId = abi.decode(data, (uint64)); if (s_subscriptions[subId].owner == address(0)) { revert InvalidSubscription(); } address owner = s_subscriptions[subId].owner; if (owner != sender) { revert MustBeSubOwner(owner); } uint256 oldBalance = s_subscriptions[subId].balance; s_subscriptions[subId].balance += uint96(amount); s_totalBalance += uint96(amount); emit SubscriptionFunded(subId, oldBalance, oldBalance + amount); } function getSubscription(uint64 subId) external view returns ( uint96 balance, address owner, address[] memory consumers ) { if (s_subscriptions[subId].owner == address(0)) { revert InvalidSubscription(); } return (s_subscriptions[subId].balance, s_subscriptions[subId].owner, s_subscriptions[subId].consumers); } function createSubscription() external nonReentrant returns (uint64) { s_currentSubId++; uint64 currentSubId = s_currentSubId; address[] memory consumers = new address[](0); s_subscriptions[currentSubId] = Subscription({ balance: 0, owner: msg.sender, requestedOwner: address(0), consumers: consumers }); emit SubscriptionCreated(currentSubId, msg.sender); return currentSubId; } function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external onlySubOwner(subId) nonReentrant { // Proposing to address(0) would never be claimable so don't need to check. if (s_subscriptions[subId].requestedOwner != newOwner) { s_subscriptions[subId].requestedOwner = newOwner; emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); } } function acceptSubscriptionOwnerTransfer(uint64 subId) external nonReentrant { if (s_subscriptions[subId].owner == address(0)) { revert InvalidSubscription(); } if (s_subscriptions[subId].requestedOwner != msg.sender) { revert MustBeRequestedOwner(s_subscriptions[subId].requestedOwner); } address oldOwner = s_subscriptions[subId].owner; s_subscriptions[subId].owner = msg.sender; s_subscriptions[subId].requestedOwner = address(0); emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); } function removeConsumer(uint64 subId, address consumer) external onlySubOwner(subId) nonReentrant { if (s_consumers[consumer][subId].subId == 0) { revert InvalidConsumer(subId, consumer); } // Note bounded by MAX_CONSUMERS address[] memory consumers = s_subscriptions[subId].consumers; uint256 lastConsumerIndex = consumers.length - 1; for (uint256 i = 0; i < consumers.length; i++) { if (consumers[i] == consumer) { address last = consumers[lastConsumerIndex]; // Storage write to preserve last element s_subscriptions[subId].consumers[i] = last; // Storage remove last element s_subscriptions[subId].consumers.pop(); break; } } delete s_consumers[consumer][subId]; emit SubscriptionConsumerRemoved(subId, consumer); } function addConsumer(uint64 subId, address consumer) external onlySubOwner(subId) nonReentrant { // Already maxed, cannot add any more consumers. if (s_subscriptions[subId].consumers.length == MAX_CONSUMERS) { revert TooManyConsumers(); } if (s_consumers[consumer][subId].subId != 0) { // Idempotence - do nothing if already added. // Ensures uniqueness in s_subscriptions[subId].consumers. return; } s_consumers[consumer][subId] = Consumer({subId: subId, nonce: 0}); s_subscriptions[subId].consumers.push(consumer); emit SubscriptionConsumerAdded(subId, consumer); } function defundSubscription( uint64 subId, address to, uint96 amount ) external onlySubOwner(subId) nonReentrant { if (s_subscriptions[subId].balance < amount) { revert InsufficientBalance(); } uint256 oldBalance = s_subscriptions[subId].balance; s_subscriptions[subId].balance -= amount; s_totalBalance -= amount; if (!LINK.transfer(to, amount)) { revert InsufficientBalance(); } emit SubscriptionDefunded(subId, oldBalance, s_subscriptions[subId].balance); } // Keep this separate from zeroing, perhaps there is a use case where consumers // want to keep the subId, but withdraw all the link. function cancelSubscription(uint64 subId, address to) external onlySubOwner(subId) nonReentrant { Subscription memory sub = s_subscriptions[subId]; uint96 balance = sub.balance; // Note bounded by MAX_CONSUMERS; // If no consumers, does nothing. for (uint256 i = 0; i < sub.consumers.length; i++) { delete s_consumers[sub.consumers[i]][subId]; } delete s_subscriptions[subId]; s_totalBalance -= balance; if (!LINK.transfer(to, uint256(balance))) { revert InsufficientBalance(); } emit SubscriptionCanceled(subId, to, balance); } modifier onlySubOwner(uint64 subId) { address owner = s_subscriptions[subId].owner; if (owner == address(0)) { revert InvalidSubscription(); } if (msg.sender != owner) { revert MustBeSubOwner(owner); } _; } modifier nonReentrant() { if (s_config.reentrancyLock) { revert Reentrant(); } _; } /** * @notice The type and version of this contract * @return Type and version string */ function typeAndVersion() external pure virtual override returns (string memory) { return "VRFCoordinatorV2 1.0.0"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface BlockhashStoreInterface { function getBlockhash(uint256 number) external view returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; abstract contract TypeAndVersionInterface { function typeAndVersion() external pure virtual returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** **************************************************************************** * @notice Verification of verifiable-random-function (VRF) proofs, following * @notice https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 * @notice See https://eprint.iacr.org/2017/099.pdf for security proofs. * @dev Bibliographic references: * @dev Goldberg, et al., "Verifiable Random Functions (VRFs)", Internet Draft * @dev draft-irtf-cfrg-vrf-05, IETF, Aug 11 2019, * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05 * @dev Papadopoulos, et al., "Making NSEC5 Practical for DNSSEC", Cryptology * @dev ePrint Archive, Report 2017/099, https://eprint.iacr.org/2017/099.pdf * **************************************************************************** * @dev USAGE * @dev The main entry point is randomValueFromVRFProof. See its docstring. * **************************************************************************** * @dev PURPOSE * @dev Reggie the Random Oracle (not his real job) wants to provide randomness * @dev to Vera the verifier in such a way that Vera can be sure he's not * @dev making his output up to suit himself. Reggie provides Vera a public key * @dev to which he knows the secret key. Each time Vera provides a seed to * @dev Reggie, he gives back a value which is computed completely * @dev deterministically from the seed and the secret key. * @dev Reggie provides a proof by which Vera can verify that the output was * @dev correctly computed once Reggie tells it to her, but without that proof, * @dev the output is computationally indistinguishable to her from a uniform * @dev random sample from the output space. * @dev The purpose of this contract is to perform that verification. * **************************************************************************** * @dev DESIGN NOTES * @dev The VRF algorithm verified here satisfies the full unqiqueness, full * @dev collision resistance, and full pseudorandomness security properties. * @dev See "SECURITY PROPERTIES" below, and * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-3 * @dev An elliptic curve point is generally represented in the solidity code * @dev as a uint256[2], corresponding to its affine coordinates in * @dev GF(FIELD_SIZE). * @dev For the sake of efficiency, this implementation deviates from the spec * @dev in some minor ways: * @dev - Keccak hash rather than the SHA256 hash recommended in * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 * @dev Keccak costs much less gas on the EVM, and provides similar security. * @dev - Secp256k1 curve instead of the P-256 or ED25519 curves recommended in * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 * @dev For curve-point multiplication, it's much cheaper to abuse ECRECOVER * @dev - hashToCurve recursively hashes until it finds a curve x-ordinate. On * @dev the EVM, this is slightly more efficient than the recommendation in * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 * @dev step 5, to concatenate with a nonce then hash, and rehash with the * @dev nonce updated until a valid x-ordinate is found. * @dev - hashToCurve does not include a cipher version string or the byte 0x1 * @dev in the hash message, as recommended in step 5.B of the draft * @dev standard. They are unnecessary here because no variation in the * @dev cipher suite is allowed. * @dev - Similarly, the hash input in scalarFromCurvePoints does not include a * @dev commitment to the cipher suite, either, which differs from step 2 of * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 * @dev . Also, the hash input is the concatenation of the uncompressed * @dev points, not the compressed points as recommended in step 3. * @dev - In the calculation of the challenge value "c", the "u" value (i.e. * @dev the value computed by Reggie as the nonce times the secp256k1 * @dev generator point, see steps 5 and 7 of * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 * @dev ) is replaced by its ethereum address, i.e. the lower 160 bits of the * @dev keccak hash of the original u. This is because we only verify the * @dev calculation of u up to its address, by abusing ECRECOVER. * **************************************************************************** * @dev SECURITY PROPERTIES * @dev Here are the security properties for this VRF: * @dev Full uniqueness: For any seed and valid VRF public key, there is * @dev exactly one VRF output which can be proved to come from that seed, in * @dev the sense that the proof will pass verifyVRFProof. * @dev Full collision resistance: It's cryptographically infeasible to find * @dev two seeds with same VRF output from a fixed, valid VRF key * @dev Full pseudorandomness: Absent the proofs that the VRF outputs are * @dev derived from a given seed, the outputs are computationally * @dev indistinguishable from randomness. * @dev https://eprint.iacr.org/2017/099.pdf, Appendix B contains the proofs * @dev for these properties. * @dev For secp256k1, the key validation described in section * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.6 * @dev is unnecessary, because secp256k1 has cofactor 1, and the * @dev representation of the public key used here (affine x- and y-ordinates * @dev of the secp256k1 point on the standard y^2=x^3+7 curve) cannot refer to * @dev the point at infinity. * **************************************************************************** * @dev OTHER SECURITY CONSIDERATIONS * * @dev The seed input to the VRF could in principle force an arbitrary amount * @dev of work in hashToCurve, by requiring extra rounds of hashing and * @dev checking whether that's yielded the x ordinate of a secp256k1 point. * @dev However, under the Random Oracle Model the probability of choosing a * @dev point which forces n extra rounds in hashToCurve is 2⁻ⁿ. The base cost * @dev for calling hashToCurve is about 25,000 gas, and each round of checking * @dev for a valid x ordinate costs about 15,555 gas, so to find a seed for * @dev which hashToCurve would cost more than 2,017,000 gas, one would have to * @dev try, in expectation, about 2¹²⁸ seeds, which is infeasible for any * @dev foreseeable computational resources. (25,000 + 128 * 15,555 < 2,017,000.) * @dev Since the gas block limit for the Ethereum main net is 10,000,000 gas, * @dev this means it is infeasible for an adversary to prevent correct * @dev operation of this contract by choosing an adverse seed. * @dev (See TestMeasureHashToCurveGasCost for verification of the gas cost for * @dev hashToCurve.) * @dev It may be possible to make a secure constant-time hashToCurve function. * @dev See notes in hashToCurve docstring. */ contract VRF { // See https://www.secg.org/sec2-v2.pdf, section 2.4.1, for these constants. // Number of points in Secp256k1 uint256 private constant GROUP_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; // Prime characteristic of the galois field over which Secp256k1 is defined uint256 private constant FIELD_SIZE = // solium-disable-next-line indentation 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; uint256 private constant WORD_LENGTH_BYTES = 0x20; // (base^exponent) % FIELD_SIZE // Cribbed from https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 function bigModExp(uint256 base, uint256 exponent) internal view returns (uint256 exponentiation) { uint256 callResult; uint256[6] memory bigModExpContractInputs; bigModExpContractInputs[0] = WORD_LENGTH_BYTES; // Length of base bigModExpContractInputs[1] = WORD_LENGTH_BYTES; // Length of exponent bigModExpContractInputs[2] = WORD_LENGTH_BYTES; // Length of modulus bigModExpContractInputs[3] = base; bigModExpContractInputs[4] = exponent; bigModExpContractInputs[5] = FIELD_SIZE; uint256[1] memory output; assembly { // solhint-disable-line no-inline-assembly callResult := staticcall( not(0), // Gas cost: no limit 0x05, // Bigmodexp contract address bigModExpContractInputs, 0xc0, // Length of input segment: 6*0x20-bytes output, 0x20 // Length of output segment ) } if (callResult == 0) { revert("bigModExp failure!"); } return output[0]; } // Let q=FIELD_SIZE. q % 4 = 3, ∴ x≡r^2 mod q ⇒ x^SQRT_POWER≡±r mod q. See // https://en.wikipedia.org/wiki/Modular_square_root#Prime_or_prime_power_modulus uint256 private constant SQRT_POWER = (FIELD_SIZE + 1) >> 2; // Computes a s.t. a^2 = x in the field. Assumes a exists function squareRoot(uint256 x) internal view returns (uint256) { return bigModExp(x, SQRT_POWER); } // The value of y^2 given that (x,y) is on secp256k1. function ySquared(uint256 x) internal pure returns (uint256) { // Curve is y^2=x^3+7. See section 2.4.1 of https://www.secg.org/sec2-v2.pdf uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE); return addmod(xCubed, 7, FIELD_SIZE); } // True iff p is on secp256k1 function isOnCurve(uint256[2] memory p) internal pure returns (bool) { // Section 2.3.6. in https://www.secg.org/sec1-v2.pdf // requires each ordinate to be in [0, ..., FIELD_SIZE-1] require(p[0] < FIELD_SIZE, "invalid x-ordinate"); require(p[1] < FIELD_SIZE, "invalid y-ordinate"); return ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE); } // Hash x uniformly into {0, ..., FIELD_SIZE-1}. function fieldHash(bytes memory b) internal pure returns (uint256 x_) { x_ = uint256(keccak256(b)); // Rejecting if x >= FIELD_SIZE corresponds to step 2.1 in section 2.3.4 of // http://www.secg.org/sec1-v2.pdf , which is part of the definition of // string_to_point in the IETF draft while (x_ >= FIELD_SIZE) { x_ = uint256(keccak256(abi.encodePacked(x_))); } } // Hash b to a random point which hopefully lies on secp256k1. The y ordinate // is always even, due to // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 // step 5.C, which references arbitrary_string_to_point, defined in // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 as // returning the point with given x ordinate, and even y ordinate. function newCandidateSecp256k1Point(bytes memory b) internal view returns (uint256[2] memory p) { unchecked { p[0] = fieldHash(b); p[1] = squareRoot(ySquared(p[0])); if (p[1] % 2 == 1) { // Note that 0 <= p[1] < FIELD_SIZE // so this cannot wrap, we use unchecked to save gas. p[1] = FIELD_SIZE - p[1]; } } } // Domain-separation tag for initial hash in hashToCurve. Corresponds to // vrf.go/hashToCurveHashPrefix uint256 internal constant HASH_TO_CURVE_HASH_PREFIX = 1; // Cryptographic hash function onto the curve. // // Corresponds to algorithm in section 5.4.1.1 of the draft standard. (But see // DESIGN NOTES above for slight differences.) // // TODO(alx): Implement a bounded-computation hash-to-curve, as described in // "Construction of Rational Points on Elliptic Curves over Finite Fields" // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.831.5299&rep=rep1&type=pdf // and suggested by // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-01#section-5.2.2 // (Though we can't used exactly that because secp256k1's j-invariant is 0.) // // This would greatly simplify the analysis in "OTHER SECURITY CONSIDERATIONS" // https://www.pivotaltracker.com/story/show/171120900 function hashToCurve(uint256[2] memory pk, uint256 input) internal view returns (uint256[2] memory rv) { rv = newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, pk, input)); while (!isOnCurve(rv)) { rv = newCandidateSecp256k1Point(abi.encodePacked(rv[0])); } } /** ********************************************************************* * @notice Check that product==scalar*multiplicand * * @dev Based on Vitalik Buterin's idea in ethresear.ch post cited below. * * @param multiplicand: secp256k1 point * @param scalar: non-zero GF(GROUP_ORDER) scalar * @param product: secp256k1 expected to be multiplier * multiplicand * @return verifies true iff product==scalar*multiplicand, with cryptographically high probability */ function ecmulVerify( uint256[2] memory multiplicand, uint256 scalar, uint256[2] memory product ) internal pure returns (bool verifies) { require(scalar != 0, "zero scalar"); // Rules out an ecrecover failure case uint256 x = multiplicand[0]; // x ordinate of multiplicand uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28; // parity of y ordinate // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 // Point corresponding to address ecrecover(0, v, x, s=scalar*x) is // (x⁻¹ mod GROUP_ORDER) * (scalar * x * multiplicand - 0 * g), i.e. // scalar*multiplicand. See https://crypto.stackexchange.com/a/18106 bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER)); address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX); // Explicit conversion to address takes bottom 160 bits address expected = address(uint160(uint256(keccak256(abi.encodePacked(product))))); return (actual == expected); } // Returns x1/z1-x2/z2=(x1z2-x2z1)/(z1z2) in projective coordinates on P¹(𝔽ₙ) function projectiveSub( uint256 x1, uint256 z1, uint256 x2, uint256 z2 ) internal pure returns (uint256 x3, uint256 z3) { unchecked { uint256 num1 = mulmod(z2, x1, FIELD_SIZE); // Note this cannot wrap since x2 is a point in [0, FIELD_SIZE-1] // we use unchecked to save gas. uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); } } // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) function projectiveMul( uint256 x1, uint256 z1, uint256 x2, uint256 z2 ) internal pure returns (uint256 x3, uint256 z3) { (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); } /** ************************************************************************** @notice Computes elliptic-curve sum, in projective co-ordinates @dev Using projective coordinates avoids costly divisions @dev To use this with p and q in affine coordinates, call @dev projectiveECAdd(px, py, qx, qy). This will return @dev the addition of (px, py, 1) and (qx, qy, 1), in the @dev secp256k1 group. @dev This can be used to calculate the z which is the inverse to zInv @dev in isValidVRFOutput. But consider using a faster @dev re-implementation such as ProjectiveECAdd in the golang vrf package. @dev This function assumes [px,py,1],[qx,qy,1] are valid projective coordinates of secp256k1 points. That is safe in this contract, because this method is only used by linearCombination, which checks points are on the curve via ecrecover. ************************************************************************** @param px The first affine coordinate of the first summand @param py The second affine coordinate of the first summand @param qx The first affine coordinate of the second summand @param qy The second affine coordinate of the second summand (px,py) and (qx,qy) must be distinct, valid secp256k1 points. ************************************************************************** Return values are projective coordinates of [px,py,1]+[qx,qy,1] as points on secp256k1, in P²(𝔽ₙ) @return sx @return sy @return sz */ function projectiveECAdd( uint256 px, uint256 py, uint256 qx, uint256 qy ) internal pure returns ( uint256 sx, uint256 sy, uint256 sz ) { unchecked { // See "Group law for E/K : y^2 = x^3 + ax + b", in section 3.1.2, p. 80, // "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes and Vanstone // We take the equations there for (sx,sy), and homogenize them to // projective coordinates. That way, no inverses are required, here, and we // only need the one inverse in affineECAdd. // We only need the "point addition" equations from Hankerson et al. Can // skip the "point doubling" equations because p1 == p2 is cryptographically // impossible, and required not to be the case in linearCombination. // Add extra "projective coordinate" to the two points (uint256 z1, uint256 z2) = (1, 1); // (lx, lz) = (qy-py)/(qx-px), i.e., gradient of secant line. // Cannot wrap since px and py are in [0, FIELD_SIZE-1] uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE); uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE); uint256 dx; // Accumulates denominator from sx calculation // sx=((qy-py)/(qx-px))^2-px-qx (sx, dx) = projectiveMul(lx, lz, lx, lz); // ((qy-py)/(qx-px))^2 (sx, dx) = projectiveSub(sx, dx, px, z1); // ((qy-py)/(qx-px))^2-px (sx, dx) = projectiveSub(sx, dx, qx, z2); // ((qy-py)/(qx-px))^2-px-qx uint256 dy; // Accumulates denominator from sy calculation // sy=((qy-py)/(qx-px))(px-sx)-py (sy, dy) = projectiveSub(px, z1, sx, dx); // px-sx (sy, dy) = projectiveMul(sy, dy, lx, lz); // ((qy-py)/(qx-px))(px-sx) (sy, dy) = projectiveSub(sy, dy, py, z1); // ((qy-py)/(qx-px))(px-sx)-py if (dx != dy) { // Cross-multiply to put everything over a common denominator sx = mulmod(sx, dy, FIELD_SIZE); sy = mulmod(sy, dx, FIELD_SIZE); sz = mulmod(dx, dy, FIELD_SIZE); } else { // Already over a common denominator, use that for z ordinate sz = dx; } } } // p1+p2, as affine points on secp256k1. // // invZ must be the inverse of the z returned by projectiveECAdd(p1, p2). // It is computed off-chain to save gas. // // p1 and p2 must be distinct, because projectiveECAdd doesn't handle // point doubling. function affineECAdd( uint256[2] memory p1, uint256[2] memory p2, uint256 invZ ) internal pure returns (uint256[2] memory) { uint256 x; uint256 y; uint256 z; (x, y, z) = projectiveECAdd(p1[0], p1[1], p2[0], p2[1]); require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z"); // Clear the z ordinate of the projective representation by dividing through // by it, to obtain the affine representation return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)]; } // True iff address(c*p+s*g) == lcWitness, where g is generator. (With // cryptographically high probability.) function verifyLinearCombinationWithGenerator( uint256 c, uint256[2] memory p, uint256 s, address lcWitness ) internal pure returns (bool) { // Rule out ecrecover failure modes which return address 0. unchecked { require(lcWitness != address(0), "bad witness"); uint8 v = (p[1] % 2 == 0) ? 27 : 28; // parity of y-ordinate of p // Note this cannot wrap (X - Y % X), but we use unchecked to save // gas. bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER)); // -s*p[0] bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER)); // c*p[0] // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 // The point corresponding to the address returned by // ecrecover(-s*p[0],v,p[0],c*p[0]) is // (p[0]⁻¹ mod GROUP_ORDER)*(c*p[0]-(-s)*p[0]*g)=c*p+s*g. // See https://crypto.stackexchange.com/a/18106 // https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature); return computed == lcWitness; } } // c*p1 + s*p2. Requires cp1Witness=c*p1 and sp2Witness=s*p2. Also // requires cp1Witness != sp2Witness (which is fine for this application, // since it is cryptographically impossible for them to be equal. In the // (cryptographically impossible) case that a prover accidentally derives // a proof with equal c*p1 and s*p2, they should retry with a different // proof nonce.) Assumes that all points are on secp256k1 // (which is checked in verifyVRFProof below.) function linearCombination( uint256 c, uint256[2] memory p1, uint256[2] memory cp1Witness, uint256 s, uint256[2] memory p2, uint256[2] memory sp2Witness, uint256 zInv ) internal pure returns (uint256[2] memory) { unchecked { // Note we are relying on the wrap around here require((cp1Witness[0] % FIELD_SIZE) != (sp2Witness[0] % FIELD_SIZE), "points in sum must be distinct"); require(ecmulVerify(p1, c, cp1Witness), "First mul check failed"); require(ecmulVerify(p2, s, sp2Witness), "Second mul check failed"); return affineECAdd(cp1Witness, sp2Witness, zInv); } } // Domain-separation tag for the hash taken in scalarFromCurvePoints. // Corresponds to scalarFromCurveHashPrefix in vrf.go uint256 internal constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2; // Pseudo-random number from inputs. Matches vrf.go/scalarFromCurvePoints, and // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 // The draft calls (in step 7, via the definition of string_to_int, in // https://datatracker.ietf.org/doc/html/rfc8017#section-4.2 ) for taking the // first hash without checking that it corresponds to a number less than the // group order, which will lead to a slight bias in the sample. // // TODO(alx): We could save a bit of gas by following the standard here and // using the compressed representation of the points, if we collated the y // parities into a single bytes32. // https://www.pivotaltracker.com/story/show/171120588 function scalarFromCurvePoints( uint256[2] memory hash, uint256[2] memory pk, uint256[2] memory gamma, address uWitness, uint256[2] memory v ) internal pure returns (uint256 s) { return uint256(keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, hash, pk, gamma, v, uWitness))); } // True if (gamma, c, s) is a correctly constructed randomness proof from pk // and seed. zInv must be the inverse of the third ordinate from // projectiveECAdd applied to cGammaWitness and sHashWitness. Corresponds to // section 5.3 of the IETF draft. // // TODO(alx): Since I'm only using pk in the ecrecover call, I could only pass // the x ordinate, and the parity of the y ordinate in the top bit of uWitness // (which I could make a uint256 without using any extra space.) Would save // about 2000 gas. https://www.pivotaltracker.com/story/show/170828567 function verifyVRFProof( uint256[2] memory pk, uint256[2] memory gamma, uint256 c, uint256 s, uint256 seed, address uWitness, uint256[2] memory cGammaWitness, uint256[2] memory sHashWitness, uint256 zInv ) internal view { unchecked { require(isOnCurve(pk), "public key is not on curve"); require(isOnCurve(gamma), "gamma is not on curve"); require(isOnCurve(cGammaWitness), "cGammaWitness is not on curve"); require(isOnCurve(sHashWitness), "sHashWitness is not on curve"); // Step 5. of IETF draft section 5.3 (pk corresponds to 5.3's Y, and here // we use the address of u instead of u itself. Also, here we add the // terms instead of taking the difference, and in the proof consruction in // vrf.GenerateProof, we correspondingly take the difference instead of // taking the sum as they do in step 7 of section 5.1.) require(verifyLinearCombinationWithGenerator(c, pk, s, uWitness), "addr(c*pk+s*g)!=_uWitness"); // Step 4. of IETF draft section 5.3 (pk corresponds to Y, seed to alpha_string) uint256[2] memory hash = hashToCurve(pk, seed); // Step 6. of IETF draft section 5.3, but see note for step 5 about +/- terms uint256[2] memory v = linearCombination(c, gamma, cGammaWitness, s, hash, sHashWitness, zInv); // Steps 7. and 8. of IETF draft section 5.3 uint256 derivedC = scalarFromCurvePoints(hash, pk, gamma, uWitness, v); require(c == derivedC, "invalid proof"); } } // Domain-separation tag for the hash used as the final VRF output. // Corresponds to vrfRandomOutputHashPrefix in vrf.go uint256 internal constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3; struct Proof { uint256[2] pk; uint256[2] gamma; uint256 c; uint256 s; uint256 seed; address uWitness; uint256[2] cGammaWitness; uint256[2] sHashWitness; uint256 zInv; } /* *************************************************************************** * @notice Returns proof's output, if proof is valid. Otherwise reverts * @param proof vrf proof components * @param seed seed used to generate the vrf output * * Throws if proof is invalid, otherwise: * @return output i.e., the random output implied by the proof * *************************************************************************** */ function randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) { verifyVRFProof( proof.pk, proof.gamma, proof.c, proof.s, seed, proof.uWitness, proof.cGammaWitness, proof.sHashWitness, proof.zInv ); output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma))); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./ConfirmedOwnerWithProposal.sol"; /** * @title The ConfirmedOwner contract * @notice A contract with helpers for basic contract ownership. */ contract ConfirmedOwner is ConfirmedOwnerWithProposal { constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/OwnableInterface.sol"; /** * @title The ConfirmedOwner contract * @notice A contract with helpers for basic contract ownership. */ contract ConfirmedOwnerWithProposal is OwnableInterface { address private s_owner; address private s_pendingOwner; event OwnershipTransferRequested(address indexed from, address indexed to); event OwnershipTransferred(address indexed from, address indexed to); constructor(address newOwner, address pendingOwner) { require(newOwner != address(0), "Cannot set owner to zero"); s_owner = newOwner; if (pendingOwner != address(0)) { _transferOwnership(pendingOwner); } } /** * @notice Allows an owner to begin transferring ownership to a new address, * pending. */ function transferOwnership(address to) public override onlyOwner { _transferOwnership(to); } /** * @notice Allows an ownership transfer to be completed by the recipient. */ function acceptOwnership() external override { require(msg.sender == s_pendingOwner, "Must be proposed owner"); address oldOwner = s_owner; s_owner = msg.sender; s_pendingOwner = address(0); emit OwnershipTransferred(oldOwner, msg.sender); } /** * @notice Get the current owner */ function owner() public view override returns (address) { return s_owner; } /** * @notice validate, transfer ownership, and emit relevant events */ function _transferOwnership(address to) private { require(to != msg.sender, "Cannot transfer to self"); s_pendingOwner = to; emit OwnershipTransferRequested(s_owner, to); } /** * @notice validate access */ function _validateOwnership() internal view { require(msg.sender == s_owner, "Only callable by owner"); } /** * @notice Reverts if called by anyone other than the contract owner. */ modifier onlyOwner() { _validateOwnership(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface OwnableInterface { function owner() external returns (address); function transferOwnership(address recipient) external; function acceptOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../dev/VRFCoordinatorV2.sol"; contract VRFCoordinatorV2TestHelper is VRFCoordinatorV2 { uint96 s_paymentAmount; uint256 s_gasStart; constructor( address link, address blockhashStore, address linkEthFeed ) // solhint-disable-next-line no-empty-blocks VRFCoordinatorV2(link, blockhashStore, linkEthFeed) { /* empty */ } function calculatePaymentAmountTest( uint256 gasAfterPaymentCalculation, uint32 fulfillmentFlatFeeLinkPPM, uint256 weiPerUnitGas ) external { s_paymentAmount = calculatePaymentAmount( gasleft(), gasAfterPaymentCalculation, fulfillmentFlatFeeLinkPPM, weiPerUnitGas ); } function getPaymentAmount() public view returns (uint96) { return s_paymentAmount; } function getGasStart() public view returns (uint256) { return s_gasStart; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./ConfirmedOwner.sol"; import "./interfaces/AggregatorValidatorInterface.sol"; import "./interfaces/TypeAndVersionInterface.sol"; contract ValidatorProxy is AggregatorValidatorInterface, TypeAndVersionInterface, ConfirmedOwner { /// @notice Uses a single storage slot to store the current address struct AggregatorConfiguration { address target; bool hasNewProposal; } struct ValidatorConfiguration { AggregatorValidatorInterface target; bool hasNewProposal; } // Configuration for the current aggregator AggregatorConfiguration private s_currentAggregator; // Proposed aggregator address address private s_proposedAggregator; // Configuration for the current validator ValidatorConfiguration private s_currentValidator; // Proposed validator address AggregatorValidatorInterface private s_proposedValidator; event AggregatorProposed(address indexed aggregator); event AggregatorUpgraded(address indexed previous, address indexed current); event ValidatorProposed(AggregatorValidatorInterface indexed validator); event ValidatorUpgraded(AggregatorValidatorInterface indexed previous, AggregatorValidatorInterface indexed current); /// @notice The proposed aggregator called validate, but the call was not passed on to any validators event ProposedAggregatorValidateCall( address indexed proposed, uint256 previousRoundId, int256 previousAnswer, uint256 currentRoundId, int256 currentAnswer ); /** * @notice Construct the ValidatorProxy with an aggregator and a validator * @param aggregator address * @param validator address */ constructor(address aggregator, AggregatorValidatorInterface validator) ConfirmedOwner(msg.sender) { s_currentAggregator = AggregatorConfiguration({target: aggregator, hasNewProposal: false}); s_currentValidator = ValidatorConfiguration({target: validator, hasNewProposal: false}); } /** * @notice Validate a transmission * @dev Must be called by either the `s_currentAggregator.target`, or the `s_proposedAggregator`. * If called by the `s_currentAggregator.target` this function passes the call on to the `s_currentValidator.target` * and the `s_proposedValidator`, if it is set. * If called by the `s_proposedAggregator` this function emits a `ProposedAggregatorValidateCall` to signal that * the call was received. * @dev To guard against external `validate` calls reverting, we use raw calls here. * We favour `call` over try-catch to ensure that failures are avoided even if the validator address is incorrectly * set as a non-contract address. * @dev If the `aggregator` and `validator` are the same contract or collude, this could exhibit reentrancy behavior. * However, since that contract would have to be explicitly written for reentrancy and that the `owner` would have * to configure this contract to use that malicious contract, we refrain from using mutex or check here. * @dev This does not perform any checks on any roundId, so it is possible that a validator receive different reports * for the same roundId at different points in time. Validator implementations should be aware of this. * @param previousRoundId uint256 * @param previousAnswer int256 * @param currentRoundId uint256 * @param currentAnswer int256 * @return bool */ function validate( uint256 previousRoundId, int256 previousAnswer, uint256 currentRoundId, int256 currentAnswer ) external override returns (bool) { address currentAggregator = s_currentAggregator.target; if (msg.sender != currentAggregator) { address proposedAggregator = s_proposedAggregator; require(msg.sender == proposedAggregator, "Not a configured aggregator"); // If the aggregator is still in proposed state, emit an event and don't push to any validator. // This is to confirm that `validate` is being called prior to upgrade. emit ProposedAggregatorValidateCall( proposedAggregator, previousRoundId, previousAnswer, currentRoundId, currentAnswer ); return true; } // Send the validate call to the current validator ValidatorConfiguration memory currentValidator = s_currentValidator; address currentValidatorAddress = address(currentValidator.target); require(currentValidatorAddress != address(0), "No validator set"); currentValidatorAddress.call( abi.encodeWithSelector( AggregatorValidatorInterface.validate.selector, previousRoundId, previousAnswer, currentRoundId, currentAnswer ) ); // If there is a new proposed validator, send the validate call to that validator also if (currentValidator.hasNewProposal) { address(s_proposedValidator).call( abi.encodeWithSelector( AggregatorValidatorInterface.validate.selector, previousRoundId, previousAnswer, currentRoundId, currentAnswer ) ); } return true; } /** AGGREGATOR CONFIGURATION FUNCTIONS **/ /** * @notice Propose an aggregator * @dev A zero address can be used to unset the proposed aggregator. Only owner can call. * @param proposed address */ function proposeNewAggregator(address proposed) external onlyOwner { require(s_proposedAggregator != proposed && s_currentAggregator.target != proposed, "Invalid proposal"); s_proposedAggregator = proposed; // If proposed is zero address, hasNewProposal = false s_currentAggregator.hasNewProposal = (proposed != address(0)); emit AggregatorProposed(proposed); } /** * @notice Upgrade the aggregator by setting the current aggregator as the proposed aggregator. * @dev Must have a proposed aggregator. Only owner can call. */ function upgradeAggregator() external onlyOwner { // Get configuration in memory AggregatorConfiguration memory current = s_currentAggregator; address previous = current.target; address proposed = s_proposedAggregator; // Perform the upgrade require(current.hasNewProposal, "No proposal"); s_currentAggregator = AggregatorConfiguration({target: proposed, hasNewProposal: false}); delete s_proposedAggregator; emit AggregatorUpgraded(previous, proposed); } /** * @notice Get aggregator details * @return current address * @return hasProposal bool * @return proposed address */ function getAggregators() external view returns ( address current, bool hasProposal, address proposed ) { current = s_currentAggregator.target; hasProposal = s_currentAggregator.hasNewProposal; proposed = s_proposedAggregator; } /** VALIDATOR CONFIGURATION FUNCTIONS **/ /** * @notice Propose an validator * @dev A zero address can be used to unset the proposed validator. Only owner can call. * @param proposed address */ function proposeNewValidator(AggregatorValidatorInterface proposed) external onlyOwner { require(s_proposedValidator != proposed && s_currentValidator.target != proposed, "Invalid proposal"); s_proposedValidator = proposed; // If proposed is zero address, hasNewProposal = false s_currentValidator.hasNewProposal = (address(proposed) != address(0)); emit ValidatorProposed(proposed); } /** * @notice Upgrade the validator by setting the current validator as the proposed validator. * @dev Must have a proposed validator. Only owner can call. */ function upgradeValidator() external onlyOwner { // Get configuration in memory ValidatorConfiguration memory current = s_currentValidator; AggregatorValidatorInterface previous = current.target; AggregatorValidatorInterface proposed = s_proposedValidator; // Perform the upgrade require(current.hasNewProposal, "No proposal"); s_currentValidator = ValidatorConfiguration({target: proposed, hasNewProposal: false}); delete s_proposedValidator; emit ValidatorUpgraded(previous, proposed); } /** * @notice Get validator details * @return current address * @return hasProposal bool * @return proposed address */ function getValidators() external view returns ( AggregatorValidatorInterface current, bool hasProposal, AggregatorValidatorInterface proposed ) { current = s_currentValidator.target; hasProposal = s_currentValidator.hasNewProposal; proposed = s_proposedValidator; } /** * @notice The type and version of this contract * @return Type and version string */ function typeAndVersion() external pure virtual override returns (string memory) { return "ValidatorProxy 1.0.0"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorValidatorInterface { function validate( uint256 previousRoundId, int256 previousAnswer, uint256 currentRoundId, int256 currentAnswer ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/TypeAndVersionInterface.sol"; import "../interfaces/AggregatorValidatorInterface.sol"; import "../interfaces/AccessControllerInterface.sol"; import "../SimpleWriteAccessController.sol"; /* ./dev dependencies - to be moved from ./dev after audit */ import "./interfaces/FlagsInterface.sol"; import "./interfaces/ForwarderInterface.sol"; import "./vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol"; /** * @title OptimismValidator - makes xDomain L2 Flags contract call (using L2 xDomain Forwarder contract) * @notice Allows to raise and lower Flags on the Optimism L2 network through L1 bridge * - The internal AccessController controls the access of the validate method */ contract OptimismValidator is TypeAndVersionInterface, AggregatorValidatorInterface, SimpleWriteAccessController { /// @dev Follows: https://eips.ethereum.org/EIPS/eip-1967 address public constant FLAG_OPTIMISM_SEQ_OFFLINE = address(bytes20(bytes32(uint256(keccak256("chainlink.flags.optimism-seq-offline")) - 1))); // Encode underlying Flags call/s bytes private constant CALL_RAISE_FLAG = abi.encodeWithSelector(FlagsInterface.raiseFlag.selector, FLAG_OPTIMISM_SEQ_OFFLINE); bytes private constant CALL_LOWER_FLAG = abi.encodeWithSelector(FlagsInterface.lowerFlag.selector, FLAG_OPTIMISM_SEQ_OFFLINE); uint32 private constant CALL_GAS_LIMIT = 1_200_000; int256 private constant ANSWER_SEQ_OFFLINE = 1; address public immutable CROSS_DOMAIN_MESSENGER; address public immutable L2_CROSS_DOMAIN_FORWARDER; address public immutable L2_FLAGS; /** * @param crossDomainMessengerAddr address the xDomain bridge messenger (Optimism bridge L1) contract address * @param l2CrossDomainForwarderAddr the L2 Forwarder contract address * @param l2FlagsAddr the L2 Flags contract address */ constructor( address crossDomainMessengerAddr, address l2CrossDomainForwarderAddr, address l2FlagsAddr ) { require(crossDomainMessengerAddr != address(0), "Invalid xDomain Messenger address"); require(l2CrossDomainForwarderAddr != address(0), "Invalid L2 xDomain Forwarder address"); require(l2FlagsAddr != address(0), "Invalid L2 Flags address"); CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; L2_CROSS_DOMAIN_FORWARDER = l2CrossDomainForwarderAddr; L2_FLAGS = l2FlagsAddr; } /** * @notice versions: * * - OptimismValidator 0.1.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "OptimismValidator 0.1.0"; } /** * @notice validate method sends an xDomain L2 tx to update Flags contract, in case of change from `previousAnswer`. * @dev A message is sent via the Optimism CrossDomainMessenger L1 contract. The "payment" for L2 execution happens on L1, * using the gas attached to this tx (some extra gas is burned by the Optimism bridge to avoid DoS attacks). * This method is accessed controlled. * @param previousAnswer previous aggregator answer * @param currentAnswer new aggregator answer - value of 1 considers the service offline. */ function validate( uint256, /* previousRoundId */ int256 previousAnswer, uint256, /* currentRoundId */ int256 currentAnswer ) external override checkAccess returns (bool) { // Avoids resending to L2 the same tx on every call if (previousAnswer == currentAnswer) { return true; // noop } // Encode the Forwarder call bytes4 selector = ForwarderInterface.forward.selector; address target = L2_FLAGS; // Choose and encode the underlying Flags call bytes memory data = currentAnswer == ANSWER_SEQ_OFFLINE ? CALL_RAISE_FLAG : CALL_LOWER_FLAG; bytes memory message = abi.encodeWithSelector(selector, target, data); // Make the xDomain call iOVM_CrossDomainMessenger(CROSS_DOMAIN_MESSENGER).sendMessage(L2_CROSS_DOMAIN_FORWARDER, message, CALL_GAS_LIMIT); // return success return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AccessControllerInterface { function hasAccess(address user, bytes calldata data) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./ConfirmedOwner.sol"; import "./interfaces/AccessControllerInterface.sol"; /** * @title SimpleWriteAccessController * @notice Gives access to accounts explicitly added to an access list by the * controller's owner. * @dev does not make any special permissions for externally, see * SimpleReadAccessController for that. */ contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwner { bool public checkEnabled; mapping(address => bool) internal accessList; event AddedAccess(address user); event RemovedAccess(address user); event CheckAccessEnabled(); event CheckAccessDisabled(); constructor() ConfirmedOwner(msg.sender) { checkEnabled = true; } /** * @notice Returns the access of an address * @param _user The address to query */ function hasAccess(address _user, bytes memory) public view virtual override returns (bool) { return accessList[_user] || !checkEnabled; } /** * @notice Adds an address to the access list * @param _user The address to add */ function addAccess(address _user) external onlyOwner { if (!accessList[_user]) { accessList[_user] = true; emit AddedAccess(_user); } } /** * @notice Removes an address from the access list * @param _user The address to remove */ function removeAccess(address _user) external onlyOwner { if (accessList[_user]) { accessList[_user] = false; emit RemovedAccess(_user); } } /** * @notice makes the access check enforced */ function enableAccessCheck() external onlyOwner { if (!checkEnabled) { checkEnabled = true; emit CheckAccessEnabled(); } } /** * @notice makes the access check unenforced */ function disableAccessCheck() external onlyOwner { if (checkEnabled) { checkEnabled = false; emit CheckAccessDisabled(); } } /** * @dev reverts if the caller does not have access */ modifier checkAccess() { require(hasAccess(msg.sender, msg.data), "No access"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; interface FlagsInterface { function getFlag(address) external view returns (bool); function getFlags(address[] calldata) external view returns (bool[] memory); function raiseFlag(address) external; function raiseFlags(address[] calldata) external; function lowerFlag(address) external; function lowerFlags(address[] calldata) external; function setRaisingAccessController(address) external; function setLoweringAccessController(address) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title ForwarderInterface - forwards a call to a target, under some conditions interface ForwarderInterface { /** * @notice forward calls the `target` with `data` * @param target contract address to be called * @param data to send to target contract */ function forward(address target, bytes memory data) external; }
pragma solidity >=0.7.6 <0.9.0; /** * @title iOVM_CrossDomainMessenger */ interface iOVM_CrossDomainMessenger { /********** * Events * **********/ event SentMessage(bytes message); event RelayedMessage(bytes32 msgHash); event FailedRelayedMessage(bytes32 msgHash); /************* * Variables * *************/ function xDomainMessageSender() external view returns (address); /******************** * Public Functions * ********************/ /** * Sends a cross domain message to the target messenger. * @param _target Target contract address. * @param _message Message to send to the target. * @param _gasLimit Gas limit for the provided message. */ function sendMessage( address _target, bytes calldata _message, uint32 _gasLimit ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/TypeAndVersionInterface.sol"; /* ./dev dependencies - to be moved from ./dev after audit */ import "./CrossDomainForwarder.sol"; import "./vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol"; /** * @title OptimismCrossDomainForwarder - L1 xDomain account representation * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. * @dev Any other L2 contract which uses this contract's address as a privileged position, * can be considered to be owned by the `l1Owner` */ contract OptimismCrossDomainForwarder is TypeAndVersionInterface, CrossDomainForwarder { // OVM_L2CrossDomainMessenger is a precompile usually deployed to 0x4200000000000000000000000000000000000007 address private immutable OVM_CROSS_DOMAIN_MESSENGER; /** * @notice creates a new Optimism xDomain Forwarder contract * @param crossDomainMessengerAddr the xDomain bridge messenger (Optimism bridge L2) contract address * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn */ constructor(address crossDomainMessengerAddr, address l1OwnerAddr) CrossDomainForwarder(l1OwnerAddr) { require(crossDomainMessengerAddr != address(0), "Invalid xDomain Messenger address"); OVM_CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; } /** * @notice versions: * * - OptimismCrossDomainForwarder 0.1.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "OptimismCrossDomainForwarder 0.1.0"; } /** * @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` beeing the L1 owner address * @inheritdoc ForwarderInterface */ function forward(address target, bytes memory data) external override { // 1. The call MUST come from the L1 Messenger require(msg.sender == OVM_CROSS_DOMAIN_MESSENGER, "Sender is not the L2 messenger"); // 2. The L1 Messenger's caller MUST be the L1 Owner require( iOVM_CrossDomainMessenger(OVM_CROSS_DOMAIN_MESSENGER).xDomainMessageSender() == l1Owner(), "xDomain sender is not the L1 owner" ); // 3. Make the external call (bool success, bytes memory res) = target.call(data); require(success, string(abi.encode("xDomain call failed:", res))); } /** * @notice This is always the address of the OVM_L2CrossDomainMessenger contract * @inheritdoc CrossDomainForwarder */ function crossDomainMessenger() public view virtual override returns (address) { return OVM_CROSS_DOMAIN_MESSENGER; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../ConfirmedOwner.sol"; import "./interfaces/ForwarderInterface.sol"; /** * @title CrossDomainForwarder - L1 xDomain account representation * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. * @dev Any other L2 contract which uses this contract's address as a privileged position, * can be considered to be owned by the `l1Owner` */ abstract contract CrossDomainForwarder is ForwarderInterface, ConfirmedOwner { address private s_l1Owner; event L1OwnershipTransferred(address indexed from, address indexed to); /** * @notice creates a new xDomain Forwarder contract * @dev Forwarding can be disabled by setting the L1 owner as `address(0)`. * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn */ constructor(address l1OwnerAddr) ConfirmedOwner(msg.sender) { _setL1Owner(l1OwnerAddr); } /// @return xDomain messenger address (L2 `msg.sender`) function crossDomainMessenger() public view virtual returns (address); /// @return L1 owner address function l1Owner() public view virtual returns (address) { return s_l1Owner; } /** * @notice transfer ownership of this account to a new L1 owner * @dev Forwarding can be disabled by setting the L1 owner as `address(0)`. Accessible only by owner. * @param to new L1 owner that will be allowed to call the forward fn */ function transferL1Ownership(address to) external virtual onlyOwner { _setL1Owner(to); } /// @notice internal method that stores the L1 owner function _setL1Owner(address to) internal { address from = s_l1Owner; if (from != to) { s_l1Owner = to; emit L1OwnershipTransferred(from, to); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/AggregatorValidatorInterface.sol"; import "../interfaces/TypeAndVersionInterface.sol"; import "../interfaces/AccessControllerInterface.sol"; import "../interfaces/AggregatorV3Interface.sol"; import "../SimpleWriteAccessController.sol"; /* ./dev dependencies - to be moved from ./dev after audit */ import "./interfaces/ForwarderInterface.sol"; import "./interfaces/FlagsInterface.sol"; import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol"; import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; import "./vendor/arb-os/e8d9696f21/contracts/arbos/builtin/ArbSys.sol"; import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; /** * @title ArbitrumValidator - makes xDomain L2 Flags contract call (using L2 xDomain Forwarder contract) * @notice Allows to raise and lower Flags on the Arbitrum L2 network through L1 bridge * - The internal AccessController controls the access of the validate method * - Gas configuration is controlled by a configurable external SimpleWriteAccessController * - Funds on the contract are managed by the owner */ contract ArbitrumValidator is TypeAndVersionInterface, AggregatorValidatorInterface, SimpleWriteAccessController { enum PaymentStrategy { L1, L2 } // Config for L1 -> L2 Arbitrum retryable ticket message struct GasConfig { uint256 maxGas; uint256 gasPriceBid; address gasPriceL1FeedAddr; } /// @dev Precompiled contract that exists in every Arbitrum chain at address(100). Exposes a variety of system-level functionality. address constant ARBSYS_ADDR = address(0x0000000000000000000000000000000000000064); /// @dev Follows: https://eips.ethereum.org/EIPS/eip-1967 address public constant FLAG_ARBITRUM_SEQ_OFFLINE = address(bytes20(bytes32(uint256(keccak256("chainlink.flags.arbitrum-seq-offline")) - 1))); // Encode underlying Flags call/s bytes private constant CALL_RAISE_FLAG = abi.encodeWithSelector(FlagsInterface.raiseFlag.selector, FLAG_ARBITRUM_SEQ_OFFLINE); bytes private constant CALL_LOWER_FLAG = abi.encodeWithSelector(FlagsInterface.lowerFlag.selector, FLAG_ARBITRUM_SEQ_OFFLINE); int256 private constant ANSWER_SEQ_OFFLINE = 1; address public immutable CROSS_DOMAIN_MESSENGER; address public immutable L2_CROSS_DOMAIN_FORWARDER; address public immutable L2_FLAGS; // L2 xDomain alias address of this contract address public immutable L2_ALIAS = AddressAliasHelper.applyL1ToL2Alias(address(this)); PaymentStrategy private s_paymentStrategy; GasConfig private s_gasConfig; AccessControllerInterface private s_configAC; /** * @notice emitted when a new payment strategy is set * @param paymentStrategy strategy describing how the contract pays for xDomain calls */ event PaymentStrategySet(PaymentStrategy indexed paymentStrategy); /** * @notice emitted when a new gas configuration is set * @param maxGas gas limit for immediate L2 execution attempt. * @param gasPriceBid maximum L2 gas price to pay * @param gasPriceL1FeedAddr address of the L1 gas price feed (used to approximate Arbitrum retryable ticket submission cost) */ event GasConfigSet(uint256 maxGas, uint256 gasPriceBid, address indexed gasPriceL1FeedAddr); /** * @notice emitted when a new gas access-control contract is set * @param previous the address prior to the current setting * @param current the address of the new access-control contract */ event ConfigACSet(address indexed previous, address indexed current); /** * @notice emitted when a new ETH withdrawal from L2 was requested * @param id unique id of the published retryable transaction (keccak256(requestID, uint(0)) * @param amount of funds to withdraw */ event L2WithdrawalRequested(uint256 indexed id, uint256 amount, address indexed refundAddr); /** * @param crossDomainMessengerAddr address the xDomain bridge messenger (Arbitrum Inbox L1) contract address * @param l2CrossDomainForwarderAddr the L2 Forwarder contract address * @param l2FlagsAddr the L2 Flags contract address * @param configACAddr address of the access controller for managing gas price on Arbitrum * @param maxGas gas limit for immediate L2 execution attempt. A value around 1M should be sufficient * @param gasPriceBid maximum L2 gas price to pay * @param gasPriceL1FeedAddr address of the L1 gas price feed (used to approximate Arbitrum retryable ticket submission cost) * @param paymentStrategy strategy describing how the contract pays for xDomain calls */ constructor( address crossDomainMessengerAddr, address l2CrossDomainForwarderAddr, address l2FlagsAddr, address configACAddr, uint256 maxGas, uint256 gasPriceBid, address gasPriceL1FeedAddr, PaymentStrategy paymentStrategy ) { require(crossDomainMessengerAddr != address(0), "Invalid xDomain Messenger address"); require(l2CrossDomainForwarderAddr != address(0), "Invalid L2 xDomain Forwarder address"); require(l2FlagsAddr != address(0), "Invalid Flags contract address"); CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; L2_CROSS_DOMAIN_FORWARDER = l2CrossDomainForwarderAddr; L2_FLAGS = l2FlagsAddr; // Additional L2 payment configuration _setConfigAC(configACAddr); _setGasConfig(maxGas, gasPriceBid, gasPriceL1FeedAddr); _setPaymentStrategy(paymentStrategy); } /** * @notice versions: * * - ArbitrumValidator 0.1.0: initial release * - ArbitrumValidator 0.2.0: critical Arbitrum network update * - xDomain `msg.sender` backwards incompatible change (now an alias address) * - new `withdrawFundsFromL2` fn that withdraws from L2 xDomain alias address * - approximation of `maxSubmissionCost` using a L1 gas price feed * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "ArbitrumValidator 0.2.0"; } /// @return stored PaymentStrategy function paymentStrategy() external view virtual returns (PaymentStrategy) { return s_paymentStrategy; } /// @return stored GasConfig function gasConfig() external view virtual returns (GasConfig memory) { return s_gasConfig; } /// @return config AccessControllerInterface contract address function configAC() external view virtual returns (address) { return address(s_configAC); } /** * @notice makes this contract payable * @dev receives funds: * - to use them (if configured) to pay for L2 execution on L1 * - when withdrawing funds from L2 xDomain alias address (pay for L2 execution on L2) */ receive() external payable {} /** * @notice withdraws all funds available in this contract to the msg.sender * @dev only owner can call this */ function withdrawFunds() external onlyOwner { address payable recipient = payable(msg.sender); uint256 amount = address(this).balance; Address.sendValue(recipient, amount); } /** * @notice withdraws all funds available in this contract to the address specified * @dev only owner can call this * @param recipient address where to send the funds */ function withdrawFundsTo(address payable recipient) external onlyOwner { uint256 amount = address(this).balance; Address.sendValue(recipient, amount); } /** * @notice withdraws funds from L2 xDomain alias address (representing this L1 contract) * @dev only owner can call this * @param amount of funds to withdraws * @param refundAddr address where gas excess on L2 will be sent * WARNING: `refundAddr` is not aliased! Make sure you can recover the refunded funds on L2. * @return id unique id of the published retryable transaction (keccak256(requestID, uint(0)) */ function withdrawFundsFromL2(uint256 amount, address refundAddr) external onlyOwner returns (uint256 id) { // Build an xDomain message to trigger the ArbSys precompile, which will create a L2 -> L1 tx transferring `amount` bytes memory message = abi.encodeWithSelector(ArbSys.withdrawEth.selector, address(this)); // Make the xDomain call // NOTICE: We approximate the max submission cost of sending a retryable tx with specific calldata length. uint256 maxSubmissionCost = _approximateMaxSubmissionCost(message.length); uint256 maxGas = 120_000; // static `maxGas` for L2 -> L1 transfer uint256 gasPriceBid = s_gasConfig.gasPriceBid; uint256 l1PaymentValue = s_paymentStrategy == PaymentStrategy.L1 ? _maxRetryableTicketCost(maxSubmissionCost, maxGas, gasPriceBid) : 0; // NOTICE: In the case of PaymentStrategy.L2 the L2 xDomain alias address needs to be funded, as it will be paying the fee. id = IInbox(CROSS_DOMAIN_MESSENGER).createRetryableTicketNoRefundAliasRewrite{value: l1PaymentValue}( ARBSYS_ADDR, // target amount, // L2 call value (requested) maxSubmissionCost, refundAddr, // excessFeeRefundAddress refundAddr, // callValueRefundAddress maxGas, gasPriceBid, message ); emit L2WithdrawalRequested(id, amount, refundAddr); } /** * @notice sets config AccessControllerInterface contract * @dev only owner can call this * @param accessController new AccessControllerInterface contract address */ function setConfigAC(address accessController) external onlyOwner { _setConfigAC(accessController); } /** * @notice sets Arbitrum gas configuration * @dev access control provided by `configAC` * @param maxGas gas limit for immediate L2 execution attempt. A value around 1M should be sufficient * @param gasPriceBid maximum L2 gas price to pay * @param gasPriceL1FeedAddr address of the L1 gas price feed (used to approximate Arbitrum retryable ticket submission cost) */ function setGasConfig( uint256 maxGas, uint256 gasPriceBid, address gasPriceL1FeedAddr ) external onlyOwnerOrConfigAccess { _setGasConfig(maxGas, gasPriceBid, gasPriceL1FeedAddr); } /** * @notice sets the payment strategy * @dev access control provided by `configAC` * @param paymentStrategy strategy describing how the contract pays for xDomain calls */ function setPaymentStrategy(PaymentStrategy paymentStrategy) external onlyOwnerOrConfigAccess { _setPaymentStrategy(paymentStrategy); } /** * @notice validate method sends an xDomain L2 tx to update Flags contract, in case of change from `previousAnswer`. * @dev A retryable ticket is created on the Arbitrum L1 Inbox contract. The tx gas fee can be paid from this * contract providing a value, or if no L1 value is sent with the xDomain message the gas will be paid by * the L2 xDomain alias account (generated from `address(this)`). This method is accessed controlled. * @param previousAnswer previous aggregator answer * @param currentAnswer new aggregator answer - value of 1 considers the service offline. */ function validate( uint256, /* previousRoundId */ int256 previousAnswer, uint256, /* currentRoundId */ int256 currentAnswer ) external override checkAccess returns (bool) { // Avoids resending to L2 the same tx on every call if (previousAnswer == currentAnswer) { return true; } // Excess gas on L2 will be sent to the L2 xDomain alias address of this contract address refundAddr = L2_ALIAS; // Encode the Forwarder call bytes4 selector = ForwarderInterface.forward.selector; address target = L2_FLAGS; // Choose and encode the underlying Flags call bytes memory data = currentAnswer == ANSWER_SEQ_OFFLINE ? CALL_RAISE_FLAG : CALL_LOWER_FLAG; bytes memory message = abi.encodeWithSelector(selector, target, data); // Make the xDomain call // NOTICE: We approximate the max submission cost of sending a retryable tx with specific calldata length. uint256 maxSubmissionCost = _approximateMaxSubmissionCost(message.length); uint256 maxGas = s_gasConfig.maxGas; uint256 gasPriceBid = s_gasConfig.gasPriceBid; uint256 l1PaymentValue = s_paymentStrategy == PaymentStrategy.L1 ? _maxRetryableTicketCost(maxSubmissionCost, maxGas, gasPriceBid) : 0; // NOTICE: In the case of PaymentStrategy.L2 the L2 xDomain alias address needs to be funded, as it will be paying the fee. // We also ignore the returned msg number, that can be queried via the `InboxMessageDelivered` event. IInbox(CROSS_DOMAIN_MESSENGER).createRetryableTicketNoRefundAliasRewrite{value: l1PaymentValue}( L2_CROSS_DOMAIN_FORWARDER, // target 0, // L2 call value maxSubmissionCost, refundAddr, // excessFeeRefundAddress refundAddr, // callValueRefundAddress maxGas, gasPriceBid, message ); // return success return true; } /// @notice internal method that stores the payment strategy function _setPaymentStrategy(PaymentStrategy paymentStrategy) internal { s_paymentStrategy = paymentStrategy; emit PaymentStrategySet(paymentStrategy); } /// @notice internal method that stores the gas configuration function _setGasConfig( uint256 maxGas, uint256 gasPriceBid, address gasPriceL1FeedAddr ) internal { require(maxGas > 0, "Max gas is zero"); require(gasPriceBid > 0, "Gas price bid is zero"); require(gasPriceL1FeedAddr != address(0), "Gas price Aggregator is zero address"); s_gasConfig = GasConfig(maxGas, gasPriceBid, gasPriceL1FeedAddr); emit GasConfigSet(maxGas, gasPriceBid, gasPriceL1FeedAddr); } /// @notice Internal method that stores the configuration access controller function _setConfigAC(address accessController) internal { address previousAccessController = address(s_configAC); if (accessController != previousAccessController) { s_configAC = AccessControllerInterface(accessController); emit ConfigACSet(previousAccessController, accessController); } } /** * @notice Internal method that approximates the `maxSubmissionCost` (using the L1 gas price feed) * @dev On L2 this info is available via `ArbRetryableTx.getSubmissionPrice`. * @param calldataSizeInBytes xDomain message size in bytes */ function _approximateMaxSubmissionCost(uint256 calldataSizeInBytes) internal view returns (uint256) { (, int256 l1GasPriceInWei, , , ) = AggregatorV3Interface(s_gasConfig.gasPriceL1FeedAddr).latestRoundData(); uint256 l1GasPriceEstimate = uint256(l1GasPriceInWei) * 3; // add 200% buffer (price volatility error margin) return (l1GasPriceEstimate * calldataSizeInBytes) / 256 + l1GasPriceEstimate; } /// @notice Internal helper method that calculates the total cost of the xDomain retryable ticket call function _maxRetryableTicketCost( uint256 maxSubmissionCost, uint256 maxGas, uint256 gasPriceBid ) internal pure returns (uint256) { return maxSubmissionCost + maxGas * gasPriceBid; } /// @dev reverts if the caller does not have access to change the configuration modifier onlyOwnerOrConfigAccess() { require( msg.sender == owner() || (address(s_configAC) != address(0) && s_configAC.hasAccess(msg.sender, msg.data)), "No access" ); _; } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // NOTICE: pragma change from original (^0.6.11) pragma solidity ^0.8.0; import "./IBridge.sol"; import "./IMessageProvider.sol"; interface IInbox is IMessageProvider { function sendL2Message(bytes calldata messageData) external returns (uint256); function sendUnsignedTransaction( uint256 maxGas, uint256 gasPriceBid, uint256 nonce, address destAddr, uint256 amount, bytes calldata data ) external returns (uint256); function sendContractTransaction( uint256 maxGas, uint256 gasPriceBid, address destAddr, uint256 amount, bytes calldata data ) external returns (uint256); function sendL1FundedUnsignedTransaction( uint256 maxGas, uint256 gasPriceBid, uint256 nonce, address destAddr, bytes calldata data ) external payable returns (uint256); function sendL1FundedContractTransaction( uint256 maxGas, uint256 gasPriceBid, address destAddr, bytes calldata data ) external payable returns (uint256); function createRetryableTicketNoRefundAliasRewrite( address destAddr, uint256 arbTxCallValue, uint256 maxSubmissionCost, address submissionRefundAddress, address valueRefundAddress, uint256 maxGas, uint256 gasPriceBid, bytes calldata data ) external payable returns (uint256); function createRetryableTicket( address destAddr, uint256 arbTxCallValue, uint256 maxSubmissionCost, address submissionRefundAddress, address valueRefundAddress, uint256 maxGas, uint256 gasPriceBid, bytes calldata data ) external payable returns (uint256); function depositEth(address destAddr) external payable returns (uint256); function depositEthRetryable( address destAddr, uint256 maxSubmissionCost, uint256 maxGas, uint256 maxGasPrice ) external payable returns (uint256); function bridge() external view returns (IBridge); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2019-2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // NOTICE: pragma change from original (^0.6.11) pragma solidity ^0.8.0; library AddressAliasHelper { uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); /// @notice Utility function that converts the msg.sender viewed in the L2 to the /// address in the L1 that submitted a tx to the inbox /// @param l1Address L2 address as viewed in msg.sender /// @return l2Address the address in the L1 that triggered the tx to L2 function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { unchecked { l2Address = address(uint160(l1Address) + offset); } } /// @notice Utility function that converts the msg.sender viewed in the L2 to the /// address in the L1 that submitted a tx to the inbox /// @param l2Address L2 address as viewed in msg.sender /// @return l1Address the address in the L1 that triggered the tx to L2 function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { unchecked { l1Address = address(uint160(l2Address) - offset); } } }
// NOTICE: pragma change from original (>=0.4.21 <0.7.0) pragma solidity >=0.4.21 <0.9.0; /** * @title Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. Exposes a variety of system-level functionality. */ interface ArbSys { /** * @notice Get internal version number identifying an ArbOS build * @return version number as int */ function arbOSVersion() external pure returns (uint256); function arbChainID() external view returns (uint256); /** * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) * @return block number as int */ function arbBlockNumber() external view returns (uint256); /** * @notice Send given amount of Eth to dest from sender. * This is a convenience function, which is equivalent to calling sendTxToL1 with empty calldataForL1. * @param destination recipient address on L1 * @return unique identifier for this L2-to-L1 transaction. */ function withdrawEth(address destination) external payable returns (uint256); /** * @notice Send a transaction to L1 * @param destination recipient address on L1 * @param calldataForL1 (optional) calldata for L1 contract call * @return a unique identifier for this L2-to-L1 transaction. */ function sendTxToL1(address destination, bytes calldata calldataForL1) external payable returns (uint256); /** * @notice get the number of transactions issued by the given external account or the account sequence number of the given contract * @param account target account * @return the number of transactions issued by the given external account or the account sequence number of the given contract */ function getTransactionCount(address account) external view returns (uint256); /** * @notice get the value of target L2 storage slot * This function is only callable from address 0 to prevent contracts from being able to call it * @param account target account * @param index target index of storage slot * @return stotage value for the given account at the given index */ function getStorageAt(address account, uint256 index) external view returns (uint256); /** * @notice check if current call is coming from l1 * @return true if the caller of this was called directly from L1 */ function isTopLevelCall() external view returns (bool); event L2ToL1Transaction( address caller, address indexed destination, uint256 indexed uniqueId, uint256 indexed batchNumber, uint256 indexInBatch, uint256 arbBlockNum, uint256 ethBlockNum, uint256 timestamp, uint256 callvalue, bytes data ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // NOTICE: pragma change from original (^0.6.11) pragma solidity ^0.8.0; interface IBridge { event MessageDelivered( uint256 indexed messageIndex, bytes32 indexed beforeInboxAcc, address inbox, uint8 kind, address sender, bytes32 messageDataHash ); function deliverMessageToInbox( uint8 kind, address sender, bytes32 messageDataHash ) external payable returns (uint256); function executeCall( address destAddr, uint256 amount, bytes calldata data ) external returns (bool success, bytes memory returnData); // These are only callable by the admin function setInbox(address inbox, bool enabled) external; function setOutbox(address inbox, bool enabled) external; // View functions function activeOutbox() external view returns (address); function allowedInboxes(address inbox) external view returns (bool); function allowedOutboxes(address outbox) external view returns (bool); function inboxAccs(uint256 index) external view returns (bytes32); function messageCount() external view returns (uint256); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // NOTICE: pragma change from original (^0.6.11) pragma solidity ^0.8.0; interface IMessageProvider { event InboxMessageDelivered(uint256 indexed messageNum, bytes data); event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/TypeAndVersionInterface.sol"; import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; import "./CrossDomainForwarder.sol"; /** * @title ArbitrumCrossDomainForwarder - L1 xDomain account representation * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. * @dev Any other L2 contract which uses this contract's address as a privileged position, * can be considered to be owned by the `l1Owner` */ contract ArbitrumCrossDomainForwarder is TypeAndVersionInterface, CrossDomainForwarder { /** * @notice creates a new Arbitrum xDomain Forwarder contract * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn */ constructor(address l1OwnerAddr) CrossDomainForwarder(l1OwnerAddr) { // noop } /** * @notice versions: * * - ArbitrumCrossDomainForwarder 0.1.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "ArbitrumCrossDomainForwarder 0.1.0"; } /** * @notice The L2 xDomain `msg.sender`, generated from L1 sender address * @inheritdoc CrossDomainForwarder */ function crossDomainMessenger() public view virtual override returns (address) { return AddressAliasHelper.applyL1ToL2Alias(l1Owner()); } /** * @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` beeing the L1 owner address * @inheritdoc ForwarderInterface */ function forward(address target, bytes memory data) external override { // 1. The call MUST come from the L2 Messenger (deterministically generated from the L1 xDomain sender address) require(msg.sender == crossDomainMessenger(), "Sender is not the L2 messenger"); // 2. Make the external call (bool success, bytes memory res) = target.call(data); require(success, string(abi.encode("xDomain call failed:", res))); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./SimpleWriteAccessController.sol"; /** * @title SimpleReadAccessController * @notice Gives access to: * - any externally owned account (note that offchain actors can always read * any contract storage regardless of onchain access control measures, so this * does not weaken the access control while improving usability) * - accounts explicitly added to an access list * @dev SimpleReadAccessController is not suitable for access controlling writes * since it grants any externally owned account access! See * SimpleWriteAccessController for that. */ contract SimpleReadAccessController is SimpleWriteAccessController { /** * @notice Returns the access of an address * @param _user The address to query */ function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) { return super.hasAccess(_user, _calldata) || _user == tx.origin; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./SimpleReadAccessController.sol"; import "./interfaces/AccessControllerInterface.sol"; import "./interfaces/FlagsInterface.sol"; /** * @title The Flags contract * @notice Allows flags to signal to any reader on the access control list. * The owner can set flags, or designate other addresses to set flags. The * owner must turn the flags off, other setters cannot. An expected pattern is * to allow addresses to raise flags on themselves, so if you are subscribing to * FlagOn events you should filter for addresses you care about. */ contract Flags is FlagsInterface, SimpleReadAccessController { AccessControllerInterface public raisingAccessController; mapping(address => bool) private flags; event FlagRaised(address indexed subject); event FlagLowered(address indexed subject); event RaisingAccessControllerUpdated(address indexed previous, address indexed current); /** * @param racAddress address for the raising access controller. */ constructor(address racAddress) { setRaisingAccessController(racAddress); } /** * @notice read the warning flag status of a contract address. * @param subject The contract address being checked for a flag. * @return A true value indicates that a flag was raised and a * false value indicates that no flag was raised. */ function getFlag(address subject) external view override checkAccess returns (bool) { return flags[subject]; } /** * @notice read the warning flag status of a contract address. * @param subjects An array of addresses being checked for a flag. * @return An array of bools where a true value for any flag indicates that * a flag was raised and a false value indicates that no flag was raised. */ function getFlags(address[] calldata subjects) external view override checkAccess returns (bool[] memory) { bool[] memory responses = new bool[](subjects.length); for (uint256 i = 0; i < subjects.length; i++) { responses[i] = flags[subjects[i]]; } return responses; } /** * @notice enable the warning flag for an address. * Access is controlled by raisingAccessController, except for owner * who always has access. * @param subject The contract address whose flag is being raised */ function raiseFlag(address subject) external override { require(allowedToRaiseFlags(), "Not allowed to raise flags"); tryToRaiseFlag(subject); } /** * @notice enable the warning flags for multiple addresses. * Access is controlled by raisingAccessController, except for owner * who always has access. * @param subjects List of the contract addresses whose flag is being raised */ function raiseFlags(address[] calldata subjects) external override { require(allowedToRaiseFlags(), "Not allowed to raise flags"); for (uint256 i = 0; i < subjects.length; i++) { tryToRaiseFlag(subjects[i]); } } /** * @notice allows owner to disable the warning flags for multiple addresses. * @param subjects List of the contract addresses whose flag is being lowered */ function lowerFlags(address[] calldata subjects) external override onlyOwner { for (uint256 i = 0; i < subjects.length; i++) { address subject = subjects[i]; if (flags[subject]) { flags[subject] = false; emit FlagLowered(subject); } } } /** * @notice allows owner to change the access controller for raising flags. * @param racAddress new address for the raising access controller. */ function setRaisingAccessController(address racAddress) public override onlyOwner { address previous = address(raisingAccessController); if (previous != racAddress) { raisingAccessController = AccessControllerInterface(racAddress); emit RaisingAccessControllerUpdated(previous, racAddress); } } // PRIVATE function allowedToRaiseFlags() private view returns (bool) { return msg.sender == owner() || raisingAccessController.hasAccess(msg.sender, msg.data); } function tryToRaiseFlag(address subject) private { if (!flags[subject]) { flags[subject] = true; emit FlagRaised(subject); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface FlagsInterface { function getFlag(address) external view returns (bool); function getFlags(address[] calldata) external view returns (bool[] memory); function raiseFlag(address) external; function raiseFlags(address[] calldata) external; function lowerFlags(address[] calldata) external; function setRaisingAccessController(address) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../Flags.sol"; contract FlagsTestHelper { Flags public flags; constructor(address flagsContract) { flags = Flags(flagsContract); } function getFlag(address subject) external view returns (bool) { return flags.getFlag(subject); } function getFlags(address[] calldata subjects) external view returns (bool[] memory) { return flags.getFlags(subjects); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./AggregatorInterface.sol"; import "./AggregatorV3Interface.sol"; interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorInterface { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; pragma abicoder v2; import "./AggregatorV2V3Interface.sol"; interface FeedRegistryInterface { struct Phase { uint16 phaseId; uint80 startingAggregatorRoundId; uint80 endingAggregatorRoundId; } event FeedProposed( address indexed asset, address indexed denomination, address indexed proposedAggregator, address currentAggregator, address sender ); event FeedConfirmed( address indexed asset, address indexed denomination, address indexed latestAggregator, address previousAggregator, uint16 nextPhaseId, address sender ); // V3 AggregatorV3Interface function decimals(address base, address quote) external view returns (uint8); function description(address base, address quote) external view returns (string memory); function version(address base, address quote) external view returns (uint256); function latestRoundData(address base, address quote) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function getRoundData( address base, address quote, uint80 _roundId ) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); // V2 AggregatorInterface function latestAnswer(address base, address quote) external view returns (int256 answer); function latestTimestamp(address base, address quote) external view returns (uint256 timestamp); function latestRound(address base, address quote) external view returns (uint256 roundId); function getAnswer( address base, address quote, uint256 roundId ) external view returns (int256 answer); function getTimestamp( address base, address quote, uint256 roundId ) external view returns (uint256 timestamp); // Registry getters function getFeed(address base, address quote) external view returns (AggregatorV2V3Interface aggregator); function getPhaseFeed( address base, address quote, uint16 phaseId ) external view returns (AggregatorV2V3Interface aggregator); function isFeedEnabled(address aggregator) external view returns (bool); function getPhase( address base, address quote, uint16 phaseId ) external view returns (Phase memory phase); // Round helpers function getRoundFeed( address base, address quote, uint80 roundId ) external view returns (AggregatorV2V3Interface aggregator); function getPhaseRange( address base, address quote, uint16 phaseId ) external view returns (uint80 startingRoundId, uint80 endingRoundId); function getPreviousRoundId( address base, address quote, uint80 roundId ) external view returns (uint80 previousRoundId); function getNextRoundId( address base, address quote, uint80 roundId ) external view returns (uint80 nextRoundId); // Feed management function proposeFeed( address base, address quote, address aggregator ) external; function confirmFeed( address base, address quote, address aggregator ) external; // Proposed aggregator function getProposedFeed(address base, address quote) external view returns (AggregatorV2V3Interface proposedAggregator); function proposedGetRoundData( address base, address quote, uint80 roundId ) external view returns ( uint80 id, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function proposedLatestRoundData(address base, address quote) external view returns ( uint80 id, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); // Phases function getCurrentPhaseId(address base, address quote) external view returns (uint16 currentPhaseId); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/AggregatorValidatorInterface.sol"; contract MockAggregatorValidator is AggregatorValidatorInterface { uint8 immutable id; constructor(uint8 id_) { id = id_; } event ValidateCalled( uint8 id, uint256 previousRoundId, int256 previousAnswer, uint256 currentRoundId, int256 currentAnswer ); function validate( uint256 previousRoundId, int256 previousAnswer, uint256 currentRoundId, int256 currentAnswer ) external override returns (bool) { emit ValidateCalled(id, previousRoundId, previousAnswer, currentRoundId, currentAnswer); return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../dev/VRF.sol"; /** *********************************************************************** @notice Testing harness for VRF.sol, exposing its internal methods. Not to @notice be used for production. */ contract VRFTestHelper is VRF { function bigModExp_(uint256 base, uint256 exponent) public view returns (uint256) { return super.bigModExp(base, exponent); } function squareRoot_(uint256 x) public view returns (uint256) { return super.squareRoot(x); } function ySquared_(uint256 x) public pure returns (uint256) { return super.ySquared(x); } function fieldHash_(bytes memory b) public pure returns (uint256) { return super.fieldHash(b); } function hashToCurve_(uint256[2] memory pk, uint256 x) public view returns (uint256[2] memory) { return super.hashToCurve(pk, x); } function ecmulVerify_( uint256[2] memory x, uint256 scalar, uint256[2] memory q ) public pure returns (bool) { return super.ecmulVerify(x, scalar, q); } function projectiveECAdd_( uint256 px, uint256 py, uint256 qx, uint256 qy ) public pure returns ( uint256, uint256, uint256 ) { return super.projectiveECAdd(px, py, qx, qy); } function affineECAdd_( uint256[2] memory p1, uint256[2] memory p2, uint256 invZ ) public pure returns (uint256[2] memory) { return super.affineECAdd(p1, p2, invZ); } function verifyLinearCombinationWithGenerator_( uint256 c, uint256[2] memory p, uint256 s, address lcWitness ) public pure returns (bool) { return super.verifyLinearCombinationWithGenerator(c, p, s, lcWitness); } function linearCombination_( uint256 c, uint256[2] memory p1, uint256[2] memory cp1Witness, uint256 s, uint256[2] memory p2, uint256[2] memory sp2Witness, uint256 zInv ) public pure returns (uint256[2] memory) { return super.linearCombination(c, p1, cp1Witness, s, p2, sp2Witness, zInv); } function scalarFromCurvePoints_( uint256[2] memory hash, uint256[2] memory pk, uint256[2] memory gamma, address uWitness, uint256[2] memory v ) public pure returns (uint256) { return super.scalarFromCurvePoints(hash, pk, gamma, uWitness, v); } function isOnCurve_(uint256[2] memory p) public pure returns (bool) { return super.isOnCurve(p); } function verifyVRFProof_( uint256[2] memory pk, uint256[2] memory gamma, uint256 c, uint256 s, uint256 seed, address uWitness, uint256[2] memory cGammaWitness, uint256[2] memory sHashWitness, uint256 zInv ) public view { super.verifyVRFProof(pk, gamma, c, s, seed, uWitness, cGammaWitness, sHashWitness, zInv); } function randomValueFromVRFProof_(Proof memory proof, uint256 seed) public view returns (uint256 output) { return super.randomValueFromVRFProof(proof, seed); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/LinkTokenInterface.sol"; import "./VRFRequestIDBase.sol"; /** **************************************************************************** * @notice Interface for contracts using VRF randomness * ***************************************************************************** * @dev PURPOSE * * @dev Reggie the Random Oracle (not his real job) wants to provide randomness * @dev to Vera the verifier in such a way that Vera can be sure he's not * @dev making his output up to suit himself. Reggie provides Vera a public key * @dev to which he knows the secret key. Each time Vera provides a seed to * @dev Reggie, he gives back a value which is computed completely * @dev deterministically from the seed and the secret key. * * @dev Reggie provides a proof by which Vera can verify that the output was * @dev correctly computed once Reggie tells it to her, but without that proof, * @dev the output is indistinguishable to her from a uniform random sample * @dev from the output space. * * @dev The purpose of this contract is to make it easy for unrelated contracts * @dev to talk to Vera the verifier about the work Reggie is doing, to provide * @dev simple access to a verifiable source of randomness. * ***************************************************************************** * @dev USAGE * * @dev Calling contracts must inherit from VRFConsumerBase, and can * @dev initialize VRFConsumerBase's attributes in their constructor as * @dev shown: * * @dev contract VRFConsumer { * @dev constuctor(<other arguments>, address _vrfCoordinator, address _link) * @dev VRFConsumerBase(_vrfCoordinator, _link) public { * @dev <initialization with other arguments goes here> * @dev } * @dev } * * @dev The oracle will have given you an ID for the VRF keypair they have * @dev committed to (let's call it keyHash), and have told you the minimum LINK * @dev price for VRF service. Make sure your contract has sufficient LINK, and * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you * @dev want to generate randomness from. * * @dev Once the VRFCoordinator has received and validated the oracle's response * @dev to your request, it will call your contract's fulfillRandomness method. * * @dev The randomness argument to fulfillRandomness is the actual random value * @dev generated from your seed. * * @dev The requestId argument is generated from the keyHash and the seed by * @dev makeRequestId(keyHash, seed). If your contract could have concurrent * @dev requests open, you can use the requestId to track which seed is * @dev associated with which randomness. See VRFRequestIDBase.sol for more * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, * @dev if your contract could have multiple requests in flight simultaneously.) * * @dev Colliding `requestId`s are cryptographically impossible as long as seeds * @dev differ. (Which is critical to making unpredictable randomness! See the * @dev next section.) * * ***************************************************************************** * @dev SECURITY CONSIDERATIONS * * @dev A method with the ability to call your fulfillRandomness method directly * @dev could spoof a VRF response with any random value, so it's critical that * @dev it cannot be directly called by anything other than this base contract * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). * * @dev For your users to trust that your contract's random behavior is free * @dev from malicious interference, it's best if you can write it so that all * @dev behaviors implied by a VRF response are executed *during* your * @dev fulfillRandomness method. If your contract must store the response (or * @dev anything derived from it) and use it later, you must ensure that any * @dev user-significant behavior which depends on that stored value cannot be * @dev manipulated by a subsequent VRF request. * * @dev Similarly, both miners and the VRF oracle itself have some influence * @dev over the order in which VRF responses appear on the blockchain, so if * @dev your contract could have multiple VRF requests in flight simultaneously, * @dev you must ensure that the order in which the VRF responses arrive cannot * @dev be used to manipulate your contract's user-significant behavior. * * @dev Since the ultimate input to the VRF is mixed with the block hash of the * @dev block in which the request is made, user-provided seeds have no impact * @dev on its economic security properties. They are only included for API * @dev compatability with previous versions of this contract. * * @dev Since the block hash of the block which contains the requestRandomness * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful * @dev miner could, in principle, fork the blockchain to evict the block * @dev containing the request, forcing the request to be included in a * @dev different block with a different hash, and therefore a different input * @dev to the VRF. However, such an attack would incur a substantial economic * @dev cost. This cost scales with the number of blocks the VRF oracle waits * @dev until it calls responds to a request. */ abstract contract VRFConsumerBase is VRFRequestIDBase { /** * @notice fulfillRandomness handles the VRF response. Your contract must * @notice implement it. See "SECURITY CONSIDERATIONS" above for important * @notice principles to keep in mind when implementing your fulfillRandomness * @notice method. * * @dev VRFConsumerBase expects its subcontracts to have a method with this * @dev signature, and will call it once it has verified the proof * @dev associated with the randomness. (It is triggered via a call to * @dev rawFulfillRandomness, below.) * * @param requestId The Id initially returned by requestRandomness * @param randomness the VRF output */ function fulfillRandomness(bytes32 requestId, uint256 randomness) internal virtual; /** * @dev In order to keep backwards compatibility we have kept the user * seed field around. We remove the use of it because given that the blockhash * enters later, it overrides whatever randomness the used seed provides. * Given that it adds no security, and can easily lead to misunderstandings, * we have removed it from usage and can now provide a simpler API. */ uint256 private constant USER_SEED_PLACEHOLDER = 0; /** * @notice requestRandomness initiates a request for VRF output given _seed * * @dev The fulfillRandomness method receives the output, once it's provided * @dev by the Oracle, and verified by the vrfCoordinator. * * @dev The _keyHash must already be registered with the VRFCoordinator, and * @dev the _fee must exceed the fee specified during registration of the * @dev _keyHash. * * @dev The _seed parameter is vestigial, and is kept only for API * @dev compatibility with older versions. It can't *hurt* to mix in some of * @dev your own randomness, here, but it's not necessary because the VRF * @dev oracle will mix the hash of the block containing your request into the * @dev VRF seed it ultimately uses. * * @param _keyHash ID of public key against which randomness is generated * @param _fee The amount of LINK to send with the request * * @return requestId unique ID for this request * * @dev The returned requestId can be used to distinguish responses to * @dev concurrent requests. It is passed as the first argument to * @dev fulfillRandomness. */ function requestRandomness(bytes32 _keyHash, uint256 _fee) internal returns (bytes32 requestId) { LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); // This is the seed passed to VRFCoordinator. The oracle will mix this with // the hash of the block containing this request to obtain the seed/input // which is finally passed to the VRF cryptographic machinery. uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); // nonces[_keyHash] must stay in sync with // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). // This provides protection against the user repeating their input seed, // which would result in a predictable/duplicate output, if multiple such // requests appeared in the same block. nonces[_keyHash] = nonces[_keyHash] + 1; return makeRequestId(_keyHash, vRFSeed); } LinkTokenInterface internal immutable LINK; address private immutable vrfCoordinator; // Nonces for each VRF key from which randomness has been requested. // // Must stay in sync with VRFCoordinator[_keyHash][this] mapping(bytes32 => uint256) /* keyHash */ /* nonce */ private nonces; /** * @param _vrfCoordinator address of VRFCoordinator contract * @param _link address of LINK token contract * * @dev https://docs.chain.link/docs/link-token-contracts */ constructor(address _vrfCoordinator, address _link) { vrfCoordinator = _vrfCoordinator; LINK = LinkTokenInterface(_link); } // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF // proof. rawFulfillRandomness then calls fulfillRandomness, after validating // the origin of the call function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external { require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); fulfillRandomness(requestId, randomness); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract VRFRequestIDBase { /** * @notice returns the seed which is actually input to the VRF coordinator * * @dev To prevent repetition of VRF output due to repetition of the * @dev user-supplied seed, that seed is combined in a hash with the * @dev user-specific nonce, and the address of the consuming contract. The * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in * @dev the final seed, but the nonce does protect against repetition in * @dev requests which are included in a single block. * * @param _userSeed VRF seed input provided by user * @param _requester Address of the requesting contract * @param _nonce User-specific nonce at the time of the request */ function makeVRFInputSeed( bytes32 _keyHash, uint256 _userSeed, address _requester, uint256 _nonce ) internal pure returns (uint256) { return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); } /** * @notice Returns the id for this request * @param _keyHash The serviceAgreement ID to be used for this request * @param _vRFInputSeed The seed to be passed directly to the VRF * @return The id for this request * * @dev Note that _vRFInputSeed is not the seed passed by the consuming * @dev contract, but the one generated by makeVRFInputSeed */ function makeRequestId(bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../VRFRequestIDBase.sol"; contract VRFRequestIDBaseTestHelper is VRFRequestIDBase { function makeVRFInputSeed_( bytes32 _keyHash, uint256 _userSeed, address _requester, uint256 _nonce ) public pure returns (uint256) { return makeVRFInputSeed(_keyHash, _userSeed, _requester, _nonce); } function makeRequestId_(bytes32 _keyHash, uint256 _vRFInputSeed) public pure returns (bytes32) { return makeRequestId(_keyHash, _vRFInputSeed); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../VRFConsumerBase.sol"; contract VRFConsumer is VRFConsumerBase { uint256 public randomnessOutput; bytes32 public requestId; constructor(address vrfCoordinator, address link) // solhint-disable-next-line no-empty-blocks VRFConsumerBase(vrfCoordinator, link) { /* empty */ } function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { randomnessOutput = randomness; requestId = requestId; } function testRequestRandomness(bytes32 keyHash, uint256 fee) external returns (bytes32) { return requestRandomness(keyHash, fee); } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"racAddress","type":"address"},{"internalType":"address","name":"lacAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"AddedAccess","type":"event"},{"anonymous":false,"inputs":[],"name":"CheckAccessDisabled","type":"event"},{"anonymous":false,"inputs":[],"name":"CheckAccessEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"subject","type":"address"}],"name":"FlagLowered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"subject","type":"address"}],"name":"FlagRaised","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previous","type":"address"},{"indexed":true,"internalType":"address","name":"current","type":"address"}],"name":"LoweringAccessControllerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previous","type":"address"},{"indexed":true,"internalType":"address","name":"current","type":"address"}],"name":"RaisingAccessControllerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"RemovedAccess","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"addAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disableAccessCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableAccessCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"subject","type":"address"}],"name":"getFlag","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"subjects","type":"address[]"}],"name":"getFlags","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"hasAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"subject","type":"address"}],"name":"lowerFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"subjects","type":"address[]"}],"name":"lowerFlags","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"loweringAccessController","outputs":[{"internalType":"contract AccessControllerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"subject","type":"address"}],"name":"raiseFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"subjects","type":"address[]"}],"name":"raiseFlags","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"raisingAccessController","outputs":[{"internalType":"contract AccessControllerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"removeAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lacAddress","type":"address"}],"name":"setLoweringAccessController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"racAddress","type":"address"}],"name":"setRaisingAccessController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b50604051620018ab380380620018ab833981016040819052620000349162000300565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000f2565b50506001805460ff60a01b1916600160a01b17905550620000df826200019e565b620000ea8162000212565b505062000338565b6001600160a01b0381163314156200014d5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001a862000285565b6003546001600160a01b0390811690821681146200020e57600380546001600160a01b0319166001600160a01b0384811691821790925560405190918316907fbaf9ea078655a4fffefd08f9435677bbc91e457a6d015fe7de1d0e68b8802cac90600090a35b5050565b6200021c62000285565b6004546001600160a01b0390811690821681146200020e57600480546001600160a01b0319166001600160a01b0384811691821790925560405190918316907f1c53b7601a990492bede34fbf4b374d7299e723e9568332d09154d0db2f893ff90600090a35050565b6000546001600160a01b03163314620002e15760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b80516001600160a01b0381168114620002fb57600080fd5b919050565b600080604083850312156200031457600080fd5b6200031f83620002e3565b91506200032f60208401620002e3565b90509250929050565b61156380620003486000396000f3fe608060405234801561001057600080fd5b506004361061016c5760003560e01c80638038e4a1116100cd578063b8b77f1111610081578063dc7f012411610066578063dc7f012414610331578063e37a836614610356578063f2fde38b1461036957600080fd5b8063b8b77f11146102fe578063d74af2631461031e57600080fd5b80638da5cb5b116100b25780638da5cb5b146102ba57806395f0c058146102d8578063a118f249146102eb57600080fd5b80638038e4a11461029f5780638823da6c146102a757600080fd5b8063517e89fe11610124578063760bc82d11610109578063760bc82d1461026457806379ba5097146102775780637d723cac1461027f57600080fd5b8063517e89fe1461023e5780636b14daf81461025157600080fd5b8063282865961161015557806328286596146101c35780632e1d859c146101d6578063357e47fe1461021b57600080fd5b80630a75698314610171578063181f5a771461017b575b600080fd5b61017961037c565b005b604080518082018252600b81527f466c61677320312e312e30000000000000000000000000000000000000000000602082015290516101ba9190611425565b60405180910390f35b6101796101d13660046112de565b6103fb565b6003546101f69073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101ba565b61022e6102293660046111e3565b6104c5565b60405190151581526020016101ba565b61017961024c3660046111e3565b61059e565b61022e61025f3660046111fe565b610641565b6101796102723660046112de565b610674565b61017961072e565b61029261028d3660046112de565b61082b565b6040516101ba91906113df565b6101796109bc565b6101796102b53660046111e3565b610a50565b60005473ffffffffffffffffffffffffffffffffffffffff166101f6565b6101796102e63660046111e3565b610b0a565b6101796102f93660046111e3565b610bac565b6004546101f69073ffffffffffffffffffffffffffffffffffffffff1681565b61017961032c3660046111e3565b610c60565b60015461022e9074010000000000000000000000000000000000000000900460ff1681565b6101796103643660046111e3565b610cd7565b6101796103773660046111e3565b610d4e565b610384610d5f565b60015474010000000000000000000000000000000000000000900460ff16156103f957600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f53963890600090a15b565b610403610de0565b61046e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f206c6f77657220666c61677300000000000060448201526064015b60405180910390fd5b60005b818110156104c057600083838381811061048d5761048d6114f8565b90506020020160208101906104a291906111e3565b90506104ad81610eb1565b50806104b881611498565b915050610471565b505050565b6000610508336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061064192505050565b61056e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f2061636365737300000000000000000000000000000000000000000000006044820152606401610465565b5073ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205460ff165b919050565b6105a6610d5f565b60035473ffffffffffffffffffffffffffffffffffffffff908116908216811461063d57600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560405190918316907fbaf9ea078655a4fffefd08f9435677bbc91e457a6d015fe7de1d0e68b8802cac90600090a35b5050565b600061064d8383610f53565b8061066d575073ffffffffffffffffffffffffffffffffffffffff831632145b9392505050565b61067c610fa8565b6106e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f20726169736520666c6167730000000000006044820152606401610465565b60005b818110156104c05761071c838383818110610702576107026114f8565b905060200201602081019061071791906111e3565b611025565b8061072681611498565b9150506106e5565b60015473ffffffffffffffffffffffffffffffffffffffff1633146107af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610465565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b606061086e336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061064192505050565b6108d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f2061636365737300000000000000000000000000000000000000000000006044820152606401610465565b60008267ffffffffffffffff8111156108ef576108ef611527565b604051908082528060200260200182016040528015610918578160200160208202803683370190505b50905060005b838110156109b4576005600086868481811061093c5761093c6114f8565b905060200201602081019061095191906111e3565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002054825160ff90911690839083908110610992576109926114f8565b91151560209283029190910190910152806109ac81611498565b91505061091e565b509392505050565b6109c4610d5f565b60015474010000000000000000000000000000000000000000900460ff166103f957600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c348090600090a1565b610a58610d5f565b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090205460ff1615610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b610b12610d5f565b60045473ffffffffffffffffffffffffffffffffffffffff908116908216811461063d57600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560405190918316907f1c53b7601a990492bede34fbf4b374d7299e723e9568332d09154d0db2f893ff90600090a35050565b610bb4610d5f565b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090205460ff16610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49101610afe565b610c68610fa8565b610cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f20726169736520666c6167730000000000006044820152606401610465565b610b0781611025565b610cdf610de0565b610d45576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f206c6f77657220666c6167730000000000006044820152606401610465565b610b0781610eb1565b610d56610d5f565b610b07816110c9565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610465565b6000805473ffffffffffffffffffffffffffffffffffffffff16331480610eac5750600480546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911691636b14daf891610e5c913391600091369101611375565b60206040518083038186803b158015610e7457600080fd5b505afa158015610e88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eac9190611353565b905090565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205460ff1615610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526005602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fd86728e2e5cbaa28c1d357b5fbccc9c1ab0add09950eb7cac42df9acb24c4bc89190a250565b73ffffffffffffffffffffffffffffffffffffffff821660009081526002602052604081205460ff168061066d57505060015474010000000000000000000000000000000000000000900460ff161592915050565b6000805473ffffffffffffffffffffffffffffffffffffffff16331480610eac57506003546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610e5c9033906000903690600401611375565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205460ff16610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526005602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f881febd4cd194dd4ace637642862aef1fb59a65c7e5551a5d9208f268d11c0069190a250565b73ffffffffffffffffffffffffffffffffffffffff8116331415611149576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610465565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b803573ffffffffffffffffffffffffffffffffffffffff8116811461059957600080fd5b6000602082840312156111f557600080fd5b61066d826111bf565b6000806040838503121561121157600080fd5b61121a836111bf565b9150602083013567ffffffffffffffff8082111561123757600080fd5b818501915085601f83011261124b57600080fd5b81358181111561125d5761125d611527565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156112a3576112a3611527565b816040528281528860208487010111156112bc57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b600080602083850312156112f157600080fd5b823567ffffffffffffffff8082111561130957600080fd5b818501915085601f83011261131d57600080fd5b81358181111561132c57600080fd5b8660208260051b850101111561134157600080fd5b60209290920196919550909350505050565b60006020828403121561136557600080fd5b8151801515811461066d57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301376000818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b6020808252825182820181905260009190848201906040850190845b818110156114195783511515835292840192918401916001016113fb565b50909695505050505050565b600060208083528351808285015260005b8181101561145257858101830151858201604001528201611436565b81811115611464576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156114f1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061016c5760003560e01c80638038e4a1116100cd578063b8b77f1111610081578063dc7f012411610066578063dc7f012414610331578063e37a836614610356578063f2fde38b1461036957600080fd5b8063b8b77f11146102fe578063d74af2631461031e57600080fd5b80638da5cb5b116100b25780638da5cb5b146102ba57806395f0c058146102d8578063a118f249146102eb57600080fd5b80638038e4a11461029f5780638823da6c146102a757600080fd5b8063517e89fe11610124578063760bc82d11610109578063760bc82d1461026457806379ba5097146102775780637d723cac1461027f57600080fd5b8063517e89fe1461023e5780636b14daf81461025157600080fd5b8063282865961161015557806328286596146101c35780632e1d859c146101d6578063357e47fe1461021b57600080fd5b80630a75698314610171578063181f5a771461017b575b600080fd5b61017961037c565b005b604080518082018252600b81527f466c61677320312e312e30000000000000000000000000000000000000000000602082015290516101ba9190611425565b60405180910390f35b6101796101d13660046112de565b6103fb565b6003546101f69073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101ba565b61022e6102293660046111e3565b6104c5565b60405190151581526020016101ba565b61017961024c3660046111e3565b61059e565b61022e61025f3660046111fe565b610641565b6101796102723660046112de565b610674565b61017961072e565b61029261028d3660046112de565b61082b565b6040516101ba91906113df565b6101796109bc565b6101796102b53660046111e3565b610a50565b60005473ffffffffffffffffffffffffffffffffffffffff166101f6565b6101796102e63660046111e3565b610b0a565b6101796102f93660046111e3565b610bac565b6004546101f69073ffffffffffffffffffffffffffffffffffffffff1681565b61017961032c3660046111e3565b610c60565b60015461022e9074010000000000000000000000000000000000000000900460ff1681565b6101796103643660046111e3565b610cd7565b6101796103773660046111e3565b610d4e565b610384610d5f565b60015474010000000000000000000000000000000000000000900460ff16156103f957600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f53963890600090a15b565b610403610de0565b61046e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f206c6f77657220666c61677300000000000060448201526064015b60405180910390fd5b60005b818110156104c057600083838381811061048d5761048d6114f8565b90506020020160208101906104a291906111e3565b90506104ad81610eb1565b50806104b881611498565b915050610471565b505050565b6000610508336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061064192505050565b61056e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f2061636365737300000000000000000000000000000000000000000000006044820152606401610465565b5073ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205460ff165b919050565b6105a6610d5f565b60035473ffffffffffffffffffffffffffffffffffffffff908116908216811461063d57600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560405190918316907fbaf9ea078655a4fffefd08f9435677bbc91e457a6d015fe7de1d0e68b8802cac90600090a35b5050565b600061064d8383610f53565b8061066d575073ffffffffffffffffffffffffffffffffffffffff831632145b9392505050565b61067c610fa8565b6106e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f20726169736520666c6167730000000000006044820152606401610465565b60005b818110156104c05761071c838383818110610702576107026114f8565b905060200201602081019061071791906111e3565b611025565b8061072681611498565b9150506106e5565b60015473ffffffffffffffffffffffffffffffffffffffff1633146107af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610465565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b606061086e336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061064192505050565b6108d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f2061636365737300000000000000000000000000000000000000000000006044820152606401610465565b60008267ffffffffffffffff8111156108ef576108ef611527565b604051908082528060200260200182016040528015610918578160200160208202803683370190505b50905060005b838110156109b4576005600086868481811061093c5761093c6114f8565b905060200201602081019061095191906111e3565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002054825160ff90911690839083908110610992576109926114f8565b91151560209283029190910190910152806109ac81611498565b91505061091e565b509392505050565b6109c4610d5f565b60015474010000000000000000000000000000000000000000900460ff166103f957600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c348090600090a1565b610a58610d5f565b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090205460ff1615610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b610b12610d5f565b60045473ffffffffffffffffffffffffffffffffffffffff908116908216811461063d57600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560405190918316907f1c53b7601a990492bede34fbf4b374d7299e723e9568332d09154d0db2f893ff90600090a35050565b610bb4610d5f565b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090205460ff16610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49101610afe565b610c68610fa8565b610cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f20726169736520666c6167730000000000006044820152606401610465565b610b0781611025565b610cdf610de0565b610d45576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f7420616c6c6f77656420746f206c6f77657220666c6167730000000000006044820152606401610465565b610b0781610eb1565b610d56610d5f565b610b07816110c9565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610465565b6000805473ffffffffffffffffffffffffffffffffffffffff16331480610eac5750600480546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911691636b14daf891610e5c913391600091369101611375565b60206040518083038186803b158015610e7457600080fd5b505afa158015610e88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eac9190611353565b905090565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205460ff1615610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526005602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fd86728e2e5cbaa28c1d357b5fbccc9c1ab0add09950eb7cac42df9acb24c4bc89190a250565b73ffffffffffffffffffffffffffffffffffffffff821660009081526002602052604081205460ff168061066d57505060015474010000000000000000000000000000000000000000900460ff161592915050565b6000805473ffffffffffffffffffffffffffffffffffffffff16331480610eac57506003546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610e5c9033906000903690600401611375565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205460ff16610b075773ffffffffffffffffffffffffffffffffffffffff811660008181526005602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f881febd4cd194dd4ace637642862aef1fb59a65c7e5551a5d9208f268d11c0069190a250565b73ffffffffffffffffffffffffffffffffffffffff8116331415611149576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610465565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b803573ffffffffffffffffffffffffffffffffffffffff8116811461059957600080fd5b6000602082840312156111f557600080fd5b61066d826111bf565b6000806040838503121561121157600080fd5b61121a836111bf565b9150602083013567ffffffffffffffff8082111561123757600080fd5b818501915085601f83011261124b57600080fd5b81358181111561125d5761125d611527565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156112a3576112a3611527565b816040528281528860208487010111156112bc57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b600080602083850312156112f157600080fd5b823567ffffffffffffffff8082111561130957600080fd5b818501915085601f83011261131d57600080fd5b81358181111561132c57600080fd5b8660208260051b850101111561134157600080fd5b60209290920196919550909350505050565b60006020828403121561136557600080fd5b8151801515811461066d57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301376000818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b6020808252825182820181905260009190848201906040850190845b818110156114195783511515835292840192918401916001016113fb565b50909695505050505050565b600060208083528351808285015260005b8181101561145257858101830151858201604001528201611436565b81811115611464576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156114f1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : racAddress (address): 0x0000000000000000000000000000000000000000
Arg [1] : lacAddress (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.