Contract
0x02777053d6764996e594c3E88AF1D58D5363a2e6
14
Contract Overview
Balance:
0 FTM
Token:
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 1 internal transaction
Parent Txn Hash | Block | From | To | Value | |||
---|---|---|---|---|---|---|---|
0x6260d4c786101cb1bbb4f38a86ce22fca6ba567c52de596d8a6d6bd15f5acd95 | 9289674 | 230 days 14 hrs ago | 0x9e3d8dcd4c4c67eaa752457a51efbf98cb5439c6 | Contract Creation | 0 FTM |
[ Download CSV Export ]
Contract Name:
KeeperRegistry
Compiler Version
v0.8.13+commit.abaa5c0e
Contract Source Code (Solidity Standard Json-Input format)
// 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, 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 pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../VRFConsumerBaseV2.sol"; // VRFV2RevertingExample will always revert. Used for testing only, useless in prod. contract VRFV2RevertingExample 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, uint256[] memory) internal override { revert(); } 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 topUpSubscription(uint96 amount) external { require(s_subId != 0, "sub not set"); // 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) { s_requestId = COORDINATOR.requestRandomWords(keyHash, subId, minReqConfs, callbackGasLimit, numWords); return s_requestId; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface VRFCoordinatorV2Interface { /** * @notice Get configuration relevant for making requests * @return minimumRequestConfirmations global min for request confirmations * @return maxGasLimit global max for request gas limit * @return s_provingKeyHashes list of registered key hashes */ function getRequestConfig() external view returns ( uint16, uint32, bytes32[] memory ); /** * @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 the minimum subscription balance required for the selected keyHash. * @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 * [0, 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 reqCount - number of requests for this subscription, determines fee tier. * @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, uint64 reqCount, 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 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; /* * @notice Check to see if there exists a request commitment consumers * for all consumers and keyhashes for a given sub. * @param subId - ID of the subscription * @return true if there exists at least one unfulfilled request for the subscription, false * otherwise. */ function pendingRequestExists(uint64 subId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** **************************************************************************** * @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 constructor(<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 // Example of a single consumer contract which owns the subscription. pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../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; address s_owner; constructor( address vrfCoordinator, address link, uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords, bytes32 keyHash ) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); s_owner = msg.sender; s_requestConfig = RequestConfig({ subId: 0, // Unset initially callbackGasLimit: callbackGasLimit, requestConfirmations: requestConfirmations, numWords: numWords, keyHash: keyHash }); subscribe(); } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { require(requestId == s_requestId, "request ID is incorrect"); s_randomWords = randomWords; } // Assumes the subscription is funded sufficiently. function requestRandomWords() external onlyOwner { 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 // This method is analogous to VRFv1, except the amount // should be selected based on the keyHash (each keyHash functions like a "gas lane" // with different link costs). function fundAndRequestRandomWords(uint256 amount) external onlyOwner { RequestConfig memory rc = s_requestConfig; LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_requestConfig.subId)); // 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 onlyOwner { LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_requestConfig.subId)); } function withdraw(uint256 amount, address to) external onlyOwner { LINKTOKEN.transfer(to, amount); } function unsubscribe(address to) external onlyOwner { // Returns funds to this address COORDINATOR.cancelSubscription(s_requestConfig.subId, to); s_requestConfig.subId = 0; } // Keep this separate in case the contract want to unsubscribe and then // resubscribe. function subscribe() public onlyOwner { // Create a subscription, current subId address[] memory consumers = new address[](1); consumers[0] = address(this); s_requestConfig.subId = COORDINATOR.createSubscription(); COORDINATOR.addConsumer(s_requestConfig.subId, consumers[0]); } modifier onlyOwner() { require(msg.sender == s_owner); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../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 "../VRFConsumerBaseV2.sol"; import "../ConfirmedOwner.sol"; /** * @title The VRFLoadTestExternalSubOwner contract. * @notice Allows making many VRF V2 randomness requests in a single transaction for load testing. */ contract VRFLoadTestExternalSubOwner is VRFConsumerBaseV2, ConfirmedOwner { VRFCoordinatorV2Interface public immutable COORDINATOR; LinkTokenInterface public immutable LINK; uint256 public s_responseCount; constructor(address _vrfCoordinator, address _link) VRFConsumerBaseV2(_vrfCoordinator) ConfirmedOwner(msg.sender) { COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator); LINK = LinkTokenInterface(_link); } function fulfillRandomWords(uint256, uint256[] memory) internal override { s_responseCount++; } function requestRandomWords( uint64 _subId, uint16 _requestConfirmations, bytes32 _keyHash, uint16 _requestCount ) external onlyOwner { for (uint16 i = 0; i < _requestCount; i++) { COORDINATOR.requestRandomWords(_keyHash, _subId, _requestConfirmations, 50_000, 1); } } }
// 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 "./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; abstract contract TypeAndVersionInterface { function typeAndVersion() external pure virtual returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/UpkeepTranscoderInterface.sol"; import "./interfaces/TypeAndVersionInterface.sol"; /** * @notice Transcoder for converting upkeep data from one keeper * registry version to another */ contract UpkeepTranscoder is UpkeepTranscoderInterface, TypeAndVersionInterface { error InvalidTranscoding(); /** * @notice versions: * - UpkeepTranscoder 1.0.0: placeholder to allow new formats in the future */ string public constant override typeAndVersion = "UpkeepTranscoder 1.0.0"; /** * @notice transcodeUpkeeps transforms upkeep data from the format expected by * one registry to the format expected by another. It future-proofs migrations * by allowing keepers team to customize migration paths and set sensible defaults * when new fields are added * @param fromVersion struct version the upkeep is migrating from * @param toVersion struct version the upkeep is migrating to * @param encodedUpkeeps encoded upkeep data * @dev this contract & function are simple now, but should evolve as new registries * and migration paths are added */ function transcodeUpkeeps( UpkeepFormat fromVersion, UpkeepFormat toVersion, bytes calldata encodedUpkeeps ) external view override returns (bytes memory) { if (fromVersion != UpkeepFormat.V1 || toVersion != UpkeepFormat.V1) { revert InvalidTranscoding(); } return encodedUpkeeps; } }
// SPDX-License-Identifier: MIT import "../UpkeepFormat.sol"; pragma solidity ^0.8.0; interface UpkeepTranscoderInterface { function transcodeUpkeeps( UpkeepFormat fromVersion, UpkeepFormat toVersion, bytes calldata encodedUpkeeps ) external view returns (bytes memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; enum UpkeepFormat { V1 }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./KeeperBase.sol"; import "./ConfirmedOwner.sol"; import "./interfaces/TypeAndVersionInterface.sol"; import "./interfaces/AggregatorV3Interface.sol"; import "./interfaces/LinkTokenInterface.sol"; import "./interfaces/KeeperCompatibleInterface.sol"; import "./interfaces/KeeperRegistryInterface.sol"; import "./interfaces/MigratableKeeperRegistryInterface.sol"; import "./interfaces/UpkeepTranscoderInterface.sol"; import "./interfaces/ERC677ReceiverInterface.sol"; /** * @notice Registry for adding work for Chainlink Keepers to perform on client * contracts. Clients must support the Upkeep interface. */ contract KeeperRegistry is TypeAndVersionInterface, ConfirmedOwner, KeeperBase, ReentrancyGuard, Pausable, KeeperRegistryExecutableInterface, MigratableKeeperRegistryInterface, ERC677ReceiverInterface { using Address for address; using EnumerableSet for EnumerableSet.UintSet; address private constant ZERO_ADDRESS = address(0); address private constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; bytes4 private constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; bytes4 private constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; uint256 private constant PERFORM_GAS_MIN = 2_300; uint256 private constant CANCELATION_DELAY = 50; uint256 private constant PERFORM_GAS_CUSHION = 5_000; uint256 private constant REGISTRY_GAS_OVERHEAD = 80_000; uint256 private constant PPB_BASE = 1_000_000_000; uint64 private constant UINT64_MAX = 2**64 - 1; uint96 private constant LINK_TOTAL_SUPPLY = 1e27; address[] private s_keeperList; EnumerableSet.UintSet private s_upkeepIDs; mapping(uint256 => Upkeep) private s_upkeep; mapping(address => KeeperInfo) private s_keeperInfo; mapping(address => address) private s_proposedPayee; mapping(uint256 => bytes) private s_checkData; mapping(address => MigrationPermission) private s_peerRegistryMigrationPermission; Storage private s_storage; uint256 private s_fallbackGasPrice; // not in config object for gas savings uint256 private s_fallbackLinkPrice; // not in config object for gas savings uint96 private s_ownerLinkBalance; uint256 private s_expectedLinkBalance; address private s_transcoder; address private s_registrar; LinkTokenInterface public immutable LINK; AggregatorV3Interface public immutable LINK_ETH_FEED; AggregatorV3Interface public immutable FAST_GAS_FEED; /** * @notice versions: * - KeeperRegistry 1.2.0: allow funding within performUpkeep * : allow configurable registry maxPerformGas * : add function to let admin change upkeep gas limit * : add minUpkeepSpend requirement : upgrade to solidity v0.8 * - KeeperRegistry 1.1.0: added flatFeeMicroLink * - KeeperRegistry 1.0.0: initial release */ string public constant override typeAndVersion = "KeeperRegistry 1.2.0"; error CannotCancel(); error UpkeepNotActive(); error MigrationNotPermitted(); error UpkeepNotCanceled(); error UpkeepNotNeeded(); error NotAContract(); error PaymentGreaterThanAllLINK(); error OnlyActiveKeepers(); error InsufficientFunds(); error KeepersMustTakeTurns(); error ParameterLengthError(); error OnlyCallableByOwnerOrAdmin(); error OnlyCallableByLINKToken(); error InvalidPayee(); error DuplicateEntry(); error ValueNotChanged(); error IndexOutOfRange(); error TranscoderNotSet(); error ArrayHasNoEntries(); error GasLimitOutsideRange(); error OnlyCallableByPayee(); error OnlyCallableByProposedPayee(); error GasLimitCanOnlyIncrease(); error OnlyCallableByAdmin(); error OnlyCallableByOwnerOrRegistrar(); error InvalidRecipient(); error InvalidDataLength(); error TargetCheckReverted(bytes reason); enum MigrationPermission { NONE, OUTGOING, INCOMING, BIDIRECTIONAL } /** * @notice storage of the registry, contains a mix of config and state data */ struct Storage { uint32 paymentPremiumPPB; uint32 flatFeeMicroLink; uint24 blockCountPerTurn; uint32 checkGasLimit; uint24 stalenessSeconds; uint16 gasCeilingMultiplier; uint96 minUpkeepSpend; // 1 evm word uint32 maxPerformGas; uint32 nonce; // 2 evm words } struct Upkeep { uint96 balance; address lastKeeper; // 1 storage slot full uint32 executeGas; uint64 maxValidBlocknumber; address target; // 2 storage slots full uint96 amountSpent; address admin; // 3 storage slots full } struct KeeperInfo { address payee; uint96 balance; bool active; } struct PerformParams { address from; uint256 id; bytes performData; uint256 maxLinkPayment; uint256 gasLimit; uint256 adjustedGasWei; uint256 linkEth; } event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); event UpkeepPerformed( uint256 indexed id, bool indexed success, address indexed from, uint96 payment, bytes performData ); event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); event OwnerFundsWithdrawn(uint96 amount); event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); event ConfigSet(Config config); event KeepersUpdated(address[] keepers, address[] payees); event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); /** * @param link address of the LINK Token * @param linkEthFeed address of the LINK/ETH price feed * @param fastGasFeed address of the Fast Gas price feed * @param config registry config settings */ constructor( address link, address linkEthFeed, address fastGasFeed, Config memory config ) ConfirmedOwner(msg.sender) { LINK = LinkTokenInterface(link); LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); setConfig(config); } // ACTIONS /** * @notice adds a new upkeep * @param target address to perform upkeep on * @param gasLimit amount of gas to provide the target contract when * performing upkeep * @param admin address to cancel upkeep and withdraw remaining funds * @param checkData data passed to the contract when checking for upkeep */ function registerUpkeep( address target, uint32 gasLimit, address admin, bytes calldata checkData ) external override onlyOwnerOrRegistrar returns (uint256 id) { id = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), s_storage.nonce))); _createUpkeep(id, target, gasLimit, admin, 0, checkData); s_storage.nonce++; emit UpkeepRegistered(id, gasLimit, admin); return id; } /** * @notice simulated by keepers via eth_call to see if the upkeep needs to be * performed. If upkeep is needed, the call then simulates performUpkeep * to make sure it succeeds. Finally, it returns the success status along with * payment information and the perform data payload. * @param id identifier of the upkeep to check * @param from the address to simulate performing the upkeep from */ function checkUpkeep(uint256 id, address from) external override cannotExecute returns ( bytes memory performData, uint256 maxLinkPayment, uint256 gasLimit, uint256 adjustedGasWei, uint256 linkEth ) { Upkeep memory upkeep = s_upkeep[id]; bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); (bool success, bytes memory result) = upkeep.target.call{gas: s_storage.checkGasLimit}(callData); if (!success) revert TargetCheckReverted(result); (success, performData) = abi.decode(result, (bool, bytes)); if (!success) revert UpkeepNotNeeded(); PerformParams memory params = _generatePerformParams(from, id, performData, false); _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); return (performData, params.maxLinkPayment, params.gasLimit, params.adjustedGasWei, params.linkEth); } /** * @notice executes the upkeep with the perform data returned from * checkUpkeep, validates the keeper's permissions, and pays the keeper. * @param id identifier of the upkeep to execute the data with. * @param performData calldata parameter to be passed to the target upkeep. */ function performUpkeep(uint256 id, bytes calldata performData) external override whenNotPaused returns (bool success) { return _performUpkeepWithParams(_generatePerformParams(msg.sender, id, performData, true)); } /** * @notice prevent an upkeep from being performed in the future * @param id upkeep to be canceled */ function cancelUpkeep(uint256 id) external override { uint64 maxValid = s_upkeep[id].maxValidBlocknumber; bool canceled = maxValid != UINT64_MAX; bool isOwner = msg.sender == owner(); if (canceled && !(isOwner && maxValid > block.number)) revert CannotCancel(); if (!isOwner && msg.sender != s_upkeep[id].admin) revert OnlyCallableByOwnerOrAdmin(); uint256 height = block.number; if (!isOwner) { height = height + CANCELATION_DELAY; } s_upkeep[id].maxValidBlocknumber = uint64(height); s_upkeepIDs.remove(id); emit UpkeepCanceled(id, uint64(height)); } /** * @notice adds LINK funding for an upkeep by transferring from the sender's * LINK balance * @param id upkeep to fund * @param amount number of LINK to transfer */ function addFunds(uint256 id, uint96 amount) external override onlyActiveUpkeep(id) { s_upkeep[id].balance = s_upkeep[id].balance + amount; s_expectedLinkBalance = s_expectedLinkBalance + amount; LINK.transferFrom(msg.sender, address(this), amount); emit FundsAdded(id, msg.sender, amount); } /** * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX * @param sender the account which transferred the funds * @param amount number of LINK transfer */ function onTokenTransfer( address sender, uint256 amount, bytes calldata data ) external { if (msg.sender != address(LINK)) revert OnlyCallableByLINKToken(); if (data.length != 32) revert InvalidDataLength(); uint256 id = abi.decode(data, (uint256)); if (s_upkeep[id].maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); s_expectedLinkBalance = s_expectedLinkBalance + amount; emit FundsAdded(id, sender, uint96(amount)); } /** * @notice removes funding from a canceled upkeep * @param id upkeep to withdraw funds from * @param to destination address for sending remaining funds */ function withdrawFunds(uint256 id, address to) external validRecipient(to) onlyUpkeepAdmin(id) { if (s_upkeep[id].maxValidBlocknumber > block.number) revert UpkeepNotCanceled(); uint96 minUpkeepSpend = s_storage.minUpkeepSpend; uint96 amountLeft = s_upkeep[id].balance; uint96 amountSpent = s_upkeep[id].amountSpent; uint96 cancellationFee = 0; // cancellationFee is supposed to be min(max(minUpkeepSpend - amountSpent,0), amountLeft) if (amountSpent < minUpkeepSpend) { cancellationFee = minUpkeepSpend - amountSpent; if (cancellationFee > amountLeft) { cancellationFee = amountLeft; } } uint96 amountToWithdraw = amountLeft - cancellationFee; s_upkeep[id].balance = 0; s_ownerLinkBalance = s_ownerLinkBalance + cancellationFee; s_expectedLinkBalance = s_expectedLinkBalance - amountToWithdraw; emit FundsWithdrawn(id, amountToWithdraw, to); LINK.transfer(to, amountToWithdraw); } /** * @notice withdraws LINK funds collected through cancellation fees */ function withdrawOwnerFunds() external onlyOwner { uint96 amount = s_ownerLinkBalance; s_expectedLinkBalance = s_expectedLinkBalance - amount; s_ownerLinkBalance = 0; emit OwnerFundsWithdrawn(amount); LINK.transfer(msg.sender, amount); } /** * @notice allows the admin of an upkeep to modify gas limit * @param id upkeep to be change the gas limit for * @param gasLimit new gas limit for the upkeep */ function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external override onlyActiveUpkeep(id) onlyUpkeepAdmin(id) { if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); s_upkeep[id].executeGas = gasLimit; emit UpkeepGasLimitSet(id, gasLimit); } /** * @notice recovers LINK funds improperly transferred to the registry * @dev In principle this function’s execution cost could exceed block * gas limit. However, in our anticipated deployment, the number of upkeeps and * keepers will be low enough to avoid this problem. */ function recoverFunds() external onlyOwner { uint256 total = LINK.balanceOf(address(this)); LINK.transfer(msg.sender, total - s_expectedLinkBalance); } /** * @notice withdraws a keeper's payment, callable only by the keeper's payee * @param from keeper address * @param to address to send the payment to */ function withdrawPayment(address from, address to) external validRecipient(to) { KeeperInfo memory keeper = s_keeperInfo[from]; if (keeper.payee != msg.sender) revert OnlyCallableByPayee(); s_keeperInfo[from].balance = 0; s_expectedLinkBalance = s_expectedLinkBalance - keeper.balance; emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); LINK.transfer(to, keeper.balance); } /** * @notice proposes the safe transfer of a keeper's payee to another address * @param keeper address of the keeper to transfer payee role * @param proposed address to nominate for next payeeship */ function transferPayeeship(address keeper, address proposed) external { if (s_keeperInfo[keeper].payee != msg.sender) revert OnlyCallableByPayee(); if (proposed == msg.sender) revert ValueNotChanged(); if (s_proposedPayee[keeper] != proposed) { s_proposedPayee[keeper] = proposed; emit PayeeshipTransferRequested(keeper, msg.sender, proposed); } } /** * @notice accepts the safe transfer of payee role for a keeper * @param keeper address to accept the payee role for */ function acceptPayeeship(address keeper) external { if (s_proposedPayee[keeper] != msg.sender) revert OnlyCallableByProposedPayee(); address past = s_keeperInfo[keeper].payee; s_keeperInfo[keeper].payee = msg.sender; s_proposedPayee[keeper] = ZERO_ADDRESS; emit PayeeshipTransferred(keeper, past, msg.sender); } /** * @notice signals to keepers that they should not perform upkeeps until the * contract has been unpaused */ function pause() external onlyOwner { _pause(); } /** * @notice signals to keepers that they can perform upkeeps once again after * having been paused */ function unpause() external onlyOwner { _unpause(); } // SETTERS /** * @notice updates the configuration of the registry * @param config registry config fields */ function setConfig(Config memory config) public onlyOwner { if (config.maxPerformGas < s_storage.maxPerformGas) revert GasLimitCanOnlyIncrease(); s_storage = Storage({ paymentPremiumPPB: config.paymentPremiumPPB, flatFeeMicroLink: config.flatFeeMicroLink, blockCountPerTurn: config.blockCountPerTurn, checkGasLimit: config.checkGasLimit, stalenessSeconds: config.stalenessSeconds, gasCeilingMultiplier: config.gasCeilingMultiplier, minUpkeepSpend: config.minUpkeepSpend, maxPerformGas: config.maxPerformGas, nonce: s_storage.nonce }); s_fallbackGasPrice = config.fallbackGasPrice; s_fallbackLinkPrice = config.fallbackLinkPrice; s_transcoder = config.transcoder; s_registrar = config.registrar; emit ConfigSet(config); } /** * @notice update the list of keepers allowed to perform upkeep * @param keepers list of addresses allowed to perform upkeep * @param payees addresses corresponding to keepers who are allowed to * move payments which have been accrued */ function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { if (keepers.length != payees.length || keepers.length < 2) revert ParameterLengthError(); for (uint256 i = 0; i < s_keeperList.length; i++) { address keeper = s_keeperList[i]; s_keeperInfo[keeper].active = false; } for (uint256 i = 0; i < keepers.length; i++) { address keeper = keepers[i]; KeeperInfo storage s_keeper = s_keeperInfo[keeper]; address oldPayee = s_keeper.payee; address newPayee = payees[i]; if ( (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) ) revert InvalidPayee(); if (s_keeper.active) revert DuplicateEntry(); s_keeper.active = true; if (newPayee != IGNORE_ADDRESS) { s_keeper.payee = newPayee; } } s_keeperList = keepers; emit KeepersUpdated(keepers, payees); } // GETTERS /** * @notice read all of the details about an upkeep */ function getUpkeep(uint256 id) external view override returns ( address target, uint32 executeGas, bytes memory checkData, uint96 balance, address lastKeeper, address admin, uint64 maxValidBlocknumber, uint96 amountSpent ) { Upkeep memory reg = s_upkeep[id]; return ( reg.target, reg.executeGas, s_checkData[id], reg.balance, reg.lastKeeper, reg.admin, reg.maxValidBlocknumber, reg.amountSpent ); } /** * @notice retrieve active upkeep IDs * @param startIndex starting index in list * @param maxCount max count to retrieve (0 = unlimited) * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one * should consider keeping the blockheight constant to ensure a wholistic picture of the contract state */ function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view override returns (uint256[] memory) { uint256 maxIdx = s_upkeepIDs.length(); if (startIndex >= maxIdx) revert IndexOutOfRange(); if (maxCount == 0) { maxCount = maxIdx - startIndex; } uint256[] memory ids = new uint256[](maxCount); for (uint256 idx = 0; idx < maxCount; idx++) { ids[idx] = s_upkeepIDs.at(startIndex + idx); } return ids; } /** * @notice read the current info about any keeper address */ function getKeeperInfo(address query) external view override returns ( address payee, bool active, uint96 balance ) { KeeperInfo memory keeper = s_keeperInfo[query]; return (keeper.payee, keeper.active, keeper.balance); } /** * @notice read the current state of the registry */ function getState() external view override returns ( State memory state, Config memory config, address[] memory keepers ) { Storage memory store = s_storage; state.nonce = store.nonce; state.ownerLinkBalance = s_ownerLinkBalance; state.expectedLinkBalance = s_expectedLinkBalance; state.numUpkeeps = s_upkeepIDs.length(); config.paymentPremiumPPB = store.paymentPremiumPPB; config.flatFeeMicroLink = store.flatFeeMicroLink; config.blockCountPerTurn = store.blockCountPerTurn; config.checkGasLimit = store.checkGasLimit; config.stalenessSeconds = store.stalenessSeconds; config.gasCeilingMultiplier = store.gasCeilingMultiplier; config.minUpkeepSpend = store.minUpkeepSpend; config.maxPerformGas = store.maxPerformGas; config.fallbackGasPrice = s_fallbackGasPrice; config.fallbackLinkPrice = s_fallbackLinkPrice; config.transcoder = s_transcoder; config.registrar = s_registrar; return (state, config, s_keeperList); } /** * @notice calculates the minimum balance required for an upkeep to remain eligible * @param id the upkeep id to calculate minimum balance for */ function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { return getMaxPaymentForGas(s_upkeep[id].executeGas); } /** * @notice calculates the maximum payment for a given gas limit * @param gasLimit the gas to calculate payment for */ function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { (uint256 gasWei, uint256 linkEth) = _getFeedData(); uint256 adjustedGasWei = _adjustGasPrice(gasWei, false); return _calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); } /** * @notice retrieves the migration permission for a peer registry */ function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { return s_peerRegistryMigrationPermission[peer]; } /** * @notice sets the peer registry migration permission */ function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { s_peerRegistryMigrationPermission[peer] = permission; } /** * @inheritdoc MigratableKeeperRegistryInterface */ function migrateUpkeeps(uint256[] calldata ids, address destination) external override { if ( s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL ) revert MigrationNotPermitted(); if (s_transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); if (ids.length == 0) revert ArrayHasNoEntries(); uint256 id; Upkeep memory upkeep; uint256 totalBalanceRemaining; bytes[] memory checkDatas = new bytes[](ids.length); Upkeep[] memory upkeeps = new Upkeep[](ids.length); for (uint256 idx = 0; idx < ids.length; idx++) { id = ids[idx]; upkeep = s_upkeep[id]; if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); if (upkeep.maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); upkeeps[idx] = upkeep; checkDatas[idx] = s_checkData[id]; totalBalanceRemaining = totalBalanceRemaining + upkeep.balance; delete s_upkeep[id]; delete s_checkData[id]; s_upkeepIDs.remove(id); emit UpkeepMigrated(id, upkeep.balance, destination); } s_expectedLinkBalance = s_expectedLinkBalance - totalBalanceRemaining; bytes memory encodedUpkeeps = abi.encode(ids, upkeeps, checkDatas); MigratableKeeperRegistryInterface(destination).receiveUpkeeps( UpkeepTranscoderInterface(s_transcoder).transcodeUpkeeps( UpkeepFormat.V1, MigratableKeeperRegistryInterface(destination).upkeepTranscoderVersion(), encodedUpkeeps ) ); LINK.transfer(destination, totalBalanceRemaining); } /** * @inheritdoc MigratableKeeperRegistryInterface */ UpkeepFormat public constant upkeepTranscoderVersion = UpkeepFormat.V1; /** * @inheritdoc MigratableKeeperRegistryInterface */ function receiveUpkeeps(bytes calldata encodedUpkeeps) external override { if ( s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL ) revert MigrationNotPermitted(); (uint256[] memory ids, Upkeep[] memory upkeeps, bytes[] memory checkDatas) = abi.decode( encodedUpkeeps, (uint256[], Upkeep[], bytes[]) ); for (uint256 idx = 0; idx < ids.length; idx++) { _createUpkeep( ids[idx], upkeeps[idx].target, upkeeps[idx].executeGas, upkeeps[idx].admin, upkeeps[idx].balance, checkDatas[idx] ); emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); } } /** * @notice creates a new upkeep with the given fields * @param target address to perform upkeep on * @param gasLimit amount of gas to provide the target contract when * performing upkeep * @param admin address to cancel upkeep and withdraw remaining funds * @param checkData data passed to the contract when checking for upkeep */ function _createUpkeep( uint256 id, address target, uint32 gasLimit, address admin, uint96 balance, bytes memory checkData ) internal whenNotPaused { if (!target.isContract()) revert NotAContract(); if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); s_upkeep[id] = Upkeep({ target: target, executeGas: gasLimit, balance: balance, admin: admin, maxValidBlocknumber: UINT64_MAX, lastKeeper: ZERO_ADDRESS, amountSpent: 0 }); s_expectedLinkBalance = s_expectedLinkBalance + balance; s_checkData[id] = checkData; s_upkeepIDs.add(id); } /** * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed * data is stale it uses the configured fallback price. Once a price is picked * for gas it takes the min of gas price in the transaction or the fast gas * price in order to reduce costs for the upkeep clients. */ function _getFeedData() private view returns (uint256 gasWei, uint256 linkEth) { uint32 stalenessSeconds = s_storage.stalenessSeconds; bool staleFallback = stalenessSeconds > 0; uint256 timestamp; int256 feedValue; (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { gasWei = s_fallbackGasPrice; } else { gasWei = uint256(feedValue); } (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { linkEth = s_fallbackLinkPrice; } else { linkEth = uint256(feedValue); } return (gasWei, linkEth); } /** * @dev calculates LINK paid for gas spent plus a configure premium percentage */ function _calculatePaymentAmount( uint256 gasLimit, uint256 gasWei, uint256 linkEth ) private view returns (uint96 payment) { uint256 weiForGas = gasWei * (gasLimit + REGISTRY_GAS_OVERHEAD); uint256 premium = PPB_BASE + s_storage.paymentPremiumPPB; uint256 total = ((weiForGas * (1e9) * (premium)) / (linkEth)) + (uint256(s_storage.flatFeeMicroLink) * (1e12)); if (total > LINK_TOTAL_SUPPLY) revert PaymentGreaterThanAllLINK(); return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX } /** * @dev calls target address with exactly gasAmount gas and data as calldata * or reverts if at least gasAmount gas is not available */ function _callWithExactGas( uint256 gasAmount, address target, bytes memory data ) private returns (bool success) { assembly { let g := gas() // Compute g -= PERFORM_GAS_CUSHION and check for underflow if lt(g, PERFORM_GAS_CUSHION) { revert(0, 0) } g := sub(g, PERFORM_GAS_CUSHION) // 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; } /** * @dev calls the Upkeep target with the performData param passed in by the * keeper and the exact gas required by the Upkeep */ function _performUpkeepWithParams(PerformParams memory params) private nonReentrant validUpkeep(params.id) returns (bool success) { Upkeep memory upkeep = s_upkeep[params.id]; _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); uint256 gasUsed = gasleft(); bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); success = _callWithExactGas(params.gasLimit, upkeep.target, callData); gasUsed = gasUsed - gasleft(); uint96 payment = _calculatePaymentAmount(gasUsed, params.adjustedGasWei, params.linkEth); s_upkeep[params.id].balance = s_upkeep[params.id].balance - payment; s_upkeep[params.id].amountSpent = s_upkeep[params.id].amountSpent + payment; s_upkeep[params.id].lastKeeper = params.from; s_keeperInfo[params.from].balance = s_keeperInfo[params.from].balance + payment; emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); return success; } /** * @dev ensures all required checks are passed before an upkeep is performed */ function _prePerformUpkeep( Upkeep memory upkeep, address from, uint256 maxLinkPayment ) private view { if (!s_keeperInfo[from].active) revert OnlyActiveKeepers(); if (upkeep.balance < maxLinkPayment) revert InsufficientFunds(); if (upkeep.lastKeeper == from) revert KeepersMustTakeTurns(); } /** * @dev adjusts the gas price to min(ceiling, tx.gasprice) or just uses the ceiling if tx.gasprice is disabled */ function _adjustGasPrice(uint256 gasWei, bool useTxGasPrice) private view returns (uint256 adjustedPrice) { adjustedPrice = gasWei * s_storage.gasCeilingMultiplier; if (useTxGasPrice && tx.gasprice < adjustedPrice) { adjustedPrice = tx.gasprice; } } /** * @dev generates a PerformParams struct for use in _performUpkeepWithParams() */ function _generatePerformParams( address from, uint256 id, bytes memory performData, bool useTxGasPrice ) private view returns (PerformParams memory) { uint256 gasLimit = s_upkeep[id].executeGas; (uint256 gasWei, uint256 linkEth) = _getFeedData(); uint256 adjustedGasWei = _adjustGasPrice(gasWei, useTxGasPrice); uint96 maxLinkPayment = _calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); return PerformParams({ from: from, id: id, performData: performData, maxLinkPayment: maxLinkPayment, gasLimit: gasLimit, adjustedGasWei: adjustedGasWei, linkEth: linkEth }); } // MODIFIERS /** * @dev ensures a upkeep is valid */ modifier validUpkeep(uint256 id) { if (s_upkeep[id].maxValidBlocknumber <= block.number) revert UpkeepNotActive(); _; } /** * @dev Reverts if called by anyone other than the admin of upkeep #id */ modifier onlyUpkeepAdmin(uint256 id) { if (msg.sender != s_upkeep[id].admin) revert OnlyCallableByAdmin(); _; } /** * @dev Reverts if called on a cancelled upkeep */ modifier onlyActiveUpkeep(uint256 id) { if (s_upkeep[id].maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); _; } /** * @dev ensures that burns don't accidentally happen by sending to the zero * address */ modifier validRecipient(address to) { if (to == ZERO_ADDRESS) revert InvalidRecipient(); _; } /** * @dev Reverts if called by anyone other than the contract owner or registrar. */ modifier onlyOwnerOrRegistrar() { if (msg.sender != owner() && msg.sender != s_registrar) revert OnlyCallableByOwnerOrRegistrar(); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol) pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { return _values(set._inner); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract KeeperBase { error OnlySimulatedBackend(); /** * @notice method that allows it to be simulated via eth_call by checking that * the sender is the zero address. */ function preventExecution() internal view { if (tx.origin != address(0)) { revert OnlySimulatedBackend(); } } /** * @notice modifier that allows it to be simulated via eth_call by checking * that the sender is the zero address. */ modifier cannotExecute() { preventExecution(); _; } }
// 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; interface KeeperCompatibleInterface { /** * @notice method that is simulated by the keepers to see if any work actually * needs to be performed. This method does does not actually need to be * executable, and since it is only ever simulated it can consume lots of gas. * @dev To ensure that it is never called, you may want to add the * cannotExecute modifier from KeeperBase to your implementation of this * method. * @param checkData specified in the upkeep registration so it is always the * same for a registered upkeep. This can easily be broken down into specific * arguments using `abi.decode`, so multiple upkeeps can be registered on the * same contract and easily differentiated by the contract. * @return upkeepNeeded boolean to indicate whether the keeper should call * performUpkeep or not. * @return performData bytes that the keeper should call performUpkeep with, if * upkeep is needed. If you would like to encode data to decode later, try * `abi.encode`. */ function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData); /** * @notice method that is actually executed by the keepers, via the registry. * The data returned by the checkUpkeep simulation will be passed into * this method to actually be executed. * @dev The input to this method should not be trusted, and the caller of the * method should not even be restricted to any single registry. Anyone should * be able call it, and the input should be validated, there is no guarantee * that the data passed in is the performData returned from checkUpkeep. This * could happen due to malicious keepers, racing keepers, or simply a state * change while the performUpkeep transaction is waiting for confirmation. * Always validate the data passed in. * @param performData is the data which was passed back from the checkData * simulation. If it is encoded, it can easily be decoded into other types by * calling `abi.decode`. This data should not be trusted, and should be * validated against the contract's current state. */ function performUpkeep(bytes calldata performData) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @notice config of the registry * @dev only used in params and return values * @member paymentPremiumPPB payment premium rate oracles receive on top of * being reimbursed for gas, measured in parts per billion * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, * priced in MicroLink; can be used in conjunction with or independently of * paymentPremiumPPB * @member blockCountPerTurn number of blocks each oracle has during their turn to * perform upkeep before it will be the next keeper's turn to submit * @member checkGasLimit gas limit when checking for upkeep * @member stalenessSeconds number of seconds that is allowed for feed data to * be stale before switching to the fallback pricing * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price * when calculating the payment ceiling for keepers * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling * @member maxPerformGas max executeGas allowed for an upkeep on this registry * @member fallbackGasPrice gas price used if the gas price feed is stale * @member fallbackLinkPrice LINK price used if the LINK price feed is stale * @member transcoder address of the transcoder contract * @member registrar address of the registrar contract */ struct Config { uint32 paymentPremiumPPB; uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK uint24 blockCountPerTurn; uint32 checkGasLimit; uint24 stalenessSeconds; uint16 gasCeilingMultiplier; uint96 minUpkeepSpend; uint32 maxPerformGas; uint256 fallbackGasPrice; uint256 fallbackLinkPrice; address transcoder; address registrar; } /** * @notice config of the registry * @dev only used in params and return values * @member nonce used for ID generation * @ownerLinkBalance withdrawable balance of LINK by contract owner * @numUpkeeps total number of upkeeps on the registry */ struct State { uint32 nonce; uint96 ownerLinkBalance; uint256 expectedLinkBalance; uint256 numUpkeeps; } interface KeeperRegistryBaseInterface { function registerUpkeep( address target, uint32 gasLimit, address admin, bytes calldata checkData ) external returns (uint256 id); function performUpkeep(uint256 id, bytes calldata performData) external returns (bool success); function cancelUpkeep(uint256 id) external; function addFunds(uint256 id, uint96 amount) external; function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; function getUpkeep(uint256 id) external view returns ( address target, uint32 executeGas, bytes memory checkData, uint96 balance, address lastKeeper, address admin, uint64 maxValidBlocknumber, uint96 amountSpent ); function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); function getKeeperInfo(address query) external view returns ( address payee, bool active, uint96 balance ); function getState() external view returns ( State memory, Config memory, address[] memory ); } /** * @dev The view methods are not actually marked as view in the implementation * but we want them to be easily queried off-chain. Solidity will not compile * if we actually inherit from this interface, so we document it here. */ interface KeeperRegistryInterface is KeeperRegistryBaseInterface { function checkUpkeep(uint256 upkeepId, address from) external view returns ( bytes memory performData, uint256 maxLinkPayment, uint256 gasLimit, int256 gasWei, int256 linkEth ); } interface KeeperRegistryExecutableInterface is KeeperRegistryBaseInterface { function checkUpkeep(uint256 upkeepId, address from) external returns ( bytes memory performData, uint256 maxLinkPayment, uint256 gasLimit, uint256 adjustedGasWei, uint256 linkEth ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../UpkeepFormat.sol"; interface MigratableKeeperRegistryInterface { /** * @notice Migrates upkeeps from one registry to another, including LINK and upkeep params. * Only callable by the upkeep admin. All upkeeps must have the same admin. Can only migrate active upkeeps. * @param upkeepIDs ids of upkeeps to migrate * @param destination the address of the registry to migrate to */ function migrateUpkeeps(uint256[] calldata upkeepIDs, address destination) external; /** * @notice Called by other registries when migrating upkeeps. Only callable by other registries. * @param encodedUpkeeps abi encoding of upkeeps to import - decoded by the transcoder */ function receiveUpkeeps(bytes calldata encodedUpkeeps) external; /** * @notice Specifies the version of upkeep data that this registry requires in order to import */ function upkeepTranscoderVersion() external returns (UpkeepFormat version); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; interface ERC677ReceiverInterface { function onTokenTransfer( address sender, uint256 amount, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // An example VRF V1 consumer contract that can be triggered using a transferAndCall from the link // contract. pragma solidity ^0.8.4; import "../VRFConsumerBase.sol"; import "../interfaces/ERC677ReceiverInterface.sol"; contract VRFOwnerlessConsumerExample is VRFConsumerBase, ERC677ReceiverInterface { uint256 public s_randomnessOutput; bytes32 public s_requestId; error OnlyCallableFromLink(); constructor(address _vrfCoordinator, address _link) VRFConsumerBase(_vrfCoordinator, _link) { /* empty */ } function fulfillRandomness(bytes32 requestId, uint256 _randomness) internal override { require(requestId == s_requestId, "request ID is incorrect"); s_randomnessOutput = _randomness; } /** * @dev Creates a new randomness request. This function can only be used by calling * transferAndCall on the LinkToken contract. * @param _amount The amount of LINK transferred to pay for this request. * @param _data The data passed to transferAndCall on LinkToken. Must be an abi-encoded key hash. */ function onTokenTransfer( address, /* sender */ uint256 _amount, bytes calldata _data ) external override { if (msg.sender != address(LINK)) { revert OnlyCallableFromLink(); } bytes32 keyHash = abi.decode(_data, (bytes32)); s_requestId = requestRandomness(keyHash, _amount); } }
// 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 constructor(<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 "../VRFConsumerBase.sol"; import "../interfaces/ERC677ReceiverInterface.sol"; /** * @title The VRFLoadTestOwnerlessConsumer contract. * @notice Allows making many VRF V1 randomness requests in a single transaction for load testing. */ contract VRFLoadTestOwnerlessConsumer is VRFConsumerBase, ERC677ReceiverInterface { // The price of each VRF request in Juels. 1 LINK = 1e18 Juels. uint256 public immutable PRICE; uint256 public s_responseCount; constructor( address _vrfCoordinator, address _link, uint256 _price ) VRFConsumerBase(_vrfCoordinator, _link) { PRICE = _price; } function fulfillRandomness(bytes32, uint256) internal override { s_responseCount++; } /** * @dev Creates as many randomness requests as can be made with the funds transferred. * @param _amount The amount of LINK transferred to pay for these requests. * @param _data The data passed to transferAndCall on LinkToken. Must be an abi-encoded key hash. */ function onTokenTransfer( address, uint256 _amount, bytes calldata _data ) external override { if (msg.sender != address(LINK)) { revert("only callable from LINK"); } bytes32 keyHash = abi.decode(_data, (bytes32)); uint256 spent = 0; while (spent + PRICE <= _amount) { requestRandomness(keyHash, PRICE); spent += PRICE; } } }
// 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); } }
// 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 VRFCoordinatorMock { LinkTokenInterface public LINK; event RandomnessRequest(address indexed sender, bytes32 indexed keyHash, uint256 indexed seed); constructor(address linkAddress) public { LINK = LinkTokenInterface(linkAddress); } function onTokenTransfer( address sender, uint256 fee, bytes memory _data ) public onlyLINK { (bytes32 keyHash, uint256 seed) = abi.decode(_data, (bytes32, uint256)); emit RandomnessRequest(sender, keyHash, seed); } function callBackWithRandomness( bytes32 requestId, uint256 randomness, address consumerContract ) public { VRFConsumerBase v; bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomness.selector, requestId, randomness); uint256 b = 206000; require(gasleft() >= b, "not enough gas for consumer"); (bool success, ) = consumerContract.call(resp); } modifier onlyLINK() { require(msg.sender == address(LINK), "Must use LINK token"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "./interfaces/LinkTokenInterface.sol"; import "./interfaces/BlockhashStoreInterface.sol"; import "./interfaces/AggregatorV3Interface.sol"; import "./interfaces/VRFCoordinatorV2Interface.sol"; import "./interfaces/TypeAndVersionInterface.sol"; import "./interfaces/ERC677ReceiverInterface.sol"; import "./VRF.sol"; import "./ConfirmedOwner.sol"; import "./VRFConsumerBaseV2.sol"; contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface, VRFCoordinatorV2Interface, ERC677ReceiverInterface { 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 PendingRequestExists(); error MustBeRequestedOwner(address proposedOwner); error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen event FundsRecovered(address to, uint256 amount); // We use the subscription struct (1 word) // at fulfillment time. 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. uint64 reqCount; // For fee tiers } // We use the config for the mgmt APIs struct SubscriptionConfig { 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; } // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subId */ /* nonce */ private s_consumers; mapping(uint64 => SubscriptionConfig) /* subId */ /* subscriptionConfig */ private s_subscriptionConfigs; mapping(uint64 => Subscription) /* subId */ /* subscription */ private s_subscriptions; // We make the sub count public so that its possible to // get all the current subscriptions via getSubscription. uint64 private s_currentSubId; // s_totalBalance tracks the total link sent to/from // this contract through onTokenTransfer, 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 private 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 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; // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) // and some arithmetic operations. uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 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; bytes32[] private s_provingKeyHashes; 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 indexed subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords, address indexed sender ); event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); struct Config { uint16 minimumRequestConfirmations; uint32 maxGasLimit; // Reentrancy protection. bool reentrancyLock; // 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; } int256 private s_fallbackWeiPerUnitLink; Config private s_config; FeeConfig private s_feeConfig; struct FeeConfig { // Flat fee charged per fulfillment in millionths of link // So fee range is [0, 2^32/10^6]. uint32 fulfillmentFlatFeeLinkPPMTier1; uint32 fulfillmentFlatFeeLinkPPMTier2; uint32 fulfillmentFlatFeeLinkPPMTier3; uint32 fulfillmentFlatFeeLinkPPMTier4; uint32 fulfillmentFlatFeeLinkPPMTier5; uint24 reqsForTier2; uint24 reqsForTier3; uint24 reqsForTier4; uint24 reqsForTier5; } event ConfigSet( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, int256 fallbackWeiPerUnitLink, FeeConfig feeConfig ); 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; s_provingKeyHashes.push(kh); 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]; for (uint256 i = 0; i < s_provingKeyHashes.length; i++) { if (s_provingKeyHashes[i] == kh) { bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1]; // Copy last element and overwrite kh to be deleted with it s_provingKeyHashes[i] = last; s_provingKeyHashes.pop(); } } emit ProvingKeyDeregistered(kh, oracle); } /** * @notice Returns the proving key hash key associated with this public key * @param publicKey the key to return the hash of */ function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { return keccak256(abi.encode(publicKey)); } /** * @notice Sets the configuration of the vrfv2 coordinator * @param minimumRequestConfirmations global min for request confirmations * @param maxGasLimit global max for request gas limit * @param stalenessSeconds if the eth/link feed is more stale then this, use the fallback price * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement * @param fallbackWeiPerUnitLink fallback eth/link price in the case of a stale feed * @param feeConfig fee tier configuration */ function setConfig( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, int256 fallbackWeiPerUnitLink, FeeConfig memory feeConfig ) 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, maxGasLimit: maxGasLimit, stalenessSeconds: stalenessSeconds, gasAfterPaymentCalculation: gasAfterPaymentCalculation, reentrancyLock: false }); s_feeConfig = feeConfig; s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; emit ConfigSet( minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, s_feeConfig ); } function getConfig() external view returns ( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation ) { return ( s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_config.stalenessSeconds, s_config.gasAfterPaymentCalculation ); } function getFeeConfig() external view returns ( uint32 fulfillmentFlatFeeLinkPPMTier1, uint32 fulfillmentFlatFeeLinkPPMTier2, uint32 fulfillmentFlatFeeLinkPPMTier3, uint32 fulfillmentFlatFeeLinkPPMTier4, uint32 fulfillmentFlatFeeLinkPPMTier5, uint24 reqsForTier2, uint24 reqsForTier3, uint24 reqsForTier4, uint24 reqsForTier5 ) { return ( s_feeConfig.fulfillmentFlatFeeLinkPPMTier1, s_feeConfig.fulfillmentFlatFeeLinkPPMTier2, s_feeConfig.fulfillmentFlatFeeLinkPPMTier3, s_feeConfig.fulfillmentFlatFeeLinkPPMTier4, s_feeConfig.fulfillmentFlatFeeLinkPPMTier5, s_feeConfig.reqsForTier2, s_feeConfig.reqsForTier3, s_feeConfig.reqsForTier4, s_feeConfig.reqsForTier5 ); } function getTotalBalance() external view returns (uint256) { return s_totalBalance; } function getFallbackWeiPerUnitLink() external view returns (int256) { return s_fallbackWeiPerUnitLink; } /** * @notice Owner cancel subscription, sends remaining link directly to the subscription owner. * @param subId subscription id * @dev notably can be called even if there are pending requests, outstanding ones may fail onchain */ function ownerCancelSubscription(uint64 subId) external onlyOwner { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner); } /** * @notice Recover link sent with transfer instead of transferAndCall. * @param to address to send link to */ 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. } /** * @inheritdoc VRFCoordinatorV2Interface */ function getRequestConfig() external view override returns ( uint16, uint32, bytes32[] memory ) { return (s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_provingKeyHashes); } /** * @inheritdoc VRFCoordinatorV2Interface */ function requestRandomWords( bytes32 keyHash, uint64 subId, uint16 requestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external override nonReentrant returns (uint256) { // Input validation using the subscription storage. if (s_subscriptionConfigs[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. // A nonce of 0 indicates consumer is not allocated to the sub. uint64 currentNonce = s_consumers[msg.sender][subId]; if (currentNonce == 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 ); } // No lower bound on the requested gas limit. A user could request 0 // and they would simply be billed for the proof verification and wouldn't be // able to do anything with the random value. 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 = currentNonce + 1; (uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, subId, nonce); 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; return requestId; } /** * @notice Get request commitment * @param requestId id of request * @dev used to determine if a request is fulfilled or not */ function getCommitment(uint256 requestId) external view returns (bytes32) { return s_requestCommitments[requestId]; } function computeRequestId( bytes32 keyHash, address sender, uint64 subId, uint64 nonce ) private pure returns (uint256, uint256) { uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); } /** * @dev calls target address with exactly gasAmount gas and data as calldata * or reverts if at least gasAmount gas is not available. */ 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 -= GAS_FOR_CALL_EXACT_CHECK and check for underflow // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). // We want to ensure that we revert if gasAmount > 63//64*gas available // as we do not want to provide them with less, however that check itself costs // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able // to revert if gasAmount > 63//64*gas available. if lt(g, GAS_FOR_CALL_EXACT_CHECK) { revert(0, 0) } g := sub(g, GAS_FOR_CALL_EXACT_CHECK) // 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 // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) 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 } /* * @notice Compute fee based on the request count * @param reqCount number of requests * @return feePPM fee in LINK PPM */ function getFeeTier(uint64 reqCount) public view returns (uint32) { FeeConfig memory fc = s_feeConfig; if (0 <= reqCount && reqCount <= fc.reqsForTier2) { return fc.fulfillmentFlatFeeLinkPPMTier1; } if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { return fc.fulfillmentFlatFeeLinkPPMTier2; } if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { return fc.fulfillmentFlatFeeLinkPPMTier3; } if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { return fc.fulfillmentFlatFeeLinkPPMTier4; } return fc.fulfillmentFlatFeeLinkPPMTier5; } /* * @notice Fulfill a randomness request * @param proof contains the proof and randomness * @param rc request commitment pre-image, committed to at request time * @return payment amount billed to the subscription * @dev simulated offchain to determine if sufficient balance is present to fulfill the request */ function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint96) { 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, requestId, randomWords); // 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. // Note that callWithExactGas will revert if we do not have sufficient gas // to give the callee their requested amount. s_config.reentrancyLock = true; bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp); s_config.reentrancyLock = false; // Increment the req count for fee tier selection. uint64 reqCount = s_subscriptions[rc.subId].reqCount; s_subscriptions[rc.subId].reqCount += 1; // 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, getFeeTier(reqCount), tx.gasprice ); if (s_subscriptions[rc.subId].balance < payment) { revert InsufficientBalance(); } s_subscriptions[rc.subId].balance -= payment; s_withdrawableTokens[s_provingKeys[keyHash]] += payment; // Include payment in the event for tracking costs. emit RandomWordsFulfilled(requestId, randomness, payment, success); return 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; } /* * @notice Oracle withdraw LINK earned through fulfilling requests * @param recipient where to send the funds * @param amount amount to withdraw */ 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 override nonReentrant { if (msg.sender != address(LINK)) { revert OnlyCallableFromLink(); } if (data.length != 32) { revert InvalidCalldata(); } uint64 subId = abi.decode(data, (uint64)); if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } // We do not check that the msg.sender is the subscription owner, // anyone can fund a subscription. uint256 oldBalance = s_subscriptions[subId].balance; s_subscriptions[subId].balance += uint96(amount); s_totalBalance += uint96(amount); emit SubscriptionFunded(subId, oldBalance, oldBalance + amount); } function getCurrentSubId() external view returns (uint64) { return s_currentSubId; } /** * @inheritdoc VRFCoordinatorV2Interface */ function getSubscription(uint64 subId) external view override returns ( uint96 balance, uint64 reqCount, address owner, address[] memory consumers ) { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } return ( s_subscriptions[subId].balance, s_subscriptions[subId].reqCount, s_subscriptionConfigs[subId].owner, s_subscriptionConfigs[subId].consumers ); } /** * @inheritdoc VRFCoordinatorV2Interface */ function createSubscription() external override nonReentrant returns (uint64) { s_currentSubId++; uint64 currentSubId = s_currentSubId; address[] memory consumers = new address[](0); s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0}); s_subscriptionConfigs[currentSubId] = SubscriptionConfig({ owner: msg.sender, requestedOwner: address(0), consumers: consumers }); emit SubscriptionCreated(currentSubId, msg.sender); return currentSubId; } /** * @inheritdoc VRFCoordinatorV2Interface */ function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external override onlySubOwner(subId) nonReentrant { // Proposing to address(0) would never be claimable so don't need to check. if (s_subscriptionConfigs[subId].requestedOwner != newOwner) { s_subscriptionConfigs[subId].requestedOwner = newOwner; emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); } } /** * @inheritdoc VRFCoordinatorV2Interface */ function acceptSubscriptionOwnerTransfer(uint64 subId) external override nonReentrant { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) { revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner); } address oldOwner = s_subscriptionConfigs[subId].owner; s_subscriptionConfigs[subId].owner = msg.sender; s_subscriptionConfigs[subId].requestedOwner = address(0); emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); } /** * @inheritdoc VRFCoordinatorV2Interface */ function removeConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { if (s_consumers[consumer][subId] == 0) { revert InvalidConsumer(subId, consumer); } // Note bounded by MAX_CONSUMERS address[] memory consumers = s_subscriptionConfigs[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_subscriptionConfigs[subId].consumers[i] = last; // Storage remove last element s_subscriptionConfigs[subId].consumers.pop(); break; } } delete s_consumers[consumer][subId]; emit SubscriptionConsumerRemoved(subId, consumer); } /** * @inheritdoc VRFCoordinatorV2Interface */ function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { // Already maxed, cannot add any more consumers. if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) { revert TooManyConsumers(); } if (s_consumers[consumer][subId] != 0) { // Idempotence - do nothing if already added. // Ensures uniqueness in s_subscriptions[subId].consumers. return; } // Initialize the nonce to 1, indicating the consumer is allocated. s_consumers[consumer][subId] = 1; s_subscriptionConfigs[subId].consumers.push(consumer); emit SubscriptionConsumerAdded(subId, consumer); } /** * @inheritdoc VRFCoordinatorV2Interface */ function cancelSubscription(uint64 subId, address to) external override onlySubOwner(subId) nonReentrant { if (pendingRequestExists(subId)) { revert PendingRequestExists(); } cancelSubscriptionHelper(subId, to); } function cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant { SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; 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 < subConfig.consumers.length; i++) { delete s_consumers[subConfig.consumers[i]][subId]; } delete s_subscriptionConfigs[subId]; delete s_subscriptions[subId]; s_totalBalance -= balance; if (!LINK.transfer(to, uint256(balance))) { revert InsufficientBalance(); } emit SubscriptionCanceled(subId, to, balance); } /** * @inheritdoc VRFCoordinatorV2Interface * @dev Looping is bounded to MAX_CONSUMERS*(number of keyhashes). * @dev Used to disable subscription canceling while outstanding request are present. */ function pendingRequestExists(uint64 subId) public view override returns (bool) { SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; for (uint256 i = 0; i < subConfig.consumers.length; i++) { for (uint256 j = 0; j < s_provingKeyHashes.length; j++) { (uint256 reqId, ) = computeRequestId( s_provingKeyHashes[j], subConfig.consumers[i], subId, s_consumers[subConfig.consumers[i]][subId] ); if (s_requestCommitments[reqId] != 0) { return true; } } } return false; } modifier onlySubOwner(uint64 subId) { address owner = s_subscriptionConfigs[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; /** **************************************************************************** * @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 uniqueness, full * @dev collision resistance, and full pseudo-randomness 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 construction 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 "../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.6; import "../interfaces/AggregatorV3Interface.sol"; /** * Network: Fantom Testnet * Base: LINK/USD * Base Address: 0x6d5689Ad4C1806D1BA0c70Ab95ebe0Da6B204fC5 * Quote: FTM/USD * Quote Address: 0xe04676B9A9A2973BCb0D1478b5E1E9098BBB7f3D * Decimals: 18 * * Network: AVAX Testnet * Base: LINK/USD * Base Address: 0x34C4c526902d88a3Aa98DB8a9b802603EB1E3470 * Quote: AVAX/USD * Quote Address: 0x5498BB86BC934c8D34FDA08E81D444153d0D06aD * Decimals: 18 * * Chainlink Data Feeds can be used in combination to derive denominated price pairs in other * currencies. * * If you require a denomination other than what is provided, you can use two data feeds to derive * the pair that you need. * * For example, if you needed a LINK / FTM price, you could take the LINK / USD feed and the * FTM / USD feed and derive LINK / FTM using division. * (LINK/USD)/(FTM/USD) = LINK/FTM */ contract DerivedPriceFeed is AggregatorV3Interface { uint256 public constant override version = 0; AggregatorV3Interface public immutable BASE; AggregatorV3Interface public immutable QUOTE; uint8 public immutable DECIMALS; constructor( address _base, address _quote, uint8 _decimals ) { require(_decimals > uint8(0) && _decimals <= uint8(18), "Invalid _decimals"); DECIMALS = _decimals; BASE = AggregatorV3Interface(_base); QUOTE = AggregatorV3Interface(_quote); } function decimals() external view override returns (uint8) { return DECIMALS; } function getRoundData(uint80) external pure override returns ( uint80, int256, uint256, uint256, uint80 ) { revert("not implemented - use latestRoundData()"); } function description() external pure override returns (string memory) { return "DerivedPriceFeed.sol"; } function latestRoundData() external view override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return (uint80(0), getDerivedPrice(), block.timestamp, block.timestamp, uint80(0)); } // https://docs.chain.link/docs/get-the-latest-price/#getting-a-different-price-denomination function getDerivedPrice() internal view returns (int256) { (, int256 basePrice, , , ) = BASE.latestRoundData(); uint8 baseDecimals = BASE.decimals(); basePrice = scalePrice(basePrice, baseDecimals, DECIMALS); (, int256 quotePrice, , , ) = QUOTE.latestRoundData(); uint8 quoteDecimals = QUOTE.decimals(); quotePrice = scalePrice(quotePrice, quoteDecimals, DECIMALS); return (basePrice * int256(10**uint256(DECIMALS))) / quotePrice; } function scalePrice( int256 _price, uint8 _priceDecimals, uint8 _decimals ) internal pure returns (int256) { if (_priceDecimals < _decimals) { return _price * int256(10**uint256(_decimals - _priceDecimals)); } else if (_priceDecimals > _decimals) { return _price / int256(10**uint256(_priceDecimals - _decimals)); } return _price; } }
// 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/ArbitrumSequencerUptimeFeedInterface.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); int256 private constant ANSWER_SEQ_OFFLINE = 1; address public immutable CROSS_DOMAIN_MESSENGER; address public immutable L2_SEQ_STATUS_RECORDER; // 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 l2ArbitrumSequencerUptimeFeedAddr 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 l2ArbitrumSequencerUptimeFeedAddr, address configACAddr, uint256 maxGas, uint256 gasPriceBid, address gasPriceL1FeedAddr, PaymentStrategy paymentStrategy ) { require(crossDomainMessengerAddr != address(0), "Invalid xDomain Messenger address"); require(l2ArbitrumSequencerUptimeFeedAddr != address(0), "Invalid ArbitrumSequencerUptimeFeed contract address"); CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; L2_SEQ_STATUS_RECORDER = l2ArbitrumSequencerUptimeFeedAddr; // 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 * - ArbitrumValidator 1.0.0: change target of L2 sequencer status update * - now calls `updateStatus` on an L2 ArbitrumSequencerUptimeFeed contract instead of * directly calling the Flags contract * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "ArbitrumValidator 1.0.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 ArbitrumSequencerUptimeFeed call bytes4 selector = ArbitrumSequencerUptimeFeedInterface.updateStatus.selector; bool status = currentAnswer == ANSWER_SEQ_OFFLINE; uint64 timestamp = uint64(block.timestamp); // Encode `status` and `timestamp` bytes memory message = abi.encodeWithSelector(selector, status, timestamp); // 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_SEQ_STATUS_RECORDER, // 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: 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.0; interface ArbitrumSequencerUptimeFeedInterface { function updateStatus(bool status, uint64 timestamp) external; }
// 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: 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.7.6 <0.9.0; import "../../dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; /** * @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; } contract MockOVMCrossDomainMessenger is iOVM_CrossDomainMessenger{ address internal mockMessageSender; constructor(address sender) { mockMessageSender = sender; } function xDomainMessageSender() external view override returns (address) { return mockMessageSender; } function _setMockMessageSender(address sender) external { mockMessageSender = sender; } /******************** * 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 override { Address.functionCall(_target, _message, "sendMessage reverted"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/DelegateForwarderInterface.sol"; import "./vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol"; import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; import "./OptimismCrossDomainForwarder.sol"; /** * @title OptimismCrossDomainGovernor - L1 xDomain account representation (with delegatecall support) for Optimism * @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 simultaneously owned by the `l1Owner` and L2 `owner` */ contract OptimismCrossDomainGovernor is DelegateForwarderInterface, OptimismCrossDomainForwarder { /** * @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 * @dev Empty constructor required due to inheriting from abstract contract CrossDomainForwarder */ constructor(iOVM_CrossDomainMessenger crossDomainMessengerAddr, address l1OwnerAddr) OptimismCrossDomainForwarder(crossDomainMessengerAddr, l1OwnerAddr) {} /** * @notice versions: * * - OptimismCrossDomainForwarder 1.0.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "OptimismCrossDomainGovernor 1.0.0"; } /** * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner * @inheritdoc ForwarderInterface */ function forward(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { Address.functionCall(target, data, "Governor call reverted"); } /** * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner * @inheritdoc DelegateForwarderInterface */ function forwardDelegate(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { Address.functionDelegateCall(target, data, "Governor delegatecall reverted"); } /** * @notice The call MUST come from either the L1 owner (via cross-chain message) or the L2 owner. Reverts otherwise. */ modifier onlyLocalOrCrossDomainOwner() { address messenger = crossDomainMessenger(); // 1. The delegatecall MUST come from either the L1 owner (via cross-chain message) or the L2 owner require(msg.sender == messenger || msg.sender == owner(), "Sender is not the L2 messenger or owner"); // 2. The L2 Messenger's caller MUST be the L1 Owner if (msg.sender == messenger) { require( iOVM_CrossDomainMessenger(messenger).xDomainMessageSender() == l1Owner(), "xDomain sender is not the L1 owner" ); } _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title DelegateForwarderInterface - forwards a delegatecall to a target, under some conditions interface DelegateForwarderInterface { /** * @notice forward delegatecalls the `target` with `data` * @param target contract address to be delegatecalled * @param data to send to target contract */ function forwardDelegate(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"; import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.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 iOVM_CrossDomainMessenger 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(iOVM_CrossDomainMessenger crossDomainMessengerAddr, address l1OwnerAddr) CrossDomainOwnable(l1OwnerAddr) { require(address(crossDomainMessengerAddr) != address(0), "Invalid xDomain Messenger address"); OVM_CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; } /** * @notice versions: * * - OptimismCrossDomainForwarder 0.1.0: initial release * - OptimismCrossDomainForwarder 1.0.0: Use OZ Address, CrossDomainOwnable * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "OptimismCrossDomainForwarder 1.0.0"; } /** * @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` being the L1 owner address * @inheritdoc ForwarderInterface */ function forward(address target, bytes memory data) external virtual override onlyL1Owner { Address.functionCall(target, data, "Forwarder call reverted"); } /** * @notice This is always the address of the OVM_L2CrossDomainMessenger contract */ function crossDomainMessenger() public view returns (address) { return address(OVM_CROSS_DOMAIN_MESSENGER); } /** * @notice The call MUST come from the L1 owner (via cross-chain message.) Reverts otherwise. */ modifier onlyL1Owner() override { require(msg.sender == crossDomainMessenger(), "Sender is not the L2 messenger"); require( iOVM_CrossDomainMessenger(crossDomainMessenger()).xDomainMessageSender() == l1Owner(), "xDomain sender is not the L1 owner" ); _; } /** * @notice The call MUST come from the proposed L1 owner (via cross-chain message.) Reverts otherwise. */ modifier onlyProposedL1Owner() override { address messenger = crossDomainMessenger(); require(msg.sender == messenger, "Sender is not the L2 messenger"); require( iOVM_CrossDomainMessenger(messenger).xDomainMessageSender() == s_l1PendingOwner, "Must be proposed L1 owner" ); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./CrossDomainOwnable.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 consider that position to be held by the `l1Owner` */ abstract contract CrossDomainForwarder is ForwarderInterface, CrossDomainOwnable { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../ConfirmedOwner.sol"; import "./interfaces/CrossDomainOwnableInterface.sol"; /** * @title The CrossDomainOwnable contract * @notice A contract with helpers for cross-domain contract ownership. */ contract CrossDomainOwnable is CrossDomainOwnableInterface, ConfirmedOwner { address internal s_l1Owner; address internal s_l1PendingOwner; constructor(address newl1Owner) ConfirmedOwner(msg.sender) { _setL1Owner(newl1Owner); } /** * @notice transfer ownership of this account to a new L1 owner * @param to new L1 owner that will be allowed to call the forward fn */ function transferL1Ownership(address to) public virtual override onlyL1Owner { _transferL1Ownership(to); } /** * @notice accept ownership of this account to a new L1 owner */ function acceptL1Ownership() public virtual override onlyProposedL1Owner { _setL1Owner(s_l1PendingOwner); } /** * @notice Get the current owner */ function l1Owner() public view override returns (address) { return s_l1Owner; } /** * @notice validate, transfer ownership, and emit relevant events */ function _transferL1Ownership(address to) internal { require(to != msg.sender, "Cannot transfer to self"); s_l1PendingOwner = to; emit L1OwnershipTransferRequested(s_l1Owner, to); } /** * @notice set ownership, emit relevant events. Used in acceptOwnership() */ function _setL1Owner(address to) internal { address oldOwner = s_l1Owner; s_l1Owner = to; s_l1PendingOwner = address(0); emit L1OwnershipTransferred(oldOwner, to); } /** * @notice Reverts if called by anyone other than the L1 owner. */ modifier onlyL1Owner() virtual { require(msg.sender == s_l1Owner, "Only callable by L1 owner"); _; } /** * @notice Reverts if called by anyone other than the L1 owner. */ modifier onlyProposedL1Owner() virtual { require(msg.sender == s_l1PendingOwner, "Only callable by proposed L1 owner"); _; } }
// 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; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title CrossDomainOwnableInterface - A contract with helpers for cross-domain contract ownership interface CrossDomainOwnableInterface { event L1OwnershipTransferRequested(address indexed from, address indexed to); event L1OwnershipTransferred(address indexed from, address indexed to); function l1Owner() external returns (address); function transferL1Ownership(address recipient) external; function acceptL1Ownership() external; }
// 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; import "./CrossDomainForwarder.sol"; import "./interfaces/ForwarderInterface.sol"; import "./interfaces/DelegateForwarderInterface.sol"; /** * @title CrossDomainDelegateForwarder - L1 xDomain account representation (with delegatecall support) * @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 consider that position to be held by the `l1Owner` */ abstract contract CrossDomainDelegateForwarder is DelegateForwarderInterface, CrossDomainOwnable { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/DelegateForwarderInterface.sol"; import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; import "./ArbitrumCrossDomainForwarder.sol"; /** * @title ArbitrumCrossDomainGovernor - L1 xDomain account representation (with delegatecall support) for Arbitrum * @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 simultaneously owned by the `l1Owner` and L2 `owner` */ contract ArbitrumCrossDomainGovernor is DelegateForwarderInterface, ArbitrumCrossDomainForwarder { /** * @notice creates a new Arbitrum xDomain Forwarder contract * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn * @dev Empty constructor required due to inheriting from abstract contract CrossDomainForwarder */ constructor(address l1OwnerAddr) ArbitrumCrossDomainForwarder(l1OwnerAddr) {} /** * @notice versions: * * - ArbitrumCrossDomainGovernor 1.0.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "ArbitrumCrossDomainGovernor 1.0.0"; } /** * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner * @inheritdoc ForwarderInterface */ function forward(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { Address.functionCall(target, data, "Governor call reverted"); } /** * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner * @inheritdoc DelegateForwarderInterface */ function forwardDelegate(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { Address.functionDelegateCall(target, data, "Governor delegatecall reverted"); } /** * @notice The call MUST come from either the L1 owner (via cross-chain message) or the L2 owner. Reverts otherwise. */ modifier onlyLocalOrCrossDomainOwner() { require(msg.sender == crossDomainMessenger() || msg.sender == owner(), "Sender is not the L2 messenger or owner"); _; } }
// 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 "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.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 * @dev Empty constructor required due to inheriting from abstract contract CrossDomainForwarder */ constructor(address l1OwnerAddr) CrossDomainOwnable(l1OwnerAddr) {} /** * @notice versions: * * - ArbitrumCrossDomainForwarder 0.1.0: initial release * - ArbitrumCrossDomainForwarder 1.0.0: Use OZ Address, CrossDomainOwnable * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "ArbitrumCrossDomainForwarder 1.0.0"; } /** * @notice The L2 xDomain `msg.sender`, generated from L1 sender address */ function crossDomainMessenger() public view returns (address) { return AddressAliasHelper.applyL1ToL2Alias(l1Owner()); } /** * @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` being the L1 owner address * @inheritdoc ForwarderInterface */ function forward(address target, bytes memory data) external virtual override onlyL1Owner { Address.functionCall(target, data, "Forwarder call reverted"); } /** * @notice The call MUST come from the L1 owner (via cross-chain message.) Reverts otherwise. */ modifier onlyL1Owner() override { require(msg.sender == crossDomainMessenger(), "Sender is not the L2 messenger"); _; } /** * @notice The call MUST come from the proposed L1 owner (via cross-chain message.) Reverts otherwise. */ modifier onlyProposedL1Owner() override { require(msg.sender == AddressAliasHelper.applyL1ToL2Alias(s_l1PendingOwner), "Must be proposed L1 owner"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {AddressAliasHelper} from "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; import {ForwarderInterface} from "./interfaces/ForwarderInterface.sol"; import {AggregatorInterface} from "../interfaces/AggregatorInterface.sol"; import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol"; import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {FlagsInterface} from "./interfaces/FlagsInterface.sol"; import {ArbitrumSequencerUptimeFeedInterface} from "./interfaces/ArbitrumSequencerUptimeFeedInterface.sol"; import {SimpleReadAccessController} from "../SimpleReadAccessController.sol"; import {ConfirmedOwner} from "../ConfirmedOwner.sol"; /** * @title ArbitrumSequencerUptimeFeed - L2 sequencer uptime status aggregator * @notice L2 contract that receives status updates from a specific L1 address, * records a new answer if the status changed, and raises or lowers the flag on the * stored Flags contract. */ contract ArbitrumSequencerUptimeFeed is AggregatorV2V3Interface, ArbitrumSequencerUptimeFeedInterface, TypeAndVersionInterface, SimpleReadAccessController { /// @dev Round info (for uptime history) struct Round { bool status; uint64 timestamp; } /// @dev Packed state struct to save sloads struct FeedState { uint80 latestRoundId; bool latestStatus; uint64 latestTimestamp; } /// @notice Contract is not yet initialized error Uninitialized(); /// @notice Contract is already initialized error AlreadyInitialized(); /// @notice Sender is not the L2 messenger error InvalidSender(); /// @notice Replacement for AggregatorV3Interface "No data present" error NoDataPresent(); event Initialized(); event L1SenderTransferred(address indexed from, address indexed to); /// @dev Emitted when an `updateStatus` call is ignored due to unchanged status or stale timestamp event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); /// @dev Follows: https://eips.ethereum.org/EIPS/eip-1967 address public constant FLAG_L2_SEQ_OFFLINE = address(bytes20(bytes32(uint256(keccak256("chainlink.flags.arbitrum-seq-offline")) - 1))); uint8 public constant override decimals = 0; string public constant override description = "L2 Sequencer Uptime Status Feed"; uint256 public constant override version = 1; /// @dev Flags contract to raise/lower flags on, during status transitions FlagsInterface public immutable FLAGS; /// @dev L1 address address private s_l1Sender; /// @dev s_latestRoundId == 0 means this contract is uninitialized. FeedState private s_feedState = FeedState({latestRoundId: 0, latestStatus: false, latestTimestamp: 0}); mapping(uint80 => Round) private s_rounds; /** * @param flagsAddress Address of the Flags contract on L2 * @param l1SenderAddress Address of the L1 contract that is permissioned to call this contract */ constructor(address flagsAddress, address l1SenderAddress) { setL1Sender(l1SenderAddress); FLAGS = FlagsInterface(flagsAddress); } /** * @notice Check if a roundId is valid in this current contract state * @dev Mainly used for AggregatorV2V3Interface functions * @param roundId Round ID to check */ function isValidRound(uint256 roundId) private view returns (bool) { return roundId > 0 && roundId <= type(uint80).max && s_feedState.latestRoundId >= roundId; } /// @notice Check that this contract is initialised, otherwise throw function requireInitialized(uint80 latestRoundId) private pure { if (latestRoundId == 0) { revert Uninitialized(); } } /** * @notice Initialise the first round. Can't be done in the constructor, * because this contract's address must be permissioned by the the Flags contract * (The Flags contract itself is a SimpleReadAccessController). */ function initialize() external onlyOwner { FeedState memory feedState = s_feedState; if (feedState.latestRoundId != 0) { revert AlreadyInitialized(); } uint64 timestamp = uint64(block.timestamp); bool currentStatus = FLAGS.getFlag(FLAG_L2_SEQ_OFFLINE); // Initialise roundId == 1 as the first round recordRound(1, currentStatus, timestamp); emit Initialized(); } /** * @notice versions: * * - ArbitrumSequencerUptimeFeed 1.0.0: initial release * * @inheritdoc TypeAndVersionInterface */ function typeAndVersion() external pure virtual override returns (string memory) { return "ArbitrumSequencerUptimeFeed 1.0.0"; } /// @return L1 sender address function l1Sender() public view virtual returns (address) { return s_l1Sender; } /** * @notice Set the allowed L1 sender for this contract to a new L1 sender * @dev Can be disabled by setting the L1 sender as `address(0)`. Accessible only by owner. * @param to new L1 sender that will be allowed to call `updateStatus` on this contract */ function transferL1Sender(address to) external virtual onlyOwner { setL1Sender(to); } /// @notice internal method that stores the L1 sender function setL1Sender(address to) private { address from = s_l1Sender; if (from != to) { s_l1Sender = to; emit L1SenderTransferred(from, to); } } /** * @notice Messages sent by the stored L1 sender will arrive on L2 with this * address as the `msg.sender` * @return L2-aliased form of the L1 sender address */ function aliasedL1MessageSender() public view returns (address) { return AddressAliasHelper.applyL1ToL2Alias(l1Sender()); } /** * @dev Returns an AggregatorV2V3Interface compatible answer from status flag * * @param status The status flag to convert to an aggregator-compatible answer */ function getStatusAnswer(bool status) private pure returns (int256) { return status ? int256(1) : int256(0); } /** * @notice Raise or lower the flag on the stored Flags contract. */ function forwardStatusToFlags(bool status) private { if (status) { FLAGS.raiseFlag(FLAG_L2_SEQ_OFFLINE); } else { FLAGS.lowerFlag(FLAG_L2_SEQ_OFFLINE); } } /** * @notice Helper function to record a round and set the latest feed state. * * @param roundId The round ID to record * @param status Sequencer status * @param timestamp Block timestamp of status update */ function recordRound( uint80 roundId, bool status, uint64 timestamp ) private { Round memory nextRound = Round(status, timestamp); FeedState memory feedState = FeedState(roundId, status, timestamp); s_rounds[roundId] = nextRound; s_feedState = feedState; emit NewRound(roundId, msg.sender, timestamp); emit AnswerUpdated(getStatusAnswer(status), roundId, timestamp); } /** * @notice Record a new status and timestamp if it has changed since the last round. * @dev This function will revert if not called from `l1Sender` via the L1->L2 messenger. * * @param status Sequencer status * @param timestamp Block timestamp of status update */ function updateStatus(bool status, uint64 timestamp) external override { FeedState memory feedState = s_feedState; requireInitialized(feedState.latestRoundId); if (msg.sender != aliasedL1MessageSender()) { revert InvalidSender(); } // Ignore if status did not change or latest recorded timestamp is newer if (feedState.latestStatus == status || feedState.latestTimestamp > timestamp) { emit UpdateIgnored(feedState.latestStatus, feedState.latestTimestamp, status, timestamp); return; } // Prepare a new round with updated status feedState.latestRoundId += 1; recordRound(feedState.latestRoundId, status, timestamp); forwardStatusToFlags(status); } /// @inheritdoc AggregatorInterface function latestAnswer() external view override checkAccess returns (int256) { FeedState memory feedState = s_feedState; requireInitialized(feedState.latestRoundId); return getStatusAnswer(feedState.latestStatus); } /// @inheritdoc AggregatorInterface function latestTimestamp() external view override checkAccess returns (uint256) { FeedState memory feedState = s_feedState; requireInitialized(feedState.latestRoundId); return feedState.latestTimestamp; } /// @inheritdoc AggregatorInterface function latestRound() external view override checkAccess returns (uint256) { FeedState memory feedState = s_feedState; requireInitialized(feedState.latestRoundId); return feedState.latestRoundId; } /// @inheritdoc AggregatorInterface function getAnswer(uint256 roundId) external view override checkAccess returns (int256) { requireInitialized(s_feedState.latestRoundId); if (isValidRound(roundId)) { return getStatusAnswer(s_rounds[uint80(roundId)].status); } return 0; } /// @inheritdoc AggregatorInterface function getTimestamp(uint256 roundId) external view override checkAccess returns (uint256) { requireInitialized(s_feedState.latestRoundId); if (isValidRound(roundId)) { return s_rounds[uint80(roundId)].timestamp; } return 0; } /// @inheritdoc AggregatorV3Interface function getRoundData(uint80 _roundId) public view override checkAccess returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { requireInitialized(s_feedState.latestRoundId); if (isValidRound(_roundId)) { Round memory round = s_rounds[_roundId]; answer = getStatusAnswer(round.status); startedAt = uint256(round.timestamp); } else { answer = 0; startedAt = 0; } roundId = _roundId; updatedAt = startedAt; answeredInRound = roundId; } /// @inheritdoc AggregatorV3Interface function latestRoundData() external view override checkAccess returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { FeedState memory feedState = s_feedState; requireInitialized(feedState.latestRoundId); roundId = feedState.latestRoundId; answer = getStatusAnswer(feedState.latestStatus); startedAt = feedState.latestTimestamp; updatedAt = startedAt; answeredInRound = roundId; } }
// 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; import "./AggregatorInterface.sol"; import "./AggregatorV3Interface.sol"; interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./SimpleWriteAccessController.sol"; /** * @title SimpleReadAccessController * @notice Gives access to: * - any externally owned account (note that off-chain actors can always read * any contract storage regardless of on-chain 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.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 "../interfaces/AggregatorV2V3Interface.sol"; /** * @title MockV3Aggregator * @notice Based on the FluxAggregator contract * @notice Use this contract when you need to test * other contract's ability to read data from an * aggregator contract, but how the aggregator got * its answer is unimportant */ contract MockV3Aggregator is AggregatorV2V3Interface { uint256 public constant override version = 0; uint8 public override decimals; int256 public override latestAnswer; uint256 public override latestTimestamp; uint256 public override latestRound; mapping(uint256 => int256) public override getAnswer; mapping(uint256 => uint256) public override getTimestamp; mapping(uint256 => uint256) private getStartedAt; constructor(uint8 _decimals, int256 _initialAnswer) { decimals = _decimals; updateAnswer(_initialAnswer); } function updateAnswer(int256 _answer) public { latestAnswer = _answer; latestTimestamp = block.timestamp; latestRound++; getAnswer[latestRound] = _answer; getTimestamp[latestRound] = block.timestamp; getStartedAt[latestRound] = block.timestamp; } function updateRoundData( uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt ) public { latestRound = _roundId; latestAnswer = _answer; latestTimestamp = _timestamp; getAnswer[latestRound] = _answer; getTimestamp[latestRound] = _timestamp; getStartedAt[latestRound] = _startedAt; } function getRoundData(uint80 _roundId) external view override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return (_roundId, getAnswer[_roundId], getStartedAt[_roundId], getTimestamp[_roundId], _roundId); } function latestRoundData() external view override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return ( uint80(latestRound), getAnswer[latestRound], getStartedAt[latestRound], getTimestamp[latestRound], uint80(latestRound) ); } function description() external pure override returns (string memory) { return "v0.8/tests/MockV3Aggregator.sol"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol"; contract FeedConsumer { AggregatorV2V3Interface public immutable AGGREGATOR; constructor(address feedAddress) { AGGREGATOR = AggregatorV2V3Interface(feedAddress); } function latestAnswer() external view returns (int256 answer) { return AGGREGATOR.latestAnswer(); } function latestTimestamp() external view returns (uint256) { return AGGREGATOR.latestTimestamp(); } function latestRound() external view returns (uint256) { return AGGREGATOR.latestRound(); } function getAnswer(uint256 roundId) external view returns (int256) { return AGGREGATOR.getAnswer(roundId); } function getTimestamp(uint256 roundId) external view returns (uint256) { return AGGREGATOR.getTimestamp(roundId); } function decimals() external view returns (uint8) { return AGGREGATOR.decimals(); } function description() external view returns (string memory) { return AGGREGATOR.description(); } function version() external view returns (uint256) { return AGGREGATOR.version(); } function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return AGGREGATOR.getRoundData(_roundId); } function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return AGGREGATOR.latestRoundData(); } }
// 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; } }
import {IInbox} from "../dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol"; import {IBridge} from "../dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol"; contract MockArbitrumInbox is IInbox { event RetryableTicketNoRefundAliasRewriteCreated( address destAddr, uint256 arbTxCallValue, uint256 maxSubmissionCost, address submissionRefundAddress, address valueRefundAddress, uint256 maxGas, uint256 gasPriceBid, bytes data ); function sendL2Message(bytes calldata messageData) external override returns (uint256) { return 0; } function sendUnsignedTransaction( uint256 maxGas, uint256 gasPriceBid, uint256 nonce, address destAddr, uint256 amount, bytes calldata data ) external override returns (uint256) { return 0; } function sendContractTransaction( uint256 maxGas, uint256 gasPriceBid, address destAddr, uint256 amount, bytes calldata data ) external override returns (uint256) { return 0; } function sendL1FundedUnsignedTransaction( uint256 maxGas, uint256 gasPriceBid, uint256 nonce, address destAddr, bytes calldata data ) external payable override returns (uint256) { return 0; } function sendL1FundedContractTransaction( uint256 maxGas, uint256 gasPriceBid, address destAddr, bytes calldata data ) external payable override returns (uint256) { return 0; } function createRetryableTicketNoRefundAliasRewrite( address destAddr, uint256 arbTxCallValue, uint256 maxSubmissionCost, address submissionRefundAddress, address valueRefundAddress, uint256 maxGas, uint256 gasPriceBid, bytes calldata data ) external payable override returns (uint256) { emit RetryableTicketNoRefundAliasRewriteCreated( destAddr, arbTxCallValue, maxSubmissionCost, submissionRefundAddress, valueRefundAddress, maxGas, gasPriceBid, data ); return 42; } function createRetryableTicket( address destAddr, uint256 arbTxCallValue, uint256 maxSubmissionCost, address submissionRefundAddress, address valueRefundAddress, uint256 maxGas, uint256 gasPriceBid, bytes calldata data ) external payable override returns (uint256) { return 0; } function depositEth(address destAddr) external payable override returns (uint256) { return 0; } function depositEthRetryable( address destAddr, uint256 maxSubmissionCost, uint256 maxGas, uint256 maxGasPrice ) external payable override returns (uint256) { return 0; } function bridge() external view override returns (IBridge) { return IBridge(address(0)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../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.13; import "./interfaces/LinkTokenInterface.sol"; import "./interfaces/KeeperRegistryInterface.sol"; import "./interfaces/TypeAndVersionInterface.sol"; import "./ConfirmedOwner.sol"; import "./interfaces/ERC677ReceiverInterface.sol"; /** * @notice Contract to accept requests for upkeep registrations * @dev There are 2 registration workflows in this contract * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually * calls `approve` to register upkeep and emit events to inform UI and others interested. * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on * keeper registry and then emits approved event to finish the flow automatically without manual intervention. * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not. * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations. */ contract KeeperRegistrar is TypeAndVersionInterface, ConfirmedOwner, ERC677ReceiverInterface { /** * DISABLED: No auto approvals, all new upkeeps should be approved manually. * ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest. * ENABLED_ALL: Auto approvals for all new upkeeps subject to max allowed. */ enum AutoApproveType { DISABLED, ENABLED_SENDER_ALLOWLIST, ENABLED_ALL } bytes4 private constant REGISTER_REQUEST_SELECTOR = this.register.selector; mapping(bytes32 => PendingRequest) private s_pendingRequests; LinkTokenInterface public immutable LINK; /** * @notice versions: * - KeeperRegistrar 1.1.0: Add functionality for sender allowlist in auto approve * : Remove rate limit and add max allowed for auto approve * - KeeperRegistrar 1.0.0: initial release */ string public constant override typeAndVersion = "KeeperRegistrar 1.1.0"; struct Config { AutoApproveType autoApproveConfigType; uint32 autoApproveMaxAllowed; uint32 approvedCount; KeeperRegistryBaseInterface keeperRegistry; uint96 minLINKJuels; } struct PendingRequest { address admin; uint96 balance; } Config private s_config; // Only applicable if s_config.configType is ENABLED_SENDER_ALLOWLIST mapping(address => bool) private s_autoApproveAllowedSenders; event RegistrationRequested( bytes32 indexed hash, string name, bytes encryptedEmail, address indexed upkeepContract, uint32 gasLimit, address adminAddress, bytes checkData, uint96 amount, uint8 indexed source ); event RegistrationApproved(bytes32 indexed hash, string displayName, uint256 indexed upkeepId); event RegistrationRejected(bytes32 indexed hash); event AutoApproveAllowedSenderSet(address indexed senderAddress, bool allowed); event ConfigChanged( AutoApproveType autoApproveConfigType, uint32 autoApproveMaxAllowed, address keeperRegistry, uint96 minLINKJuels ); error InvalidAdminAddress(); error RequestNotFound(); error HashMismatch(); error OnlyAdminOrOwner(); error InsufficientPayment(); error RegistrationRequestFailed(); error OnlyLink(); error AmountMismatch(); error SenderMismatch(); error FunctionNotPermitted(); error LinkTransferFailed(address to); error InvalidDataLength(); /* * @param LINKAddress Address of Link token * @param autoApproveConfigType setting for auto-approve registrations * @param autoApproveMaxAllowed max number of registrations that can be auto approved * @param keeperRegistry keeper registry address * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with */ constructor( address LINKAddress, AutoApproveType autoApproveConfigType, uint16 autoApproveMaxAllowed, address keeperRegistry, uint96 minLINKJuels ) ConfirmedOwner(msg.sender) { LINK = LinkTokenInterface(LINKAddress); setRegistrationConfig(autoApproveConfigType, autoApproveMaxAllowed, keeperRegistry, minLINKJuels); } //EXTERNAL /** * @notice register can only be called through transferAndCall on LINK contract * @param name string of the upkeep to be registered * @param encryptedEmail email address of upkeep contact * @param upkeepContract address to perform upkeep on * @param gasLimit amount of gas to provide the target contract when performing upkeep * @param adminAddress address to cancel upkeep and withdraw remaining funds * @param checkData data passed to the contract when checking for upkeep * @param amount quantity of LINK upkeep is funded with (specified in Juels) * @param source application sending this request * @param sender address of the sender making the request */ function register( string memory name, bytes calldata encryptedEmail, address upkeepContract, uint32 gasLimit, address adminAddress, bytes calldata checkData, uint96 amount, uint8 source, address sender ) external onlyLINK { if (adminAddress == address(0)) { revert InvalidAdminAddress(); } bytes32 hash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); emit RegistrationRequested( hash, name, encryptedEmail, upkeepContract, gasLimit, adminAddress, checkData, amount, source ); Config memory config = s_config; if (_shouldAutoApprove(config, sender)) { s_config.approvedCount = config.approvedCount + 1; _approve(name, upkeepContract, gasLimit, adminAddress, checkData, amount, hash); } else { uint96 newBalance = s_pendingRequests[hash].balance + amount; s_pendingRequests[hash] = PendingRequest({admin: adminAddress, balance: newBalance}); } } /** * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event */ function approve( string memory name, address upkeepContract, uint32 gasLimit, address adminAddress, bytes calldata checkData, bytes32 hash ) external onlyOwner { PendingRequest memory request = s_pendingRequests[hash]; if (request.admin == address(0)) { revert RequestNotFound(); } bytes32 expectedHash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); if (hash != expectedHash) { revert HashMismatch(); } delete s_pendingRequests[hash]; _approve(name, upkeepContract, gasLimit, adminAddress, checkData, request.balance, hash); } /** * @notice cancel will remove a registration request and return the refunds to the msg.sender * @param hash the request hash */ function cancel(bytes32 hash) external { PendingRequest memory request = s_pendingRequests[hash]; if (!(msg.sender == request.admin || msg.sender == owner())) { revert OnlyAdminOrOwner(); } if (request.admin == address(0)) { revert RequestNotFound(); } delete s_pendingRequests[hash]; bool success = LINK.transfer(msg.sender, request.balance); if (!success) { revert LinkTransferFailed(msg.sender); } emit RegistrationRejected(hash); } /** * @notice owner calls this function to set if registration requests should be sent directly to the Keeper Registry * @param autoApproveConfigType setting for auto-approve registrations * note: autoApproveAllowedSenders list persists across config changes irrespective of type * @param autoApproveMaxAllowed max number of registrations that can be auto approved * @param keeperRegistry new keeper registry address * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with */ function setRegistrationConfig( AutoApproveType autoApproveConfigType, uint16 autoApproveMaxAllowed, address keeperRegistry, uint96 minLINKJuels ) public onlyOwner { uint32 approvedCount = s_config.approvedCount; s_config = Config({ autoApproveConfigType: autoApproveConfigType, autoApproveMaxAllowed: autoApproveMaxAllowed, approvedCount: approvedCount, minLINKJuels: minLINKJuels, keeperRegistry: KeeperRegistryBaseInterface(keeperRegistry) }); emit ConfigChanged(autoApproveConfigType, autoApproveMaxAllowed, keeperRegistry, minLINKJuels); } /** * @notice owner calls this function to set allowlist status for senderAddress * @param senderAddress senderAddress to set the allowlist status for * @param allowed true if senderAddress needs to be added to allowlist, false if needs to be removed */ function setAutoApproveAllowedSender(address senderAddress, bool allowed) external onlyOwner { s_autoApproveAllowedSenders[senderAddress] = allowed; emit AutoApproveAllowedSenderSet(senderAddress, allowed); } /** * @notice read the allowlist status of senderAddress * @param senderAddress address to read the allowlist status for */ function getAutoApproveAllowedSender(address senderAddress) external view returns (bool) { return s_autoApproveAllowedSenders[senderAddress]; } /** * @notice read the current registration configuration */ function getRegistrationConfig() external view returns ( AutoApproveType autoApproveConfigType, uint32 autoApproveMaxAllowed, uint32 approvedCount, address keeperRegistry, uint256 minLINKJuels ) { Config memory config = s_config; return ( config.autoApproveConfigType, config.autoApproveMaxAllowed, config.approvedCount, address(config.keeperRegistry), config.minLINKJuels ); } /** * @notice gets the admin address and the current balance of a registration request */ function getPendingRequest(bytes32 hash) external view returns (address, uint96) { PendingRequest memory request = s_pendingRequests[hash]; return (request.admin, request.balance); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @param sender Address of the sender transfering LINK * @param amount Amount of LINK sent (specified in Juels) * @param data Payload of the transaction */ function onTokenTransfer( address sender, uint256 amount, bytes calldata data ) external onlyLINK permittedFunctionsForLINK(data) isActualAmount(amount, data) isActualSender(sender, data) { if (data.length < 292) revert InvalidDataLength(); if (amount < s_config.minLINKJuels) { revert InsufficientPayment(); } (bool success, ) = address(this).delegatecall(data); // calls register if (!success) { revert RegistrationRequestFailed(); } } //PRIVATE /** * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event */ function _approve( string memory name, address upkeepContract, uint32 gasLimit, address adminAddress, bytes calldata checkData, uint96 amount, bytes32 hash ) private { KeeperRegistryBaseInterface keeperRegistry = s_config.keeperRegistry; // register upkeep uint256 upkeepId = keeperRegistry.registerUpkeep(upkeepContract, gasLimit, adminAddress, checkData); // fund upkeep bool success = LINK.transferAndCall(address(keeperRegistry), amount, abi.encode(upkeepId)); if (!success) { revert LinkTransferFailed(address(keeperRegistry)); } emit RegistrationApproved(hash, name, upkeepId); } /** * @dev verify sender allowlist if needed and check max limit */ function _shouldAutoApprove(Config memory config, address sender) private returns (bool) { if (config.autoApproveConfigType == AutoApproveType.DISABLED) { return false; } if ( config.autoApproveConfigType == AutoApproveType.ENABLED_SENDER_ALLOWLIST && (!s_autoApproveAllowedSenders[sender]) ) { return false; } if (config.approvedCount < config.autoApproveMaxAllowed) { return true; } return false; } //MODIFIERS /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { if (msg.sender != address(LINK)) { revert OnlyLink(); } _; } /** * @dev Reverts if the given data does not begin with the `register` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes memory _data) { bytes4 funcSelector; assembly { // solhint-disable-next-line avoid-low-level-calls funcSelector := mload(add(_data, 32)) // First 32 bytes contain length of data } if (funcSelector != REGISTER_REQUEST_SELECTOR) { revert FunctionNotPermitted(); } _; } /** * @dev Reverts if the actual amount passed does not match the expected amount * @param expected amount that should match the actual amount * @param data bytes */ modifier isActualAmount(uint256 expected, bytes memory data) { uint256 actual; assembly { actual := mload(add(data, 228)) } if (expected != actual) { revert AmountMismatch(); } _; } /** * @dev Reverts if the actual sender address does not match the expected sender address * @param expected address that should match the actual sender address * @param data bytes */ modifier isActualSender(address expected, bytes memory data) { address actual; assembly { actual := mload(add(data, 292)) } if (expected != actual) { revert SenderMismatch(); } _; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import {KeeperRegistryExecutableInterface} from "../interfaces/KeeperRegistryInterface.sol"; import {ConfirmedOwner} from "../ConfirmedOwner.sol"; /** * @notice This contract serves as a wrapper around a keeper registry's checkUpkeep function. */ contract KeeperRegistryCheckUpkeepGasUsageWrapper is ConfirmedOwner { KeeperRegistryExecutableInterface private immutable i_keeperRegistry; /** * @param keeperRegistry address of a keeper registry */ constructor(KeeperRegistryExecutableInterface keeperRegistry) ConfirmedOwner(msg.sender) { i_keeperRegistry = keeperRegistry; } /** * @return the keeper registry */ function getKeeperRegistry() external view returns (KeeperRegistryExecutableInterface) { return i_keeperRegistry; } /** * @notice This function is called by monitoring service to estimate how much gas checkUpkeep functions will consume. * @param id identifier of the upkeep to check * @param from the address to simulate performing the upkeep from */ function measureCheckGas(uint256 id, address from) external returns ( bool, bytes memory, uint256 ) { uint256 startGas = gasleft(); try i_keeperRegistry.checkUpkeep(id, from) returns ( bytes memory performData, uint256 maxLinkPayment, uint256 gasLimit, uint256 adjustedGasWei, uint256 linkEth ) { uint256 gasUsed = startGas - gasleft(); return (true, performData, gasUsed); } catch { uint256 gasUsed = startGas - gasleft(); return (false, "", gasUsed); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "../interfaces/KeeperCompatibleInterface.sol"; import "../interfaces/KeeperRegistryInterface.sol"; import "../ConfirmedOwner.sol"; error NoKeeperNodes(); error InsufficientInterval(); /** * @notice A canary upkeep which requires a different keeper to service its upkeep at an interval. This makes sure that * all keepers are in a healthy state. */ contract CanaryUpkeep is KeeperCompatibleInterface, ConfirmedOwner { uint256 private s_keeperIndex; uint256 private s_interval; uint256 private s_timestamp; KeeperRegistryExecutableInterface private immutable i_keeperRegistry; /** * @param keeperRegistry address of a keeper registry */ constructor(KeeperRegistryExecutableInterface keeperRegistry, uint256 interval) ConfirmedOwner(msg.sender) { i_keeperRegistry = keeperRegistry; s_timestamp = block.timestamp; s_interval = interval; s_keeperIndex = 0; } /** * @return the current keeper index */ function getKeeperIndex() external view returns (uint256) { return s_keeperIndex; } /** * @return the current timestamp */ function getTimestamp() external view returns (uint256) { return s_timestamp; } /** * @return the current interval */ function getInterval() external view returns (uint256) { return s_interval; } /** * @return the keeper registry */ function getKeeperRegistry() external view returns (KeeperRegistryExecutableInterface) { return i_keeperRegistry; } /** * @notice updates the interval * @param interval the new interval */ function setInterval(uint256 interval) external onlyOwner { s_interval = interval; } /** * @notice returns true if keeper array is not empty and sufficient time has passed */ function checkUpkeep( bytes calldata /* checkData */ ) external view returns (bool, bytes memory) { bool upkeepNeeded = block.timestamp >= s_interval + s_timestamp; return (upkeepNeeded, bytes("")); } /** * @notice checks keepers array limit, timestamp limit, and requires transaction origin must be the anticipated keeper. * If all checks pass, update the keeper index and timestamp. Otherwise, revert this transaction. */ function performUpkeep( bytes calldata /* performData */ ) external { (State memory _s, Config memory _c, address[] memory keepers) = i_keeperRegistry.getState(); if (keepers.length == 0) { revert NoKeeperNodes(); } if (block.timestamp < s_interval + s_timestamp) { revert InsufficientInterval(); } // if keepers array is shortened, this statement will make sure keeper index is always valid if (s_keeperIndex >= keepers.length) { s_keeperIndex = 0; } require(tx.origin == keepers[s_keeperIndex], "transaction origin is not the anticipated keeper."); s_keeperIndex = (s_keeperIndex + 1) % keepers.length; s_timestamp = block.timestamp; } }
// SPDX-License-Identifier: MIT // sourced from https://github.com/pipermerriam/ethereum-datetime pragma solidity ^0.8.0; library DateTime { /* * Date and Time utilities for ethereum contracts * */ struct _DateTime { uint16 year; uint8 month; uint8 day; uint8 hour; uint8 minute; uint8 second; uint8 weekday; } uint256 constant DAY_IN_SECONDS = 86400; uint256 constant YEAR_IN_SECONDS = 31536000; uint256 constant LEAP_YEAR_IN_SECONDS = 31622400; uint256 constant HOUR_IN_SECONDS = 3600; uint256 constant MINUTE_IN_SECONDS = 60; uint16 constant ORIGIN_YEAR = 1970; function isLeapYear(uint16 year) internal pure returns (bool) { if (year % 4 != 0) { return false; } if (year % 100 != 0) { return true; } if (year % 400 != 0) { return false; } return true; } function leapYearsBefore(uint256 year) internal pure returns (uint256) { year -= 1; return year / 4 - year / 100 + year / 400; } function getDaysInMonth(uint8 month, uint16 year) internal pure returns (uint8) { if ( month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ) { return 31; } else if (month == 4 || month == 6 || month == 9 || month == 11) { return 30; } else if (isLeapYear(year)) { return 29; } else { return 28; } } function parseTimestamp(uint256 timestamp) internal pure returns (_DateTime memory dt) { uint256 secondsAccountedFor = 0; uint256 buf; uint8 i; // Year dt.year = getYear(timestamp); buf = leapYearsBefore(dt.year) - leapYearsBefore(ORIGIN_YEAR); secondsAccountedFor += LEAP_YEAR_IN_SECONDS * buf; secondsAccountedFor += YEAR_IN_SECONDS * (dt.year - ORIGIN_YEAR - buf); // Month uint256 secondsInMonth; for (i = 1; i <= 12; i++) { secondsInMonth = DAY_IN_SECONDS * getDaysInMonth(i, dt.year); if (secondsInMonth + secondsAccountedFor > timestamp) { dt.month = i; break; } secondsAccountedFor += secondsInMonth; } // Day for (i = 1; i <= getDaysInMonth(dt.month, dt.year); i++) { if (DAY_IN_SECONDS + secondsAccountedFor > timestamp) { dt.day = i; break; } secondsAccountedFor += DAY_IN_SECONDS; } // Hour dt.hour = getHour(timestamp); // Minute dt.minute = getMinute(timestamp); // Second dt.second = getSecond(timestamp); // Day of week. dt.weekday = getWeekday(timestamp); } function getYear(uint256 timestamp) internal pure returns (uint16) { uint256 secondsAccountedFor = 0; uint16 year; uint256 numLeapYears; // Year year = uint16(ORIGIN_YEAR + timestamp / YEAR_IN_SECONDS); numLeapYears = leapYearsBefore(year) - leapYearsBefore(ORIGIN_YEAR); secondsAccountedFor += LEAP_YEAR_IN_SECONDS * numLeapYears; secondsAccountedFor += YEAR_IN_SECONDS * (year - ORIGIN_YEAR - numLeapYears); while (secondsAccountedFor > timestamp) { if (isLeapYear(uint16(year - 1))) { secondsAccountedFor -= LEAP_YEAR_IN_SECONDS; } else { secondsAccountedFor -= YEAR_IN_SECONDS; } year -= 1; } return year; } function getMonth(uint256 timestamp) internal pure returns (uint8) { return parseTimestamp(timestamp).month; } function getDay(uint256 timestamp) internal pure returns (uint8) { return parseTimestamp(timestamp).day; } function getHour(uint256 timestamp) internal pure returns (uint8) { return uint8((timestamp / 60 / 60) % 24); } function getMinute(uint256 timestamp) internal pure returns (uint8) { return uint8((timestamp / 60) % 60); } function getSecond(uint256 timestamp) internal pure returns (uint8) { return uint8(timestamp % 60); } function getWeekday(uint256 timestamp) internal pure returns (uint8) { return uint8((timestamp / DAY_IN_SECONDS + 4) % 7); } function toTimestamp( uint16 year, uint8 month, uint8 day ) internal pure returns (uint256 timestamp) { return toTimestamp(year, month, day, 0, 0, 0); } function toTimestamp( uint16 year, uint8 month, uint8 day, uint8 hour ) internal pure returns (uint256 timestamp) { return toTimestamp(year, month, day, hour, 0, 0); } function toTimestamp( uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute ) internal pure returns (uint256 timestamp) { return toTimestamp(year, month, day, hour, minute, 0); } function toTimestamp( uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute, uint8 second ) internal pure returns (uint256 timestamp) { uint16 i; // Year for (i = ORIGIN_YEAR; i < year; i++) { if (isLeapYear(i)) { timestamp += LEAP_YEAR_IN_SECONDS; } else { timestamp += YEAR_IN_SECONDS; } } // Month uint8[12] memory monthDayCounts; monthDayCounts[0] = 31; if (isLeapYear(year)) { monthDayCounts[1] = 29; } else { monthDayCounts[1] = 28; } monthDayCounts[2] = 31; monthDayCounts[3] = 30; monthDayCounts[4] = 31; monthDayCounts[5] = 30; monthDayCounts[6] = 31; monthDayCounts[7] = 31; monthDayCounts[8] = 30; monthDayCounts[9] = 31; monthDayCounts[10] = 30; monthDayCounts[11] = 31; for (i = 1; i < month; i++) { timestamp += DAY_IN_SECONDS * monthDayCounts[i - 1]; } // Day timestamp += DAY_IN_SECONDS * (day - 1); // Hour timestamp += HOUR_IN_SECONDS * (hour); // Minute timestamp += MINUTE_IN_SECONDS * (minute); // Second timestamp += second; return timestamp; } }
// SPDX-License-Identifier: Apache 2.0 /* * @title String & slice utility library for Solidity contracts. * @author Nick Johnson <[email protected]> * * @dev Functionality in this library is largely implemented using an * abstraction called a 'slice'. A slice represents a part of a string - * anything from the entire string to a single character, or even no * characters at all (a 0-length slice). Since a slice only has to specify * an offset and a length, copying and manipulating slices is a lot less * expensive than copying and manipulating the strings they reference. * * To further reduce gas costs, most functions on slice that need to return * a slice modify the original one instead of allocating a new one; for * instance, `s.split(".")` will return the text up to the first '.', * modifying s to only contain the remainder of the string after the '.'. * In situations where you do not want to modify the original slice, you * can make a copy first with `.copy()`, for example: * `s.copy().split(".")`. Try and avoid using this idiom in loops; since * Solidity has no memory management, it will result in allocating many * short-lived slices that are later discarded. * * Functions that return two slices come in two versions: a non-allocating * version that takes the second slice as an argument, modifying it in * place, and an allocating version that allocates and returns the second * slice; see `nextRune` for example. * * Functions that have to copy string data will return strings rather than * slices; these can be cast back to slices for further processing if * required. * * For convenience, some functions are provided with non-modifying * variants that create a new slice and return both; for instance, * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ pragma solidity ^0.8.0; library strings { struct slice { uint256 _len; uint256 _ptr; } function memcpy( uint256 dest, uint256 src, uint256 len ) private pure { // Copy word-length chunks while possible for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint256 mask = type(uint256).max; if (len > 0) { mask = 256**(32 - len) - 1; } assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /* * @dev Returns a slice containing the entire string. * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ function toSlice(string memory self) internal pure returns (slice memory) { uint256 ptr; assembly { ptr := add(self, 0x20) } return slice(bytes(self).length, ptr); } /* * @dev Returns the length of a null-terminated bytes32 string. * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ function len(bytes32 self) internal pure returns (uint256) { uint256 ret; if (self == 0) return 0; if (uint256(self) & type(uint128).max == 0) { ret += 16; self = bytes32(uint256(self) / 0x100000000000000000000000000000000); } if (uint256(self) & type(uint64).max == 0) { ret += 8; self = bytes32(uint256(self) / 0x10000000000000000); } if (uint256(self) & type(uint32).max == 0) { ret += 4; self = bytes32(uint256(self) / 0x100000000); } if (uint256(self) & type(uint16).max == 0) { ret += 2; self = bytes32(uint256(self) / 0x10000); } if (uint256(self) & type(uint8).max == 0) { ret += 1; } return 32 - ret; } /* * @dev Returns a slice containing the entire bytes32, interpreted as a * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) mstore(ptr, self) mstore(add(ret, 0x20), ptr) } ret._len = len(self); } /* * @dev Returns a new slice containing the same data as the current slice. * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } /* * @dev Copies a slice to a new string. * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ function toString(slice memory self) internal pure returns (string memory) { string memory ret = new string(self._len); uint256 retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); return ret; } /* * @dev Returns the length in runes of the slice. Note that this operation * takes time proportional to the length of the slice; avoid using it * in loops, and call `slice.empty()` if you only need to know whether * the slice is empty or not. * @param self The slice to operate on. * @return The length of the slice in runes. */ function len(slice memory self) internal pure returns (uint256 l) { // Starting at ptr-31 means the LSB will be the byte we care about uint256 ptr = self._ptr - 31; uint256 end = ptr + self._len; for (l = 0; ptr < end; l++) { uint8 b; assembly { b := and(mload(ptr), 0xFF) } if (b < 0x80) { ptr += 1; } else if (b < 0xE0) { ptr += 2; } else if (b < 0xF0) { ptr += 3; } else if (b < 0xF8) { ptr += 4; } else if (b < 0xFC) { ptr += 5; } else { ptr += 6; } } } /* * @dev Returns true if the slice is empty (has a length of 0). * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } /* * @dev Returns a positive number if `other` comes lexicographically after * `self`, a negative number if it comes before, or zero if the * contents of the two slices are equal. Comparison is done per-rune, * on unicode codepoints. * @param self The first slice to compare. * @param other The second slice to compare. * @return The result of the comparison. */ function compare(slice memory self, slice memory other) internal pure returns (int256) { uint256 shortest = self._len; if (other._len < self._len) shortest = other._len; uint256 selfptr = self._ptr; uint256 otherptr = other._ptr; for (uint256 idx = 0; idx < shortest; idx += 32) { uint256 a; uint256 b; assembly { a := mload(selfptr) b := mload(otherptr) } if (a != b) { // Mask out irrelevant bytes and check again uint256 mask = type(uint256).max; // 0xffff... if (shortest < 32) { mask = ~(2**(8 * (32 - shortest + idx)) - 1); } unchecked { uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int256(diff); } } selfptr += 32; otherptr += 32; } return int256(self._len) - int256(other._len); } /* * @dev Returns true if the two slices contain the same text. * @param self The first slice to compare. * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } /* * @dev Extracts the first rune in the slice into `rune`, advancing the * slice to point to the next rune and returning `self`. * @param self The slice to operate on. * @param rune The slice that will contain the first rune. * @return `rune`. */ function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { rune._len = 0; return rune; } uint256 l; uint256 b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { l = 1; } else if (b < 0xE0) { l = 2; } else if (b < 0xF0) { l = 3; } else { l = 4; } // Check for truncated codepoints if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } self._ptr += l; self._len -= l; rune._len = l; return rune; } /* * @dev Returns the first rune in the slice, advancing the slice to point * to the next rune. * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } /* * @dev Returns the number of the first codepoint in the slice. * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ function ord(slice memory self) internal pure returns (uint256 ret) { if (self._len == 0) { return 0; } uint256 word; uint256 length; uint256 divisor = 2**248; // Load the rune into the MSBs of b assembly { word := mload(mload(add(self, 32))) } uint256 b = word / divisor; if (b < 0x80) { ret = b; length = 1; } else if (b < 0xE0) { ret = b & 0x1F; length = 2; } else if (b < 0xF0) { ret = b & 0x0F; length = 3; } else { ret = b & 0x07; length = 4; } // Check for truncated codepoints if (length > self._len) { return 0; } for (uint256 i = 1; i < length; i++) { divisor = divisor / 256; b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; } ret = (ret * 64) | (b & 0x3F); } return ret; } /* * @dev Returns the keccak-256 hash of the slice. * @param self The slice to hash. * @return The hash of the slice. */ function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { ret := keccak256(mload(add(self, 32)), mload(self)) } } /* * @dev Returns true if `self` starts with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } if (self._ptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` starts with `needle`, `needle` is removed from the * beginning of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } bool equal = true; if (self._ptr != needle._ptr) { assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; self._ptr += needle._len; } return self; } /* * @dev Returns true if the slice ends with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } uint256 selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` ends with `needle`, `needle` is removed from the * end of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } uint256 selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; } return self; } // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. function findPtr( uint256 selflen, uint256 selfptr, uint256 needlelen, uint256 needleptr ) private pure returns (uint256) { uint256 ptr = selfptr; uint256 idx; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask; if (needlelen > 0) { mask = bytes32(~(2**(8 * (32 - needlelen)) - 1)); } bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } uint256 end = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr >= end) return selfptr + selflen; ptr++; assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; } } } return selfptr + selflen; } // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. function rfindPtr( uint256 selflen, uint256 selfptr, uint256 needlelen, uint256 needleptr ) private pure returns (uint256) { uint256 ptr; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask; if (needlelen > 0) { mask = bytes32(~(2**(8 * (32 - needlelen)) - 1)); } bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } ptr = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr <= selfptr) return selfptr; ptr--; assembly { ptrdata := and(mload(ptr), mask) } } return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; } } } return selfptr; } /* * @dev Modifies `self` to contain everything from the first occurrence of * `needle` to the end of the slice. `self` is set to the empty slice * if `needle` is not found. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; return self; } /* * @dev Modifies `self` to contain the part of the string from the start of * `self` to the end of the first occurrence of `needle`. If `needle` * is not found, `self` is set to the empty slice. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and `token` to everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function split( slice memory self, slice memory needle, slice memory token ) internal pure returns (slice memory) { uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; if (ptr == self._ptr + self._len) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; self._ptr = ptr + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and returning everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and `token` to everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function rsplit( slice memory self, slice memory needle, slice memory token ) internal pure returns (slice memory) { uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); if (ptr == self._ptr) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and returning everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } /* * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ function count(slice memory self, slice memory needle) internal pure returns (uint256 cnt) { uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } /* * @dev Returns True if `self` contains `needle`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } /* * @dev Returns a newly allocated string containing the concatenation of * `self` and `other`. * @param self The first slice to concatenate. * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ function concat(slice memory self, slice memory other) internal pure returns (string memory) { string memory ret = new string(self._len + other._len); uint256 retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); memcpy(retptr + self._len, other._ptr, other._len); return ret; } /* * @dev Joins an array of slices, using `self` as a delimiter, returning a * newly allocated string. * @param self The delimiter to use. * @param parts A list of slices to join. * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; uint256 length = self._len * (parts.length - 1); for (uint256 i = 0; i < parts.length; i++) length += parts[i]._len; string memory ret = new string(length); uint256 retptr; assembly { retptr := add(ret, 32) } for (uint256 i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { memcpy(retptr, self._ptr, self._len); retptr += self._len; } } return ret; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @notice getRevertMsg extracts a revert reason from a failed contract call */ function getRevertMsg(bytes memory payload) pure returns (string memory) { if (payload.length < 68) return "transaction reverted silently"; assembly { payload := add(payload, 0x04) } return abi.decode(payload, (string)); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./KeeperBase.sol"; import "./interfaces/KeeperCompatibleInterface.sol"; abstract contract KeeperCompatible is KeeperBase, KeeperCompatibleInterface {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../KeeperCompatible.sol"; contract KeeperCompatibleTestHelper is KeeperCompatible { function checkUpkeep(bytes calldata) external override returns (bool, bytes memory) {} function performUpkeep(bytes calldata) external override {} function testCannotExecute() public view cannotExecute {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "./KeeperCompatible.sol"; import "./VRFConsumerBaseV2.sol"; import "./interfaces/VRFCoordinatorV2Interface.sol"; /** * @title KeepersVRFConsumer * @notice KeepersVRFConsumer is a Chainlink Keepers compatible contract that also acts as a * VRF V2 requester and consumer. In particular, a random words request is made when `performUpkeep` * is called in a cadence provided by the upkeep interval. */ contract KeepersVRFConsumer is KeeperCompatibleInterface, VRFConsumerBaseV2 { // Upkeep interval in seconds. This contract's performUpkeep method will // be called by the Keepers network roughly every UPKEEP_INTERVAL seconds. uint256 public immutable UPKEEP_INTERVAL; // VRF V2 information, provided upon contract construction. VRFCoordinatorV2Interface public immutable COORDINATOR; uint64 public immutable SUBSCRIPTION_ID; uint16 public immutable REQUEST_CONFIRMATIONS; bytes32 public immutable KEY_HASH; // Contract state, updated in performUpkeep and fulfillRandomWords. uint256 public s_lastTimeStamp; uint256 public s_vrfRequestCounter; uint256 public s_vrfResponseCounter; struct RequestRecord { uint256 requestId; bool fulfilled; uint32 callbackGasLimit; uint256 randomness; } mapping(uint256 => RequestRecord) public s_requests; /* request ID */ /* request record */ constructor( address vrfCoordinator, uint64 subscriptionId, bytes32 keyHash, uint16 requestConfirmations, uint256 upkeepInterval ) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); SUBSCRIPTION_ID = subscriptionId; REQUEST_CONFIRMATIONS = requestConfirmations; KEY_HASH = keyHash; UPKEEP_INTERVAL = upkeepInterval; s_lastTimeStamp = block.timestamp; s_vrfRequestCounter = 0; s_vrfResponseCounter = 0; } /** * @notice Returns true if and only if at least UPKEEP_INTERVAL seconds have elapsed * since the last upkeep or since construction of the contract. * @return upkeepNeeded true if and only if at least UPKEEP_INTERVAL seconds have elapsed since the last upkeep or since construction * of the contract. */ function checkUpkeep( bytes calldata /* checkData */ ) external view override returns ( bool upkeepNeeded, bytes memory /* performData */ ) { upkeepNeeded = (block.timestamp - s_lastTimeStamp) > UPKEEP_INTERVAL; } /** * @notice Requests random words from the VRF coordinator if UPKEEP_INTERVAL seconds have elapsed * since the last upkeep or since construction of the contract. */ function performUpkeep( bytes calldata /* performData */ ) external override { if ((block.timestamp - s_lastTimeStamp) > UPKEEP_INTERVAL) { s_lastTimeStamp = block.timestamp; requestRandomWords(); } } /** * @notice VRF callback implementation * @param requestId the VRF V2 request ID, provided at request time. * @param randomWords the randomness provided by Chainlink VRF. */ function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { // Check that the request exists. If not, revert. RequestRecord memory record = s_requests[requestId]; require(record.requestId == requestId, "request ID not found in map"); // Update the randomness in the record, and increment the response counter. s_requests[requestId].randomness = randomWords[0]; s_vrfResponseCounter++; } /** * @notice Requests random words from Chainlink VRF. */ function requestRandomWords() internal { uint256 requestId = COORDINATOR.requestRandomWords( KEY_HASH, SUBSCRIPTION_ID, REQUEST_CONFIRMATIONS, 150000, // callback gas limit 1 // num words ); s_requests[requestId] = RequestRecord({ requestId: requestId, fulfilled: false, callbackGasLimit: 150000, randomness: 0 }); s_vrfRequestCounter++; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol) pragma solidity ^0.8.0; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. */ abstract contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function * and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address); /** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internall call site, it will return directly to the external caller. */ function _fallback() internal virtual { _beforeFallback(); _delegate(_implementation()); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data * is empty. */ receive() external payable virtual { _fallback(); } /** * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` * call, or as part of the Solidity `fallback` or `receive` functions. * * If overriden should call `super._beforeFallback()`. */ function _beforeFallback() internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "@openzeppelin/contracts/utils/Address.sol"; import "./ConfirmedOwner.sol"; /** * @title PermissionedForwardProxy * @notice This proxy is used to forward calls from sender to target. It maintains * a permission list to check which sender is allowed to call which target */ contract PermissionedForwardProxy is ConfirmedOwner { using Address for address; error PermissionNotSet(); event PermissionSet(address indexed sender, address target); event PermissionRemoved(address indexed sender); mapping(address => address) private s_forwardPermissionList; constructor() ConfirmedOwner(msg.sender) {} /** * @notice Verifies if msg.sender has permission to forward to target address and then forwards the handler * @param target address of the contract to forward the handler to * @param handler bytes to be passed to target in call data */ function forward(address target, bytes calldata handler) external { if (s_forwardPermissionList[msg.sender] != target) { revert PermissionNotSet(); } target.functionCall(handler); } /** * @notice Adds permission for sender to forward calls to target via this proxy. * Note that it allows to overwrite an existing permission * @param sender The address who will use this proxy to forward calls * @param target The address where sender will be allowed to forward calls */ function setPermission(address sender, address target) external onlyOwner { s_forwardPermissionList[sender] = target; emit PermissionSet(sender, target); } /** * @notice Removes permission for sender to forward calls via this proxy * @param sender The address who will use this proxy to forward calls */ function removePermission(address sender) external onlyOwner { delete s_forwardPermissionList[sender]; emit PermissionRemoved(sender); } /** * @notice Returns the target address that the sender can use this proxy for * @param sender The address to fetch the permissioned target for */ function getPermission(address sender) external view returns (address) { return s_forwardPermissionList[sender]; } }
pragma solidity ^0.8.0; import "../ConfirmedOwner.sol"; contract Greeter is ConfirmedOwner { string public greeting; constructor(address owner) ConfirmedOwner(owner) {} function setGreeting(string calldata _greeting) external onlyOwner { require(bytes(_greeting).length > 0, "Invalid greeting length"); greeting = _greeting; } function triggerRevert() external pure { require(false, "Greeter: revert triggered"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../VRFConsumerBaseV2.sol"; contract VRFExternalSubOwnerExample is VRFConsumerBaseV2 { VRFCoordinatorV2Interface COORDINATOR; LinkTokenInterface LINKTOKEN; uint256[] public s_randomWords; uint256 public s_requestId; address s_owner; constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); s_owner = msg.sender; } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { require(requestId == s_requestId, "request ID is incorrect"); s_randomWords = randomWords; } function requestRandomWords( uint64 subId, uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords, bytes32 keyHash ) external onlyOwner { // Will revert if subscription is not funded. s_requestId = COORDINATOR.requestRandomWords(keyHash, subId, requestConfirmations, callbackGasLimit, numWords); } function transferOwnership(address newOwner) external onlyOwner { s_owner = newOwner; } modifier onlyOwner() { require(msg.sender == s_owner); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../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 { require(requestId == s_requestId, "request ID is incorrect"); s_gasAvailable = gasleft(); s_randomWords = randomWords; } 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 topUpSubscription(uint96 amount) external { require(s_subId != 0, "sub not set"); // 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) { s_requestId = COORDINATOR.requestRandomWords(keyHash, subId, minReqConfs, callbackGasLimit, numWords); return s_requestId; } }
// SPDX-License-Identifier: MIT // A mock for testing code that relies on VRFCoordinatorV2. pragma solidity ^0.8.4; import "../interfaces/LinkTokenInterface.sol"; import "../interfaces/VRFCoordinatorV2Interface.sol"; import "../VRFConsumerBaseV2.sol"; contract VRFCoordinatorV2Mock is VRFCoordinatorV2Interface { uint96 public immutable BASE_FEE; uint96 public immutable GAS_PRICE_LINK; uint16 public immutable MAX_CONSUMERS = 100; error InvalidSubscription(); error InsufficientBalance(); error MustBeSubOwner(address owner); error TooManyConsumers(); error InvalidConsumer(); error InvalidRandomWords(); event RandomWordsRequested( bytes32 indexed keyHash, uint256 requestId, uint256 preSeed, uint64 indexed subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords, address indexed sender ); event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); event SubscriptionCreated(uint64 indexed subId, address owner); event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); event ConsumerAdded(uint64 indexed subId, address consumer); event ConsumerRemoved(uint64 indexed subId, address consumer); uint64 s_currentSubId; uint256 s_nextRequestId = 1; uint256 s_nextPreSeed = 100; struct Subscription { address owner; uint96 balance; } mapping(uint64 => Subscription) s_subscriptions; /* subId */ /* subscription */ mapping(uint64 => address[]) s_consumers; /* subId */ /* consumers */ struct Request { uint64 subId; uint32 callbackGasLimit; uint32 numWords; } mapping(uint256 => Request) s_requests; /* requestId */ /* request */ constructor(uint96 _baseFee, uint96 _gasPriceLink) { BASE_FEE = _baseFee; GAS_PRICE_LINK = _gasPriceLink; } function consumerIsAdded(uint64 _subId, address _consumer) public view returns (bool) { address[] memory consumers = s_consumers[_subId]; for (uint256 i = 0; i < consumers.length; i++) { if (consumers[i] == _consumer) { return true; } } return false; } modifier onlyValidConsumer(uint64 _subId, address _consumer) { if (!consumerIsAdded(_subId, _consumer)) { revert InvalidConsumer(); } _; } /** * @notice fulfillRandomWords fulfills the given request, sending the random words to the supplied * @notice consumer. * * @dev This mock uses a simplified formula for calculating payment amount and gas usage, and does * @dev not account for all edge cases handled in the real VRF coordinator. When making requests * @dev against the real coordinator a small amount of additional LINK is required. * * @param _requestId the request to fulfill * @param _consumer the VRF randomness consumer to send the result to */ function fulfillRandomWords(uint256 _requestId, address _consumer) external { fulfillRandomWordsWithOverride(_requestId, _consumer, new uint256[](0)); } /** * @notice fulfillRandomWordsWithOverride allows the user to pass in their own random words. * * @param _requestId the request to fulfill * @param _consumer the VRF randomness consumer to send the result to * @param _words user-provided random words */ function fulfillRandomWordsWithOverride( uint256 _requestId, address _consumer, uint256[] memory _words ) public { uint256 startGas = gasleft(); if (s_requests[_requestId].subId == 0) { revert("nonexistent request"); } Request memory req = s_requests[_requestId]; if (_words.length == 0) { _words = new uint256[](req.numWords); for (uint256 i = 0; i < req.numWords; i++) { _words[i] = uint256(keccak256(abi.encode(_requestId, i))); } } else if (_words.length != req.numWords) { revert InvalidRandomWords(); } VRFConsumerBaseV2 v; bytes memory callReq = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, _requestId, _words); (bool success, ) = _consumer.call{gas: req.callbackGasLimit}(callReq); uint96 payment = uint96(BASE_FEE + ((startGas - gasleft()) * GAS_PRICE_LINK)); if (s_subscriptions[req.subId].balance < payment) { revert InsufficientBalance(); } s_subscriptions[req.subId].balance -= payment; delete (s_requests[_requestId]); emit RandomWordsFulfilled(_requestId, _requestId, payment, success); } /** * @notice fundSubscription allows funding a subscription with an arbitrary amount for testing. * * @param _subId the subscription to fund * @param _amount the amount to fund */ function fundSubscription(uint64 _subId, uint96 _amount) public { if (s_subscriptions[_subId].owner == address(0)) { revert InvalidSubscription(); } uint96 oldBalance = s_subscriptions[_subId].balance; s_subscriptions[_subId].balance += _amount; emit SubscriptionFunded(_subId, oldBalance, oldBalance + _amount); } function requestRandomWords( bytes32 _keyHash, uint64 _subId, uint16 _minimumRequestConfirmations, uint32 _callbackGasLimit, uint32 _numWords ) external override onlyValidConsumer(_subId, msg.sender) returns (uint256) { if (s_subscriptions[_subId].owner == address(0)) { revert InvalidSubscription(); } uint256 requestId = s_nextRequestId++; uint256 preSeed = s_nextPreSeed++; s_requests[requestId] = Request({subId: _subId, callbackGasLimit: _callbackGasLimit, numWords: _numWords}); emit RandomWordsRequested( _keyHash, requestId, preSeed, _subId, _minimumRequestConfirmations, _callbackGasLimit, _numWords, msg.sender ); return requestId; } function createSubscription() external override returns (uint64 _subId) { s_currentSubId++; s_subscriptions[s_currentSubId] = Subscription({owner: msg.sender, balance: 0}); emit SubscriptionCreated(s_currentSubId, msg.sender); return s_currentSubId; } function getSubscription(uint64 _subId) external view override returns ( uint96 balance, uint64 reqCount, address owner, address[] memory consumers ) { if (s_subscriptions[_subId].owner == address(0)) { revert InvalidSubscription(); } return (s_subscriptions[_subId].balance, 0, s_subscriptions[_subId].owner, s_consumers[_subId]); } function cancelSubscription(uint64 _subId, address _to) external override onlySubOwner(_subId) { emit SubscriptionCanceled(_subId, _to, s_subscriptions[_subId].balance); delete (s_subscriptions[_subId]); } modifier onlySubOwner(uint64 _subId) { address owner = s_subscriptions[_subId].owner; if (owner == address(0)) { revert InvalidSubscription(); } if (msg.sender != owner) { revert MustBeSubOwner(owner); } _; } function getRequestConfig() external pure override returns ( uint16, uint32, bytes32[] memory ) { return (3, 2000000, new bytes32[](0)); } function addConsumer(uint64 _subId, address _consumer) external override onlySubOwner(_subId) { if (s_consumers[_subId].length == MAX_CONSUMERS) { revert TooManyConsumers(); } if (consumerIsAdded(_subId, _consumer)) { return; } s_consumers[_subId].push(_consumer); emit ConsumerAdded(_subId, _consumer); } function removeConsumer(uint64 _subId, address _consumer) external override onlySubOwner(_subId) onlyValidConsumer(_subId, _consumer) { address[] storage consumers = s_consumers[_subId]; for (uint256 i = 0; i < consumers.length; i++) { if (consumers[i] == _consumer) { address last = consumers[consumers.length - 1]; consumers[i] = last; consumers.pop(); break; } } emit ConsumerRemoved(_subId, _consumer); } function requestSubscriptionOwnerTransfer(uint64 _subId, address _newOwner) external pure override { revert("not implemented"); } function acceptSubscriptionOwnerTransfer(uint64 _subId) external pure override { revert("not implemented"); } function pendingRequestExists(uint64 subId) public view override returns (bool) { revert("not implemented"); } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "none", "useLiteralContent": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkEthFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint24","name":"blockCountPerTurn","type":"uint24"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address","name":"registrar","type":"address"}],"internalType":"struct Config","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"KeepersMustTakeTurns","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveKeepers","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"PaymentGreaterThanAllLINK","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"UpkeepNotActive","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint24","name":"blockCountPerTurn","type":"uint24"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address","name":"registrar","type":"address"}],"indexed":false,"internalType":"struct Config","name":"config","type":"tuple"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"keepers","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"KeepersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"OwnerFundsWithdrawn","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":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"payment","type":"uint96"},{"indexed":false,"internalType":"bytes","name":"performData","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"executeGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"inputs":[],"name":"FAST_GAS_FEED","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINK","outputs":[{"internalType":"contract LinkTokenInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINK_ETH_FEED","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"from","type":"address"}],"name":"checkUpkeep","outputs":[{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"uint256","name":"maxLinkPayment","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"adjustedGasWei","type":"uint256"},{"internalType":"uint256","name":"linkEth","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getKeeperInfo","outputs":[{"internalType":"address","name":"payee","type":"address"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum KeeperRegistry.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"}],"internalType":"struct State","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint24","name":"blockCountPerTurn","type":"uint24"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address","name":"registrar","type":"address"}],"internalType":"struct Config","name":"config","type":"tuple"},{"internalType":"address[]","name":"keepers","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"executeGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"lastKeeper","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint96","name":"amountSpent","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"performUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint24","name":"blockCountPerTurn","type":"uint24"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address","name":"registrar","type":"address"}],"internalType":"struct Config","name":"config","type":"tuple"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"keepers","type":"address[]"},{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setKeepers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum KeeperRegistry.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepTranscoderVersion","outputs":[{"internalType":"enum UpkeepFormat","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwnerFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60e06040523480156200001157600080fd5b5060405162006703380380620067038339810160408190526200003491620005a5565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000fe565b50506001600255506003805460ff191690556001600160a01b0380851660805283811660a052821660c052620000f481620001a9565b50505050620007f0565b336001600160a01b03821603620001585760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001b36200049e565b600d5460e082015163ffffffff91821691161015620001e557604051630e6af04160e21b815260040160405180910390fd5b604051806101200160405280826000015163ffffffff168152602001826020015163ffffffff168152602001826040015162ffffff168152602001826060015163ffffffff168152602001826080015162ffffff1681526020018260a0015161ffff1681526020018260c001516001600160601b031681526020018260e0015163ffffffff168152602001600c60010160049054906101000a900463ffffffff1663ffffffff16815250600c60008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548162ffffff021916908362ffffff160217905550606082015181600001600b6101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600f6101000a81548162ffffff021916908362ffffff16021790555060a08201518160000160126101000a81548161ffff021916908361ffff16021790555060c08201518160000160146101000a8154816001600160601b0302191690836001600160601b0316021790555060e08201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160010160046101000a81548163ffffffff021916908363ffffffff160217905550905050806101000151600e81905550806101200151600f81905550806101400151601260006101000a8154816001600160a01b0302191690836001600160a01b03160217905550806101600151601360006101000a8154816001600160a01b0302191690836001600160a01b031602179055507ffe125a41957477226ba20f85ef30a4024ea3bb8d066521ddc16df3f2944de32581604051620004939190620006f1565b60405180910390a150565b6000546001600160a01b03163314620004fa5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b80516001600160a01b03811681146200051457600080fd5b919050565b60405161018081016001600160401b03811182821017156200054b57634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff811681146200051457600080fd5b805162ffffff811681146200051457600080fd5b805161ffff811681146200051457600080fd5b80516001600160601b03811681146200051457600080fd5b6000806000808486036101e0811215620005be57600080fd5b620005c986620004fc565b9450620005d960208701620004fc565b9350620005e960408701620004fc565b925061018080605f19830112156200060057600080fd5b6200060a62000519565b91506200061a6060880162000551565b82526200062a6080880162000551565b60208301526200063d60a0880162000566565b60408301526200065060c0880162000551565b60608301526200066360e0880162000566565b6080830152610100620006788189016200057a565b60a08401526101206200068d818a016200058d565b60c0850152610140620006a2818b0162000551565b60e0860152610160808b015184870152848b015183870152620006c96101a08c01620004fc565b82870152620006dc6101c08c01620004fc565b90860152509699959850939650909450505050565b815163ffffffff1681526101808101602083015162000718602084018263ffffffff169052565b50604083015162000730604084018262ffffff169052565b50606083015162000749606084018263ffffffff169052565b50608083015162000761608084018262ffffff169052565b5060a08301516200077860a084018261ffff169052565b5060c08301516200079460c08401826001600160601b03169052565b5060e0830151620007ad60e084018263ffffffff169052565b5061010083810151908301526101208084015190830152610140808401516001600160a01b03908116918401919091526101609384015116929091019190915290565b60805160a05160c051615e9d62000866600039600081816104240152614253015260008181610575015261432501526000818161030401528181610e13015281816111300152818161195a01528181611ce501528181611dca015281816121c20152818161253101526125b50152615e9d6000f3fe608060405234801561001057600080fd5b506004361061025c5760003560e01c806393f0c1fc11610145578063b7fdb436116100bd578063da5c67411161008c578063ef47a0ce11610071578063ef47a0ce1461066a578063f2fde38b1461067d578063faa3e9961461069057600080fd5b8063da5c674114610636578063eb5dcd6c1461065757600080fd5b8063b7fdb436146105c5578063c41b813a146105d8578063c7c3a19a146105fc578063c80480221461062357600080fd5b8063a72aa27e11610114578063b121e147116100f9578063b121e14714610597578063b657bc9c146105aa578063b79550be146105bd57600080fd5b8063a72aa27e1461055d578063ad1783611461057057600080fd5b806393f0c1fc146104f4578063948108f714610524578063a4c0ed3614610537578063a710b2211461054a57600080fd5b80635c975abb116101d85780637d9b97e0116101a757806385c1b0ba1161018c57806385c1b0ba146104b05780638da5cb5b146104c35780638e86139b146104e157600080fd5b80637d9b97e0146104a05780638456cb59146104a857600080fd5b80635c975abb1461045b578063744bfe611461047257806379ba5097146104855780637bbaf1ea1461048d57600080fd5b80631b6b6d231161022f5780633f4ba83a116102145780633f4ba83a146104175780634584a4191461041f57806348013d7b1461044657600080fd5b80631b6b6d23146102ff5780631e12b8a51461034b57600080fd5b806306e3b63214610261578063181f5a771461028a5780631865c57d146102d3578063187256e8146102ea575b600080fd5b61027461026f366004614a72565b6106d6565b6040516102819190614a94565b60405180910390f35b6102c66040518060400160405280601481526020017f4b6565706572526567697374727920312e322e3000000000000000000000000081525081565b6040516102819190614b52565b6102db6107d5565b60405161028193929190614c7c565b6102fd6102f8366004614d4c565b610a8d565b005b6103267f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610281565b6103d7610359366004614d87565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526008602090815260409182902082516060810184528154948516808252740100000000000000000000000000000000000000009095046bffffffffffffffffffffffff1692810183905260019091015460ff16151592018290529192909190565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845291151560208401526bffffffffffffffffffffffff1690820152606001610281565b6102fd610afe565b6103267f000000000000000000000000000000000000000000000000000000000000000081565b61044e600081565b6040516102819190614de5565b60035460ff165b6040519015158152602001610281565b6102fd610480366004614df3565b610b10565b6102fd610e8d565b61046261049b366004614e68565b610f8f565b6102fd611058565b6102fd6111b7565b6102fd6104be366004614ef9565b6111c7565b60005473ffffffffffffffffffffffffffffffffffffffff16610326565b6102fd6104ef366004614f4d565b61198b565b610507610502366004614f8f565b611b8b565b6040516bffffffffffffffffffffffff9091168152602001610281565b6102fd610532366004614fc4565b611bbf565b6102fd610545366004614fe7565b611db2565b6102fd610558366004615041565b611fad565b6102fd61056b36600461507f565b612238565b6103267f000000000000000000000000000000000000000000000000000000000000000081565b6102fd6105a5366004614d87565b6123df565b6105076105b8366004614f8f565b6124d7565b6102fd6124f8565b6102fd6105d33660046150a2565b612654565b6105eb6105e6366004614df3565b6129b5565b604051610281959493929190615102565b61060f61060a366004614f8f565b612c6a565b604051610281989796959493929190615139565b6102fd610631366004614f8f565b612df5565b6106496106443660046151c3565b612feb565b604051908152602001610281565b6102fd610665366004615041565b6131e2565b6102fd610678366004615329565b613340565b6102fd61068b366004614d87565b61368c565b6106c961069e366004614d87565b73ffffffffffffffffffffffffffffffffffffffff166000908152600b602052604090205460ff1690565b6040516102819190615407565b606060006106e460056136a0565b905080841061071f576040517f1390f2a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600003610734576107318482615450565b92505b60008367ffffffffffffffff81111561074f5761074f615239565b604051908082528060200260200182016040528015610778578160200160208202803683370190505b50905060005b848110156107ca5761079b6107938288615467565b6005906136aa565b8282815181106107ad576107ad61547f565b6020908102919091010152806107c2816154ae565b91505061077e565b509150505b92915050565b6040805160808101825260008082526020820181905291810182905260608101919091526040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905261014081018290526101608101919091526040805161012081018252600c5463ffffffff8082168352640100000000808304821660208086019190915262ffffff6801000000000000000085048116868801526b010000000000000000000000850484166060878101919091526f010000000000000000000000000000008604909116608087015261ffff720100000000000000000000000000000000000086041660a08701526bffffffffffffffffffffffff74010000000000000000000000000000000000000000909504851660c0870152600d5480851660e08801529290920490921661010085018190528752601054909216908601526011549285019290925261095760056136a0565b606080860191909152815163ffffffff908116855260208084015182168187015260408085015162ffffff90811682890152858501518416948801949094526080808601519094169387019390935260a08085015161ffff169087015260c0808501516bffffffffffffffffffffffff169087015260e08085015190921691860191909152600e54610100860152600f5461012086015260125473ffffffffffffffffffffffffffffffffffffffff90811661014087015260135416610160860152600480548351818402810184019094528084528793879390918391830182828015610a7a57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610a4f575b5050505050905093509350935050909192565b610a956136bd565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b6020526040902080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115610af557610af5614da2565b02179055505050565b610b066136bd565b610b0e61373e565b565b8073ffffffffffffffffffffffffffffffffffffffff8116610b5e576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602052604090206002015483906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314610bd0576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152600760205260409020600101544364010000000090910467ffffffffffffffff161115610c2e576040517fff84e5dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c54600085815260076020526040812080546002909101546bffffffffffffffffffffffff740100000000000000000000000000000000000000009094048416939182169291169083821015610cb257610c8982856154e6565b9050826bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610cb25750815b6000610cbe82856154e6565b60008a815260076020526040902080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169055601054909150610d119083906bffffffffffffffffffffffff16615513565b601080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff928316179055601154610d5991831690615450565b601155604080516bffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff8a1660208201528a917ff3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318910160405180910390a26040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff89811660048301526bffffffffffffffffffffffff831660248301527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044015b6020604051808303816000875af1158015610e5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e819190615553565b50505050505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610f13576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000610f9d60035460ff1690565b15611004576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610f0a565b61105061104b338686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001925061381f915050565b613919565b949350505050565b6110606136bd565b6010546011546bffffffffffffffffffffffff90911690611082908290615450565b601155601080547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690556040516bffffffffffffffffffffffff821681527f1d07d0b0be43d3e5fee41a80b579af370affee03fa595bf56d5d4c19328162f19060200160405180910390a16040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526bffffffffffffffffffffffff821660248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044015b6020604051808303816000875af115801561118f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111b39190615553565b5050565b6111bf6136bd565b610b0e613d9c565b600173ffffffffffffffffffffffffffffffffffffffff82166000908152600b602052604090205460ff16600381111561120357611203614da2565b1415801561124b5750600373ffffffffffffffffffffffffffffffffffffffff82166000908152600b602052604090205460ff16600381111561124857611248614da2565b14155b15611282576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60125473ffffffffffffffffffffffffffffffffffffffff166112d1576040517fd12d7d8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082900361130c576040517f2c2fc94100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290526000808567ffffffffffffffff81111561136057611360615239565b60405190808252806020026020018201604052801561139357816020015b606081526020019060019003908161137e5790505b50905060008667ffffffffffffffff8111156113b1576113b1615239565b60405190808252806020026020018201604052801561143657816020015b6040805160e08101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816113cf5790505b50905060005b87811015611736578888828181106114565761145661547f565b60209081029290920135600081815260078452604090819020815160e08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c0100000000000000000000000092839004811698840198909852600184015463ffffffff81169584019590955267ffffffffffffffff6401000000008604166060840152938190048716608083015260029092015492831660a0820152910490931660c08401819052909850919650503314611549576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606085015167ffffffffffffffff90811614611591576040517fd096219c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848282815181106115a4576115a461547f565b6020026020010181905250600a600087815260200190815260200160002080546115cd9061556e565b80601f01602080910402602001604051908101604052809291908181526020018280546115f99061556e565b80156116465780601f1061161b57610100808354040283529160200191611646565b820191906000526020600020905b81548152906001019060200180831161162957829003601f168201915b505050505083828151811061165d5761165d61547f565b60209081029190910101528451611682906bffffffffffffffffffffffff1685615467565b600087815260076020908152604080832083815560018101849055600201839055600a90915281209195506116b79190614927565b6116c2600587613e5c565b508451604080516bffffffffffffffffffffffff909216825273ffffffffffffffffffffffffffffffffffffffff8916602083015287917fb38647142fbb1ea4c000fc4569b37a4e9a9f6313317b84ee3e5326c1a6cd06ff910160405180910390a28061172e816154ae565b91505061143c565b50826011546117459190615450565b601155604051600090611762908a908a9085908790602001615616565b60405160208183030381529060405290508673ffffffffffffffffffffffffffffffffffffffff16638e86139b601260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c71249ab60008b73ffffffffffffffffffffffffffffffffffffffff166348013d7b6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561181c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118409190615764565b866040518463ffffffff1660e01b815260040161185f93929190615785565b600060405180830381865afa15801561187c573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526118c29190810190615842565b6040518263ffffffff1660e01b81526004016118de9190614b52565b600060405180830381600087803b1580156118f857600080fd5b505af115801561190c573d6000803e3d6000fd5b50506040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a81166004830152602482018890527f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb9150604401610e3e565b6002336000908152600b602052604090205460ff1660038111156119b1576119b1614da2565b141580156119e357506003336000908152600b602052604090205460ff1660038111156119e0576119e0614da2565b14155b15611a1a576040517f0ebeec3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080611a2a84860186615a62565b92509250925060005b8351811015611b8357611af0848281518110611a5157611a5161547f565b6020026020010151848381518110611a6b57611a6b61547f565b602002602001015160800151858481518110611a8957611a8961547f565b602002602001015160400151868581518110611aa757611aa761547f565b602002602001015160c00151878681518110611ac557611ac561547f565b602002602001015160000151878781518110611ae357611ae361547f565b6020026020010151613e68565b838181518110611b0257611b0261547f565b60200260200101517f74931a144e43a50694897f241d973aecb5024c0e910f9bb80a163ea3c1cf5a71848381518110611b3d57611b3d61547f565b60209081029190910181015151604080516bffffffffffffffffffffffff909216825233928201929092520160405180910390a280611b7b816154ae565b915050611a33565b505050505050565b6000806000611b98614220565b915091506000611ba98360006143fd565b9050611bb6858284614442565b95945050505050565b6000828152600760205260409020600101548290640100000000900467ffffffffffffffff90811614611c1e576040517fd096219c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260076020526040902054611c469083906bffffffffffffffffffffffff16615513565b600084815260076020526040902080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff928316179055601154611c9a91841690615467565b6011556040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526bffffffffffffffffffffffff831660448201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd906064016020604051808303816000875af1158015611d43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d679190615553565b506040516bffffffffffffffffffffffff83168152339084907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a3505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611e21576040517fc8bad78d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208114611e5b576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e6982840184614f8f565b600081815260076020526040902060010154909150640100000000900467ffffffffffffffff90811614611ec9576040517fd096219c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260076020526040902054611ef19085906bffffffffffffffffffffffff16615513565b600082815260076020526040902080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff92909216919091179055601154611f48908590615467565b6011556040516bffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff86169082907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a35050505050565b8073ffffffffffffffffffffffffffffffffffffffff8116611ffb576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83811660009081526008602090815260409182902082516060810184528154948516808252740100000000000000000000000000000000000000009095046bffffffffffffffffffffffff16928101929092526001015460ff161515918101919091529033146120ac576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808516600090815260086020908152604090912080549092169091558101516011546120fb916bffffffffffffffffffffffff1690615450565b60115560208082015160405133815273ffffffffffffffffffffffffffffffffffffffff808716936bffffffffffffffffffffffff90931692908816917f9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f40698910160405180910390a460208101516040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301526bffffffffffffffffffffffff90921660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af115801561220d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122319190615553565b5050505050565b6000828152600760205260409020600101548290640100000000900467ffffffffffffffff90811614612297576040517fd096219c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602052604090206002015483906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314612309576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108fc8363ffffffff16108061232a5750600d5463ffffffff908116908416115b15612361576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008481526007602090815260409182902060010180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff8716908117909155915191825285917fc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c910160405180910390a250505050565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526009602052604090205416331461243f576040517f6752e7aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81811660008181526008602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556009909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b6000818152600760205260408120600101546107cf9063ffffffff16611b8b565b6125006136bd565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa15801561258d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b19190615b3f565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33601154846125fe9190615450565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401611170565b61265c6136bd565b828114158061266b5750600283105b156126a2576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b60045481101561272e576000600482815481106126c4576126c461547f565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168252600890526040902060010180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555080612726816154ae565b9150506126a5565b5060005b8381101561296457600085858381811061274e5761274e61547f565b90506020020160208101906127639190614d87565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600860205260408120805493945092909116908686868181106127a5576127a561547f565b90506020020160208101906127ba9190614d87565b905073ffffffffffffffffffffffffffffffffffffffff8116158061284d575073ffffffffffffffffffffffffffffffffffffffff82161580159061282b57508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561284d575073ffffffffffffffffffffffffffffffffffffffff81811614155b15612884576040517fb387a23800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600183015460ff16156128c3576040517f357d0cc400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600183810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905573ffffffffffffffffffffffffffffffffffffffff8181161461294d5782547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff82161783555b50505050808061295c906154ae565b915050612732565b5061297160048585614961565b507f056264c94f28bb06c99d13f0446eb96c67c215d8d707bce2655a98ddf1c0b71f848484846040516129a79493929190615bac565b60405180910390a150505050565b60606000806000806129c561451f565b6000878152600760209081526040808320815160e08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c0100000000000000000000000092839004811684880152600185015463ffffffff81168588015267ffffffffffffffff64010000000082041660608601528390048116608085015260029094015490811660a08401520490911660c08201528a8452600a90925280832090519192917f6e04ff0d0000000000000000000000000000000000000000000000000000000091612aa591602401615bde565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600080836080015173ffffffffffffffffffffffffffffffffffffffff16600c600001600b9054906101000a900463ffffffff1663ffffffff1684604051612b4c9190615cbc565b60006040518083038160008787f1925050503d8060008114612b8a576040519150601f19603f3d011682016040523d82523d6000602084013e612b8f565b606091505b509150915081612bcd57806040517f96c36235000000000000000000000000000000000000000000000000000000008152600401610f0a9190614b52565b80806020019051810190612be19190615cd8565b9950915081612c1c576040517f865676e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612c2b8b8d8c600061381f565b9050612c408582600001518360600151614557565b6060810151608082015160a083015160c0909301519b9e919d509b50909998509650505050505050565b6000818152600760209081526040808320815160e08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000928390048116848801908152600186015463ffffffff811686890181905267ffffffffffffffff64010000000083041660608881019182529287900485166080890181905260029099015495861660a089019081529690950490931660c087019081528b8b52600a9099529689208551915198519351945181548b9a8b998a998a998a998a9992989397929692959394939092908690612d599061556e565b80601f0160208091040260200160405190810160405280929190818152602001828054612d859061556e565b8015612dd25780601f10612da757610100808354040283529160200191612dd2565b820191906000526020600020905b815481529060010190602001808311612db557829003601f168201915b505050505095509850985098509850985098509850985050919395975091939597565b60008181526007602052604081206001015467ffffffffffffffff6401000000009091048116919082141590612e4060005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16149050818015612e905750808015612e8e5750438367ffffffffffffffff16115b155b15612ec7576040517ffbc0357800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015612f0c57506000848152600760205260409020600201546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314155b15612f43576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4381612f5757612f54603282615467565b90505b600085815260076020526040902060010180547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1664010000000067ffffffffffffffff841602179055612fac600586613e5c565b5060405167ffffffffffffffff82169086907f91cb3bb75cfbd718bbfccc56b7f53d92d7048ef4ca39a3b7b7c6d4af1f79118190600090a35050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff16331480159061302c575060135473ffffffffffffffffffffffffffffffffffffffff163314155b15613063576040517fd48b678b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61306e600143615450565b600d5460408051924060208401523060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690830152640100000000900460e01b7fffffffff000000000000000000000000000000000000000000000000000000001660548201526058016040516020818303038152906040528051906020012060001c905061313b81878787600088888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e6892505050565b600d8054640100000000900463ffffffff1690600461315983615d26565b91906101000a81548163ffffffff021916908363ffffffff16021790555050807fbae366358c023f887e791d7a62f2e4316f1026bd77f6fb49501a917b3bc5d01286866040516131d192919063ffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b60405180910390a295945050505050565b73ffffffffffffffffffffffffffffffffffffffff828116600090815260086020526040902054163314613242576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821603613291576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600960205260409020548116908216146111b35773ffffffffffffffffffffffffffffffffffffffff82811660008181526009602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055513392917f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836791a45050565b6133486136bd565b600d5460e082015163ffffffff91821691161015613392576040517f39abc10400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051806101200160405280826000015163ffffffff168152602001826020015163ffffffff168152602001826040015162ffffff168152602001826060015163ffffffff168152602001826080015162ffffff1681526020018260a0015161ffff1681526020018260c001516bffffffffffffffffffffffff1681526020018260e0015163ffffffff168152602001600c60010160049054906101000a900463ffffffff1663ffffffff16815250600c60008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548162ffffff021916908362ffffff160217905550606082015181600001600b6101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600f6101000a81548162ffffff021916908362ffffff16021790555060a08201518160000160126101000a81548161ffff021916908361ffff16021790555060c08201518160000160146101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060e08201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160010160046101000a81548163ffffffff021916908363ffffffff160217905550905050806101000151600e81905550806101200151600f81905550806101400151601260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806101600151601360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507ffe125a41957477226ba20f85ef30a4024ea3bb8d066521ddc16df3f2944de325816040516136819190615d49565b60405180910390a150565b6136946136bd565b61369d81614670565b50565b60006107cf825490565b60006136b68383614765565b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610b0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610f0a565b60035460ff166137aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610f0a565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6138756040518060e00160405280600073ffffffffffffffffffffffffffffffffffffffff1681526020016000815260200160608152602001600081526020016000815260200160008152602001600081525090565b60008481526007602052604081206001015463ffffffff169080613897614220565b9150915060006138a783876143fd565b905060006138b6858385614442565b6040805160e08101825273ffffffffffffffffffffffffffffffffffffffff8d168152602081018c90529081018a90526bffffffffffffffffffffffff909116606082015260808101959095525060a084015260c0830152509050949350505050565b60006002805403613986576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610f0a565b60028055602082810151600081815260079092526040909120600101544364010000000090910467ffffffffffffffff16116139ee576040517fd096219c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602080840151600090815260078252604090819020815160e08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c0100000000000000000000000092839004811696840196909652600184015463ffffffff81169584019590955267ffffffffffffffff640100000000860416606080850191909152948290048616608084015260029093015492831660a083015290910490921660c0830152845190850151613ab2918391614557565b60005a90506000634585e33b60e01b8660400151604051602401613ad69190614b52565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050613b48866080015184608001518361478f565b94505a613b559083615450565b91506000613b6c838860a001518960c00151614442565b602080890151600090815260079091526040902054909150613b9d9082906bffffffffffffffffffffffff166154e6565b6020888101805160009081526007909252604080832080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff95861617905590518252902060020154613c0091839116615513565b60208881018051600090815260078352604080822060020180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff9687161790558b5192518252808220805486166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff958616021790558b5190921681526008909252902054613cb991839174010000000000000000000000000000000000000000900416615513565b60086000896000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160146101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550866000015173ffffffffffffffffffffffffffffffffffffffff1686151588602001517fcaacad83e47cc45c280d487ec84184eee2fa3b54ebaa393bda7549f13da228f6848b60400151604051613d85929190615d58565b60405180910390a450505050506001600255919050565b60035460ff1615613e09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610f0a565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586137f53390565b60006136b683836147db565b60035460ff1615613ed5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610f0a565b73ffffffffffffffffffffffffffffffffffffffff85163b613f23576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108fc8463ffffffff161080613f445750600d5463ffffffff908116908516115b15613f7b576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518060e00160405280836bffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020018563ffffffff16815260200167ffffffffffffffff801681526020018673ffffffffffffffffffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff168152506007600088815260200190815260200160002060008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160006101000a81548163ffffffff021916908363ffffffff16021790555060608201518160010160046101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550608082015181600101600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a08201518160020160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060c082015181600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550905050816bffffffffffffffffffffffff166011546141e99190615467565b6011556000868152600a60209081526040909120825161420b928401906149e9565b506142176005876148ce565b50505050505050565b6000806000600c600001600f9054906101000a900462ffffff1662ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156142bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e09190615d99565b50945090925084915050801561430457506142fb8242615450565b8463ffffffff16105b80614310575060008113155b1561431f57600e549550614323565b8095505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561438e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143b29190615d99565b5094509092508491505080156143d657506143cd8242615450565b8463ffffffff16105b806143e2575060008113155b156143f157600f5494506143f5565b8094505b505050509091565b600c54600090614427907201000000000000000000000000000000000000900461ffff1684615de9565b90508180156144355750803a105b156107cf57503a92915050565b6000806144526201388086615467565b61445c9085615de9565b600c549091506000906144799063ffffffff16633b9aca00615467565b600c5490915060009061449f90640100000000900463ffffffff1664e8d4a51000615de9565b85836144af86633b9aca00615de9565b6144b99190615de9565b6144c39190615e26565b6144cd9190615467565b90506b033b2e3c9fd0803ce8000000811115614515576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9695505050505050565b3215610b0e576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526008602052604090206001015460ff166145b9576040517fcfbacfd800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82516bffffffffffffffffffffffff16811115614602576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16836020015173ffffffffffffffffffffffffffffffffffffffff160361466b576040517f06bc104000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036146ef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610f0a565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600082600001828154811061477c5761477c61547f565b9060005260206000200154905092915050565b60005a6113888110156147a157600080fd5b6113888103905084604082048203116147b957600080fd5b50823b6147c557600080fd5b60008083516020850160008789f1949350505050565b600081815260018301602052604081205480156148c45760006147ff600183615450565b855490915060009061481390600190615450565b90508181146148785760008660000182815481106148335761483361547f565b90600052602060002001549050808760000184815481106148565761485661547f565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061488957614889615e61565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506107cf565b60009150506107cf565b60008181526001830160205260408120546136b69084908490849061491f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556107cf565b5060006107cf565b5080546149339061556e565b6000825580601f10614943575050565b601f01602090049060005260206000209081019061369d9190614a5d565b8280548282559060005260206000209081019282156149d9579160200282015b828111156149d95781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190614981565b506149e5929150614a5d565b5090565b8280546149f59061556e565b90600052602060002090601f016020900481019282614a1757600085556149d9565b82601f10614a3057805160ff19168380011785556149d9565b828001600101855582156149d9579182015b828111156149d9578251825591602001919060010190614a42565b5b808211156149e55760008155600101614a5e565b60008060408385031215614a8557600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015614acc57835183529284019291840191600101614ab0565b50909695505050505050565b60005b83811015614af3578181015183820152602001614adb565b83811115614b02576000848401525b50505050565b60008151808452614b20816020860160208601614ad8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006136b66020830184614b08565b805163ffffffff1682526020810151614b86602084018263ffffffff169052565b506040810151614b9d604084018262ffffff169052565b506060810151614bb5606084018263ffffffff169052565b506080810151614bcc608084018262ffffff169052565b5060a0810151614be260a084018261ffff169052565b5060c0810151614c0260c08401826bffffffffffffffffffffffff169052565b5060e0810151614c1a60e084018263ffffffff169052565b50610100818101519083015261012080820151908301526101408082015173ffffffffffffffffffffffffffffffffffffffff81168285015250506101608181015173ffffffffffffffffffffffffffffffffffffffff811684830152614b02565b600061022080830163ffffffff875116845260206bffffffffffffffffffffffff8189015116818601526040880151604086015260608801516060860152614cc76080860188614b65565b6102008501929092528451908190526102408401918086019160005b81811015614d1557835173ffffffffffffffffffffffffffffffffffffffff1685529382019392820192600101614ce3565b509298975050505050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614d4757600080fd5b919050565b60008060408385031215614d5f57600080fd5b614d6883614d23565b9150602083013560048110614d7c57600080fd5b809150509250929050565b600060208284031215614d9957600080fd5b6136b682614d23565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60018110614de157614de1614da2565b9052565b602081016107cf8284614dd1565b60008060408385031215614e0657600080fd5b82359150614e1660208401614d23565b90509250929050565b60008083601f840112614e3157600080fd5b50813567ffffffffffffffff811115614e4957600080fd5b602083019150836020828501011115614e6157600080fd5b9250929050565b600080600060408486031215614e7d57600080fd5b83359250602084013567ffffffffffffffff811115614e9b57600080fd5b614ea786828701614e1f565b9497909650939450505050565b60008083601f840112614ec657600080fd5b50813567ffffffffffffffff811115614ede57600080fd5b6020830191508360208260051b8501011115614e6157600080fd5b600080600060408486031215614f0e57600080fd5b833567ffffffffffffffff811115614f2557600080fd5b614f3186828701614eb4565b9094509250614f44905060208501614d23565b90509250925092565b60008060208385031215614f6057600080fd5b823567ffffffffffffffff811115614f7757600080fd5b614f8385828601614e1f565b90969095509350505050565b600060208284031215614fa157600080fd5b5035919050565b80356bffffffffffffffffffffffff81168114614d4757600080fd5b60008060408385031215614fd757600080fd5b82359150614e1660208401614fa8565b60008060008060608587031215614ffd57600080fd5b61500685614d23565b935060208501359250604085013567ffffffffffffffff81111561502957600080fd5b61503587828801614e1f565b95989497509550505050565b6000806040838503121561505457600080fd5b61505d83614d23565b9150614e1660208401614d23565b803563ffffffff81168114614d4757600080fd5b6000806040838503121561509257600080fd5b82359150614e166020840161506b565b600080600080604085870312156150b857600080fd5b843567ffffffffffffffff808211156150d057600080fd5b6150dc88838901614eb4565b909650945060208701359150808211156150f557600080fd5b5061503587828801614eb4565b60a08152600061511560a0830188614b08565b90508560208301528460408301528360608301528260808301529695505050505050565b600061010073ffffffffffffffffffffffffffffffffffffffff808c16845263ffffffff8b1660208501528160408501526151768285018b614b08565b6bffffffffffffffffffffffff998a16606086015297811660808501529590951660a08301525067ffffffffffffffff9290921660c083015290931660e090930192909252949350505050565b6000806000806000608086880312156151db57600080fd5b6151e486614d23565b94506151f26020870161506b565b935061520060408701614d23565b9250606086013567ffffffffffffffff81111561521c57600080fd5b61522888828901614e1f565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610180810167ffffffffffffffff8111828210171561528c5761528c615239565b60405290565b60405160e0810167ffffffffffffffff8111828210171561528c5761528c615239565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156152fc576152fc615239565b604052919050565b803562ffffff81168114614d4757600080fd5b803561ffff81168114614d4757600080fd5b6000610180828403121561533c57600080fd5b615344615268565b61534d8361506b565b815261535b6020840161506b565b602082015261536c60408401615304565b604082015261537d6060840161506b565b606082015261538e60808401615304565b608082015261539f60a08401615317565b60a08201526153b060c08401614fa8565b60c08201526153c160e0840161506b565b60e0820152610100838101359082015261012080840135908201526101406153ea818501614d23565b908201526101606153fc848201614d23565b908201529392505050565b602081016004831061541b5761541b614da2565b91905290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561546257615462615421565b500390565b6000821982111561547a5761547a615421565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036154df576154df615421565b5060010190565b60006bffffffffffffffffffffffff8381169083168181101561550b5761550b615421565b039392505050565b60006bffffffffffffffffffffffff80831681851680830382111561553a5761553a615421565b01949350505050565b80518015158114614d4757600080fd5b60006020828403121561556557600080fd5b6136b682615543565b600181811c9082168061558257607f821691505b6020821081036155bb577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600081518084526020808501808196508360051b8101915082860160005b858110156156095782840389526155f7848351614b08565b988501989350908401906001016155df565b5091979650505050505050565b60006060808352858184015260807f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87111561565157600080fd5b8660051b808983870137808501905081810160008152602083878403018188015281895180845260a093508385019150828b01945060005b8181101561574057855180516bffffffffffffffffffffffff1684528481015173ffffffffffffffffffffffffffffffffffffffff9081168686015260408083015163ffffffff16908601528982015167ffffffffffffffff168a8601528882015116888501528581015161570d878601826bffffffffffffffffffffffff169052565b5060c09081015173ffffffffffffffffffffffffffffffffffffffff16908401529483019460e090920191600101615689565b50508781036040890152615754818a6155c1565b9c9b505050505050505050505050565b60006020828403121561577657600080fd5b8151600181106136b657600080fd5b61578f8185614dd1565b61579c6020820184614dd1565b606060408201526000611bb66060830184614b08565b600067ffffffffffffffff8211156157cc576157cc615239565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261580957600080fd5b815161581c615817826157b2565b6152b5565b81815284602083860101111561583157600080fd5b611050826020830160208701614ad8565b60006020828403121561585457600080fd5b815167ffffffffffffffff81111561586b57600080fd5b611050848285016157f8565b600067ffffffffffffffff82111561589157615891615239565b5060051b60200190565b600082601f8301126158ac57600080fd5b813560206158bc61581783615877565b82815260e092830285018201928282019190878511156158db57600080fd5b8387015b8581101561598b5781818a0312156158f75760008081fd5b6158ff615292565b61590882614fa8565b8152615915868301614d23565b86820152604061592681840161506b565b9082015260608281013567ffffffffffffffff811681146159475760008081fd5b908201526080615958838201614d23565b9082015260a0615969838201614fa8565b9082015260c061597a838201614d23565b9082015284529284019281016158df565b5090979650505050505050565b600082601f8301126159a957600080fd5b813560206159b961581783615877565b82815260059290921b840181019181810190868411156159d857600080fd5b8286015b84811015615a5757803567ffffffffffffffff8111156159fc5760008081fd5b8701603f81018913615a0e5760008081fd5b848101356040615a20615817836157b2565b8281528b82848601011115615a355760008081fd5b82828501898301376000928101880192909252508452509183019183016159dc565b509695505050505050565b600080600060608486031215615a7757600080fd5b833567ffffffffffffffff80821115615a8f57600080fd5b818601915086601f830112615aa357600080fd5b81356020615ab361581783615877565b82815260059290921b8401810191818101908a841115615ad257600080fd5b948201945b83861015615af057853582529482019490820190615ad7565b97505087013592505080821115615b0657600080fd5b615b128783880161589b565b93506040860135915080821115615b2857600080fd5b50615b3586828701615998565b9150509250925092565b600060208284031215615b5157600080fd5b5051919050565b8183526000602080850194508260005b85811015615ba15773ffffffffffffffffffffffffffffffffffffffff615b8e83614d23565b1687529582019590820190600101615b68565b509495945050505050565b604081526000615bc0604083018688615b58565b8281036020840152615bd3818587615b58565b979650505050505050565b600060208083526000845481600182811c915080831680615c0057607f831692505b8583108103615c36577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b878601838152602001818015615c535760018114615c8257615cad565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861682528782019650615cad565b60008b81526020902060005b86811015615ca757815484820152908501908901615c8e565b83019750505b50949998505050505050505050565b60008251615cce818460208701614ad8565b9190910192915050565b60008060408385031215615ceb57600080fd5b615cf483615543565b9150602083015167ffffffffffffffff811115615d1057600080fd5b615d1c858286016157f8565b9150509250929050565b600063ffffffff808316818103615d3f57615d3f615421565b6001019392505050565b61018081016107cf8284614b65565b6bffffffffffffffffffffffff831681526040602082015260006110506040830184614b08565b805169ffffffffffffffffffff81168114614d4757600080fd5b600080600080600060a08688031215615db157600080fd5b615dba86615d7f565b9450602086015193506040860151925060608601519150615ddd60808701615d7f565b90509295509295909350565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615e2157615e21615421565b500290565b600082615e5c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c634300080d000a000000000000000000000000fafedb041c0dd4fa2dc0d87a6b0979ee6fa7af5f000000000000000000000000f549af21578cfe2385ffd3488b3039fd9e52f006000000000000000000000000409cf388dab66275da3e44005d182c12eeaa12a0000000000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000632ea00000000000000000000000000000000000000000000000000000000000015f900000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c4b400000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000a59aa03de50a4383d90e1a4bb912a8dd795d1292000000000000000000000000db8e8e2ccb5c033938736aa89fe4fa1edfd15a1d
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000fafedb041c0dd4fa2dc0d87a6b0979ee6fa7af5f000000000000000000000000f549af21578cfe2385ffd3488b3039fd9e52f006000000000000000000000000409cf388dab66275da3e44005d182c12eeaa12a0000000000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000632ea00000000000000000000000000000000000000000000000000000000000015f900000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c4b400000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000a59aa03de50a4383d90e1a4bb912a8dd795d1292000000000000000000000000db8e8e2ccb5c033938736aa89fe4fa1edfd15a1d
-----Decoded View---------------
Arg [0] : link (address): 0xfafedb041c0dd4fa2dc0d87a6b0979ee6fa7af5f
Arg [1] : linkEthFeed (address): 0xf549af21578cfe2385ffd3488b3039fd9e52f006
Arg [2] : fastGasFeed (address): 0x409cf388dab66275da3e44005d182c12eeaa12a0
Arg [3] : config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
15 Constructor Arguments found :
Arg [0] : 000000000000000000000000fafedb041c0dd4fa2dc0d87a6b0979ee6fa7af5f
Arg [1] : 000000000000000000000000f549af21578cfe2385ffd3488b3039fd9e52f006
Arg [2] : 000000000000000000000000409cf388dab66275da3e44005d182c12eeaa12a0
Arg [3] : 000000000000000000000000000000000000000000000000000000001dcd6500
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000c8
Arg [6] : 0000000000000000000000000000000000000000000000000000000000632ea0
Arg [7] : 0000000000000000000000000000000000000000000000000000000000015f90
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 00000000000000000000000000000000000000000000000000000000004c4b40
Arg [11] : 0000000000000000000000000000000000000000000000000000015d3ef79800
Arg [12] : 0000000000000000000000000000000000000000000000008ac7230489e80000
Arg [13] : 000000000000000000000000a59aa03de50a4383d90e1a4bb912a8dd795d1292
Arg [14] : 000000000000000000000000db8e8e2ccb5c033938736aa89fe4fa1edfd15a1d
Deployed ByteCode Sourcemap
912:31620:14:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19161:465;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;3229:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;20041:1031::-;;;:::i;:::-;;;;;;;;;:::i;22116:172::-;;;;;;:::i;:::-;;:::i;:::-;;2612:40;;;;;;;;5704:42:105;5692:55;;;5674:74;;5662:2;5647:18;2612:40:14;5500:254:105;19702:271:14;;;;;;:::i;:::-;19891:19;;;;19795:13;19891:19;;;:12;:19;;;;;;;;;19864:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19702:271;;;;;6174:42:105;6162:55;;;6144:74;;6261:14;;6254:22;6249:2;6234:18;;6227:50;6325:26;6313:39;6293:18;;;6286:67;6132:2;6117:18;19702:271:14;5950:409:105;15959:59:14;;;:::i;2712:52::-;;;;;24067:70;;24122:15;24067:70;;;;;;;;;:::i;1098:84:1:-;1168:7;;;;1098:84;;;7347:14:105;;7340:22;7322:41;;7310:2;7295:18;1098:84:1;7182:187:105;11736:970:14;;;;;;:::i;:::-;;:::i;1016:265:9:-;;;:::i;9282:237:14:-;;;;;;:::i;:::-;;:::i;12792:261::-;;;:::i;15785:55::-;;;:::i;22355:1645::-;;;;;;:::i;:::-;;:::i;1332:81:9:-;1379:7;1401;;;1332:81;;24205:782:14;;;;;;:::i;:::-;;:::i;21520:280::-;;;;;;:::i;:::-;;:::i;:::-;;;10359:26:105;10347:39;;;10329:58;;10317:2;10302:18;21520:280:14;10185:208:105;10434:310:14;;;;;;:::i;:::-;;:::i;11021:539::-;;;;;;:::i;:::-;;:::i;14178:411::-;;;;;;:::i;:::-;;:::i;13235:309::-;;;;;;:::i;:::-;;:::i;2656:52::-;;;;;15324:334;;;;;;:::i;:::-;;:::i;21236:148::-;;;;;;:::i;:::-;;:::i;13844:161::-;;;:::i;17215:961::-;;;;;;:::i;:::-;;:::i;8067:911::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;:::i;18259:530::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;:::i;9638:607::-;;;;;;:::i;:::-;;:::i;7204:439::-;;;;;;:::i;:::-;;:::i;:::-;;;15085:25:105;;;15073:2;15058:18;7204:439:14;14939:177:105;14808:378:14;;;;;;:::i;:::-;;:::i;16145:808::-;;;;;;:::i;:::-;;:::i;826:98:9:-;;;;;;:::i;:::-;;:::i;21884:159:14:-;;;;;;:::i;:::-;21999:39;;21965:19;21999:39;;;:33;:39;;;;;;;;;21884:159;;;;;;;;:::i;19161:465::-;19259:16;19283:14;19300:20;:11;:18;:20::i;:::-;19283:37;;19344:6;19330:10;:20;19326:50;;19359:17;;;;;;;;;;;;;;19326:50;19386:8;19398:1;19386:13;19382:64;;19420:19;19429:10;19420:6;:19;:::i;:::-;19409:30;;19382:64;19451:20;19488:8;19474:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19474:23:14;;19451:46;;19508:11;19503:103;19531:8;19525:3;:14;19503:103;;;19567:32;19582:16;19595:3;19582:10;:16;:::i;:::-;19567:11;;:14;:32::i;:::-;19556:3;19560;19556:8;;;;;;;;:::i;:::-;;;;;;;;;;:43;19541:5;;;;:::i;:::-;;;;19503:103;;;-1:-1:-1;19618:3:14;-1:-1:-1;;19161:465:14;;;;;:::o;20041:1031::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20209:32:14;;;;;;;;20232:9;20209:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20170:24;20209:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20247:25;;20303:18;;;;;20278:22;;;:43;20355:21;;20327:25;;;:49;;;;20401:20;:11;:18;:20::i;:::-;20382:16;;;;:39;;;;20454:23;;20427:50;;;;;;20509:22;;;;;20483:48;;:23;;;:48;20564:23;;;;;20537:50;;;;:24;;;:50;20616:19;;;;20593:42;;:20;;;:42;;;;20667:22;;;;;20641:48;;;:23;;;:48;;;;20725:26;;;;;20695:56;;:27;;;:56;20781:20;;;;;20757:44;;:21;;;:44;20830:19;;;;;20807:42;;;:20;;;:42;;;;20881:18;;20855:23;;;:44;20932:19;;20905:24;;;:46;20977:12;;;;;;20957:17;;;:32;21014:11;;;20995:16;;;:30;21054:12;21031:36;;;;;;;;;;;;;;;;;20382:5;;20427:6;;21031:36;;21054:12;;21031:36;;21054:12;21031:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20041:1031;;;:::o;22116:172::-;1956:20:9;:18;:20::i;:::-;22231:39:14::1;::::0;::::1;;::::0;;;:33:::1;:39;::::0;;;;:52;;22273:10;;22231:39;:52;::::1;::::0;22273:10;22231:52:::1;::::0;::::1;;;;;;:::i;:::-;;;;;;22116:172:::0;;:::o;15959:59::-;1956:20:9;:18;:20::i;:::-;16003:10:14::1;:8;:10::i;:::-;15959:59::o:0;11736:970::-;11807:2;32230:18;;;32226:49;;32257:18;;;;;;;;;;;;;;32226:49;31819:12:::1;::::0;;;:8:::1;:12;::::0;;;;:18:::1;;::::0;:12;;:18;;::::1;;;31805:10;:32;31801:66;;31846:21;;;;;;;;;;;;;;31801:66;11841:12:::2;::::0;;;:8:::2;:12;::::0;;;;:32:::2;;::::0;11876:12:::2;11841:32:::0;;;::::2;;;:47;11837:79;;;11897:19;;;;;;;;;;;;;;11837:79;11947:9;:24:::0;11923:21:::2;11997:12:::0;;;:8:::2;:12;::::0;;;;:20;;12044:24:::2;::::0;;::::2;::::0;11947::::2;::::0;;;::::2;::::0;::::2;::::0;11997:20;;::::2;::::0;12044:24;::::2;::::0;12205:28;;::::2;12201:183;;;12261:28;12278:11:::0;12261:14;:28:::2;:::i;:::-;12243:46;;12319:10;12301:28;;:15;:28;;;12297:81;;;-1:-1:-1::0;12359:10:14;12297:81:::2;12389:23;12415:28;12428:15:::0;12415:10;:28:::2;:::i;:::-;12473:1;12450:12:::0;;;:8:::2;:12;::::0;;;;:24;;;::::2;::::0;;12501:18:::2;::::0;12389:54;;-1:-1:-1;12501:36:14::2;::::0;12522:15;;12450:24:::2;12501:18;:36;:::i;:::-;12480:18;:57:::0;;;::::2;;::::0;;::::2;;::::0;;12568:21:::2;::::0;:40:::2;::::0;;::::2;::::0;::::2;:::i;:::-;12544:21;:64:::0;12619:40:::2;::::0;;19436:26:105;19424:39;;19406:58;;19512:42;19500:55;;19495:2;19480:18;;19473:83;12634:2:14;;12619:40:::2;::::0;19379:18:105;12619:40:14::2;;;;;;;12666:35;::::0;;;;:13:::2;19758:55:105::0;;;12666:35:14::2;::::0;::::2;19740:74:105::0;19862:26;19850:39;;19830:18;;;19823:67;12666:4:14::2;:13;::::0;::::2;::::0;19713:18:105;;12666:35:14::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;11831:875;;;;;32281:1:::1;11736:970:::0;;;:::o;1016:265:9:-;1089:14;;;;1075:10;:28;1067:63;;;;;;;20479:2:105;1067:63:9;;;20461:21:105;20518:2;20498:18;;;20491:30;20557:24;20537:18;;;20530:52;20599:18;;1067:63:9;;;;;;;;;1137:16;1156:7;;1179:10;1169:20;;;;;;;;-1:-1:-1;1195:27:9;;;;;;;1234:42;;1156:7;;;;;1179:10;;1156:7;;1234:42;;;1061:220;1016:265::o;9282:237:14:-;9402:12;1412:8:1;1168:7;;;;;1098:84;1412:8;1411:9;1403:38;;;;;;;20830:2:105;1403:38:1;;;20812:21:105;20869:2;20849:18;;;20842:30;20908:18;20888;;;20881:46;20944:18;;1403:38:1;20628:340:105;1403:38:1;9431:83:14::1;9456:57;9479:10;9491:2;9495:11;;9456:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;;-1:-1:-1;9508:4:14::1;::::0;-1:-1:-1;9456:22:14::1;::::0;-1:-1:-1;;9456:57:14:i:1;:::-;9431:24;:83::i;:::-;9424:90:::0;9282:237;-1:-1:-1;;;;9282:237:14:o;12792:261::-;1956:20:9;:18;:20::i;:::-;12863:18:14::1;::::0;12912:21:::1;::::0;12863:18:::1;::::0;;::::1;::::0;12912:30:::1;::::0;12863:18;;12912:30:::1;:::i;:::-;12888:21;:54:::0;12948:18:::1;:22:::0;;;::::1;::::0;;12982:27:::1;::::0;10359:26:105;10347:39;;10329:58;;12982:27:14::1;::::0;10317:2:105;10302:18;12982:27:14::1;;;;;;;13015:33;::::0;;;;13029:10:::1;13015:33;::::0;::::1;19740:74:105::0;19862:26;19850:39;;19830:18;;;19823:67;13015:4:14::1;:13;;::::0;::::1;::::0;19713:18:105;;13015:33:14::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;12841:212;12792:261::o:0;15785:55::-;1956:20:9;:18;:20::i;:::-;15827:8:14::1;:6;:8::i;22355:1645::-:0;22509:28;22459:46;;;;;;;:33;:46;;;;;;;;:78;;;;;;;;:::i;:::-;;;:171;;;;-1:-1:-1;22597:33:14;22547:46;;;;;;;:33;:46;;;;;;;;:83;;;;;;;;:::i;:::-;;;22459:171;22448:219;;;22644:23;;;;;;;;;;;;;;22448:219;22677:12;;:28;:12;22673:59;;22714:18;;;;;;;;;;;;;;22673:59;22756:1;22742:15;;;22738:47;;22766:19;;;;;;;;;;;;;;22738:47;-1:-1:-1;;;;;;;;22791:10:14;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22833:29:14;;22908:3;22896:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;22868:51:14;-1:-1:-1;22925:23:14;22964:3;22951:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22951:24:14;;;;;;;;;;;;;;;22925:50;;22986:11;22981:537;23003:16;;;22981:537;;;23041:3;;23045;23041:8;;;;;;;:::i;:::-;;;;;;;;;;23066:12;;;;:8;:12;;;;;;;23057:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23041:8;;-1:-1:-1;23057:21:14;;-1:-1:-1;;23106:10:14;23090:26;23086:60;;23125:21;;;;;;;;;;;;;;23086:60;23158:26;;;;1848:9;23158:40;;;;23154:70;;23207:17;;;;;;;;;;;;;;23154:70;23247:6;23232:7;23240:3;23232:12;;;;;;;;:::i;:::-;;;;;;:21;;;;23279:11;:15;23291:2;23279:15;;;;;;;;;;;23261:33;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:10;23272:3;23261:15;;;;;;;;:::i;:::-;;;;;;;;;;:33;23350:14;;23326:38;;;;:21;:38;:::i;:::-;23379:12;;;;:8;:12;;;;;;;;23372:19;;;;;;;;;;;;;;23406:11;:15;;;;;23302:62;;-1:-1:-1;23399:22:14;;23406:15;23399:22;:::i;:::-;23429;:11;23448:2;23429:18;:22::i;:::-;-1:-1:-1;23483:14:14;;23464:47;;;19436:26:105;19424:39;;;19406:58;;19512:42;19500:55;;19495:2;19480:18;;19473:83;23479:2:14;;23464:47;;19379:18:105;23464:47:14;;;;;;;23021:5;;;;:::i;:::-;;;;22981:537;;;;23571:21;23547;;:45;;;;:::i;:::-;23523:21;:69;23628:36;;23598:27;;23628:36;;23639:3;;;;23644:7;;23653:10;;23628:36;;;:::i;:::-;;;;;;;;;;;;;23598:66;;23704:11;23670:61;;;23765:12;;;;;;;;;;;23739:56;;;23805:15;23864:11;23830:70;;;:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;23912:14;23739:195;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;23670:270;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;23946:49:14;;;;;:13;26308:55:105;;;23946:49:14;;;26290:74:105;26380:18;;;26373:34;;;23946:4:14;:13;;-1:-1:-1;23946:13:14;;-1:-1:-1;26263:18:105;;23946:49:14;26116:297:105;24205:782:14;24344:28;24329:10;24295:45;;;;:33;:45;;;;;;;;:77;;;;;;;;:::i;:::-;;;:169;;;;-1:-1:-1;24431:33:14;24416:10;24382:45;;;;:33;:45;;;;;;;;:82;;;;;;;;:::i;:::-;;;24295:169;24284:217;;;24478:23;;;;;;;;;;;;;;24284:217;24508:20;;;24584:76;;;;24602:14;24584:76;:::i;:::-;24507:153;;;;;;24671:11;24666:317;24694:3;:10;24688:3;:16;24666:317;;;24721:184;24744:3;24748;24744:8;;;;;;;;:::i;:::-;;;;;;;24762:7;24770:3;24762:12;;;;;;;;:::i;:::-;;;;;;;:19;;;24791:7;24799:3;24791:12;;;;;;;;:::i;:::-;;;;;;;:23;;;24824:7;24832:3;24824:12;;;;;;;;:::i;:::-;;;;;;;:18;;;24852:7;24860:3;24852:12;;;;;;;;:::i;:::-;;;;;;;:20;;;24882:10;24893:3;24882:15;;;;;;;;:::i;:::-;;;;;;;24721:13;:184::i;:::-;24933:3;24937;24933:8;;;;;;;;:::i;:::-;;;;;;;24918:58;24943:7;24951:3;24943:12;;;;;;;;:::i;:::-;;;;;;;;;;;;:20;24918:58;;;19436:26:105;19424:39;;;19406:58;;24965:10:14;19480:18:105;;;19473:83;;;;19379:18;24918:58:14;;;;;;;24706:5;;;;:::i;:::-;;;;24666:317;;;;24278:709;;;24205:782;;:::o;21520:280::-;21588:17;21614:14;21630:15;21649:14;:12;:14::i;:::-;21613:50;;;;21669:22;21694:30;21710:6;21718:5;21694:15;:30::i;:::-;21669:55;;21737:58;21761:8;21771:14;21787:7;21737:23;:58::i;:::-;21730:65;21520:280;-1:-1:-1;;;;;21520:280:14:o;10434:310::-;31993:12;;;;:8;:12;;;;;:32;;;10514:2;;31993:32;;;1848:9;31993:32;;;:46;31989:76;;32048:17;;;;;;;;;;;;;;31989:76;10547:12:::1;::::0;;;:8:::1;:12;::::0;;;;:20;:29:::1;::::0;10570:6;;10547:20:::1;;:29;:::i;:::-;10524:12;::::0;;;:8:::1;:12;::::0;;;;:52;;;::::1;;::::0;;::::1;;::::0;;10606:21:::1;::::0;:30:::1;::::0;;::::1;::::0;::::1;:::i;:::-;10582:21;:54:::0;10642:52:::1;::::0;;;;10660:10:::1;10642:52;::::0;::::1;31355:34:105::0;10680:4:14::1;31405:18:105::0;;;31398:43;31489:26;31477:39;;31457:18;;;31450:67;10642:4:14::1;:17;;::::0;::::1;::::0;31267:18:105;;10642:52:14::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;10705:34:14::1;::::0;10359:26:105;10347:39;;10329:58;;10720:10:14::1;::::0;10716:2;;10705:34:::1;::::0;10317:2:105;10302:18;10705:34:14::1;;;;;;;10434:310:::0;;;:::o;11021:539::-;11134:10;:27;11156:4;11134:27;;11130:65;;11170:25;;;;;;;;;;;;;;11130:65;11220:2;11205:17;;11201:49;;11231:19;;;;;;;;;;;;;;11201:49;11256:10;11269:27;;;;11280:4;11269:27;:::i;:::-;11306:12;;;;:8;:12;;;;;:32;;;11256:40;;-1:-1:-1;11306:32:14;;;1848:9;11306:32;;;:46;11302:76;;11361:17;;;;;;;;;;;;;;11302:76;11408:12;;;;:8;:12;;;;;:20;:37;;11438:6;;11408:20;;:37;:::i;:::-;11385:12;;;;:8;:12;;;;;:60;;;;;;;;;;;;;;;11475:21;;:30;;11499:6;;11475:30;:::i;:::-;11451:21;:54;11517:38;;10359:26:105;10347:39;;10329:58;;11517:38:14;;;;11528:2;;11517:38;;10317:2:105;10302:18;11517:38:14;;;;;;;11124:436;11021:539;;;;:::o;14178:411::-;14253:2;32230:18;;;32226:49;;32257:18;;;;;;;;;;;;;;32226:49;14290:18:::1;::::0;;::::1;14263:24;14290:18:::0;;;:12:::1;:18;::::0;;;;;;;;14263:45;;::::1;::::0;::::1;::::0;;;;;;::::1;::::0;;;;;;::::1;;;::::0;;::::1;::::0;;;;;::::1;::::0;::::1;;;;::::0;;;;;;;;14334:10:::1;14318:26;14314:60;;14353:21;;;;;;;;;;;;;;14314:60;14381:18;::::0;;::::1;14410:1;14381:18:::0;;;:12:::1;:18;::::0;;;;;;;:30;;;;::::1;::::0;;;14465:14;::::1;::::0;14441:21:::1;::::0;:38:::1;::::0;14381:30:::1;14441:38;::::0;::::1;:::i;:::-;14417:21;:62:::0;14513:14:::1;::::0;;::::1;::::0;14490:54:::1;::::0;14533:10:::1;5674:74:105::0;;14490:54:14::1;::::0;;::::1;::::0;::::1;::::0;;::::1;::::0;;;::::1;::::0;::::1;::::0;5647:18:105;14490:54:14::1;;;;;;;14569:14;::::0;::::1;::::0;14551:33:::1;::::0;;;;:13:::1;19758:55:105::0;;;14551:33:14::1;::::0;::::1;19740:74:105::0;19862:26;19850:39;;;19830:18;;;19823:67;14551:4:14::1;:13:::0;;::::1;::::0;::::1;::::0;19713:18:105;;14551:33:14::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;14257:332;14178:411:::0;;;:::o;13235:309::-;31993:12;;;;:8;:12;;;;;:32;;;13326:2;;31993:32;;;1848:9;31993:32;;;:46;31989:76;;32048:17;;;;;;;;;;;;;;31989:76;31819:12:::1;::::0;;;:8:::1;:12;::::0;;;;:18:::1;;::::0;:12;;:18;;::::1;;;31805:10;:32;31801:66;;31846:21;;;;;;;;;;;;;;31801:66;1583:5:::2;13360:8;:26;;;:64;;;-1:-1:-1::0;13401:23:14;;::::2;::::0;;::::2;13390:34:::0;;::::2;;13360:64;13356:99;;;13433:22;;;;;;;;;;;;;;13356:99;13462:12;::::0;;;:8:::2;:12;::::0;;;;;;;;:23:::2;;:34:::0;;;::::2;;::::0;::::2;::::0;;::::2;::::0;;;13508:31;;31672:42:105;;;13462:12:14;;13508:31:::2;::::0;31645:18:105;13508:31:14::2;;;;;;;32071:1:::1;13235:309:::0;;;:::o;15324:334::-;15384:37;:23;;;;;;;:15;:23;;;;;;;15411:10;15384:37;15380:79;;15430:29;;;;;;;;;;;;;;15380:79;15480:20;;;;15465:12;15480:20;;;:12;:20;;;;;;;;:26;;15541:10;15512:39;;;;;;;;;15557:15;:23;;;;;;:38;;;;;;;;15607:46;;15480:26;;;15541:10;;15480:26;;:20;;15607:46;;15465:12;15607:46;15374:284;15324:334;:::o;21236:148::-;21303:17;21355:12;;;:8;:12;;;;;:23;;;21335:44;;21355:23;;21335:19;:44::i;13844:161::-;1956:20:9;:18;:20::i;:::-;13909:29:14::1;::::0;;;;13932:4:::1;13909:29;::::0;::::1;5674:74:105::0;13893:13:14::1;::::0;13909:4:::1;:14;;::::0;::::1;::::0;5647:18:105;;13909:29:14::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;13893:45;;13944:4;:13;;;13958:10;13978:21;;13970:5;:29;;;;:::i;:::-;13944:56;::::0;;::::1;::::0;;;;;;26320:42:105;26308:55;;;13944:56:14::1;::::0;::::1;26290:74:105::0;26380:18;;;26373:34;26263:18;;13944:56:14::1;26116:297:105::0;17215:961:14;1956:20:9;:18;:20::i;:::-;17319:31:14;;::::1;;::::0;:53:::1;;-1:-1:-1::0;17371:1:14::1;17354:18:::0;::::1;17319:53;17315:88;;;17381:22;;;;;;;;;;;;;;17315:88;17414:9;17409:140;17433:12;:19:::0;17429:23;::::1;17409:140;;;17467:14;17484:12;17497:1;17484:15;;;;;;;;:::i;:::-;;::::0;;;::::1;::::0;;;;;::::1;::::0;::::1;;17507:20:::0;;:12:::1;:20:::0;;;;;17484:15;17507:27:::1;:35:::0;;;::::1;::::0;;-1:-1:-1;17454:3:14;::::1;::::0;::::1;:::i;:::-;;;;17409:140;;;;17559:9;17554:548;17574:18:::0;;::::1;17554:548;;;17607:14;17624:7;;17632:1;17624:10;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;17672:20;::::0;;::::1;17642:27;17672:20:::0;;;:12:::1;:20;::::0;;;;17719:14;;17607:27;;-1:-1:-1;17672:20:14;17719:14;;::::1;::::0;17760:6;;17767:1;17760:9;;::::1;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;17741:28:::0;-1:-1:-1;17791:24:14::1;::::0;::::1;::::0;;17790:110:::1;;-1:-1:-1::0;17821:24:14::1;::::0;::::1;::::0;;::::1;::::0;:48:::1;;;17861:8;17849:20;;:8;:20;;;;17821:48;:78;;;;-1:-1:-1::0;1308:42:14::1;17873:26:::0;;::::1;;;17821:78;17777:153;;;17916:14;;;;;;;;;;;;;;17777:153;17942:15;::::0;::::1;::::0;::::1;;17938:44;;;17966:16;;;;;;;;;;;;;;17938:44;18008:4;17990:15:::0;;::::1;:22:::0;;;::::1;::::0;;::::1;::::0;;1308:42:::1;18024:26:::0;;::::1;;18020:76;;18062:25:::0;;;::::1;;::::0;::::1;;::::0;;18020:76:::1;17599:503;;;;17594:3;;;;;:::i;:::-;;;;17554:548;;;-1:-1:-1::0;18107:22:14::1;:12;18122:7:::0;;18107:22:::1;:::i;:::-;;18140:31;18155:7;;18164:6;;18140:31;;;;;;;;;:::i;:::-;;;;;;;;17215:961:::0;;;;:::o;8067:911::-;8178:24;8210:22;8240:16;8264:22;8294:15;536:18:11;:16;:18::i;:::-;8324:20:14::1;8347:12:::0;;;:8:::1;:12;::::0;;;;;;;8324:35;;::::1;::::0;::::1;::::0;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;::::1;::::0;;;::::1;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;::::1;;::::0;;;;;;::::1;::::0;::::1;::::0;;;;::::1;::::0;;::::1;::::0;;;::::1;::::0;;;;::::1;::::0;;::::1;::::0;;;;8429:15;;;:11:::1;:15:::0;;;;;;8390:55;;8324:35;;:20;1395:46;;8390:55:::1;::::0;::::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8366:79;;8452:12;8466:19:::0;8489:6:::1;:13;;;:18;;8513:9;:23;;;;;;;;;;;;8489:58;;8538:8;8489:58;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8451:96;;;;8559:7;8554:48;;8595:6;8575:27;;;;;;;;;;;:::i;8554:48::-;8645:6;8634:33;;;;;;;;;;;;:::i;:::-;8609:58:::0;-1:-1:-1;8609:58:14;-1:-1:-1;8609:58:14;8673:38:::1;;8694:17;;;;;;;;;;;;;;8673:38;8718:27;8748:52;8771:4;8777:2;8781:11;8794:5;8748:22;:52::i;:::-;8718:82;;8806:61;8824:6;8832;:11;;;8845:6;:21;;;8806:17;:61::i;:::-;8895:21;::::0;::::1;::::0;8918:15:::1;::::0;::::1;::::0;8935:21:::1;::::0;::::1;::::0;8958:14:::1;::::0;;::::1;::::0;8067:911;;8895:21;;-1:-1:-1;8918:15:14;-1:-1:-1;8935:21:14;;8958:14;-1:-1:-1;8067:911:14;-1:-1:-1;;;;;;;8067:911:14:o;18259:530::-;18345:14;18578:12;;;:8;:12;;;;;;;;18558:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18392:22;18558:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18651:15;;;:11;:15;;;;;;18674:11;;18693:14;;18715:9;;18732:23;;18763:15;;18596:188;;18345:14;;;;;;;;;;;;18558:32;;;;;;18651:15;;18674:11;;18693:14;18715:9;;18763:15;18651;;18596:188;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18259:530;;;;;;;;;:::o;9638:607::-;9696:15;9714:12;;;:8;:12;;;;;:32;;;;;;;;;;;9768:22;;;;;9825:7;1379::9;1401;;;;1332:81;9825:7:14;9811:21;;:10;:21;;;9796:36;;9843:8;:49;;;;;9857:7;:34;;;;;9879:12;9868:8;:23;;;9857:34;9855:37;9843:49;9839:76;;;9901:14;;;;;;;;;;;;;;9839:76;9926:7;9925:8;:44;;;;-1:-1:-1;9951:12:14;;;;:8;:12;;;;;:18;;;;;;;;9937:10;:32;;9925:44;9921:85;;;9978:28;;;;;;;;;;;;;;9921:85;10030:12;10053:7;10048:64;;10079:26;1637:2;10079:6;:26;:::i;:::-;10070:35;;10048:64;10117:12;;;;:8;:12;;;;;:32;;:49;;;;;;;;;;;;10172:22;:11;10117:12;10172:18;:22::i;:::-;-1:-1:-1;10206:34:14;;;;;;10221:2;;10206:34;;;;;9690:555;;;;9638:607;:::o;7204:439::-;7370:10;1401:7:9;;;;32427:10:14;:21;;;;:50;;-1:-1:-1;32466:11:14;;;;32452:10;:25;;32427:50;32423:95;;;32486:32;;;;;;;;;;;;;;32423:95;7438:16:::1;7453:1;7438:12;:16;:::i;:::-;7472:15:::0;;7411:77:::1;::::0;;7428:27;::::1;7411:77;::::0;::::1;35233:19:105::0;7465:4:14::1;35290:2:105::0;35286:15;35303:66;35282:88;35268:12;;;35261:110;7472:15:14;;::::1;35409:3:105::0;35405:16;;;35387:12;;;35380:111;35507:12;;7411:77:14::1;;;;;;;;;;;;7401:88;;;;;;7393:97;;7388:102;;7496:56;7510:2;7514:6;7522:8;7532:5;7539:1;7542:9;;7496:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;;-1:-1:-1;7496:13:14::1;::::0;-1:-1:-1;;;7496:56:14:i:1;:::-;7558:15:::0;:17;;;;::::1;;;::::0;:15:::1;:17;::::0;::::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;7603:2;7586:37;7607:8;7617:5;7586:37;;;;;;35938:10:105::0;35926:23;;;;35908:42;;35998;35986:55;35981:2;35966:18;;35959:83;35896:2;35881:18;;35736:312;7586:37:14::1;;;;;;;;7204:439:::0;;;;;;;:::o;14808:378::-;14888:40;:20;;;;;;;:12;:20;;;;;:26;;14918:10;14888:40;14884:74;;14937:21;;;;;;;;;;;;;;14884:74;14980:10;14968:22;;;;14964:52;;14999:17;;;;;;;;;;;;;;14964:52;15027:35;:23;;;;;;;:15;:23;;;;;;;;:35;;;;15023:159;;15072:23;;;;;;;;:15;:23;;;;;;:34;;;;;;;;;;;;15119:56;15154:10;;15072:23;15119:56;;;14808:378;;:::o;16145:808::-;1956:20:9;:18;:20::i;:::-;16236:23:14;;16213:20:::1;::::0;::::1;::::0;16236:23:::1;::::0;;::::1;16213:46:::0;::::1;;16209:84;;;16268:25;;;;;;;;;;;;;;16209:84;16311:433;;;;;;;;16346:6;:24;;;16311:433;;;;;;16396:6;:23;;;16311:433;;;;;;16446:6;:24;;;16311:433;;;;;;16493:6;:20;;;16311:433;;;;;;16539:6;:23;;;16311:433;;;;;;16592:6;:27;;;16311:433;;;;;;16643:6;:21;;;16311:433;;;;;;16687:6;:20;;;16311:433;;;;;;16722:9;:15;;;;;;;;;;;;16311:433;;;;::::0;16299:9:::1;:445;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16771:6;:23;;;16750:18;:44;;;;16822:6;:24;;;16800:19;:46;;;;16867:6;:17;;;16852:12;;:32;;;;;;;;;;;;;;;;;;16904:6;:16;;;16890:11;;:30;;;;;;;;;;;;;;;;;;16931:17;16941:6;16931:17;;;;;;:::i;:::-;;;;;;;;16145:808:::0;:::o;826:98:9:-;1956:20;:18;:20::i;:::-;897:22:::1;916:2;897:18;:22::i;:::-;826:98:::0;:::o;10795:112:5:-;10855:7;10881:19;10889:3;4028:18;;3946:107;11249:135;11320:7;11354:22;11358:3;11370:5;11354:3;:22::i;:::-;11346:31;11249:135;-1:-1:-1;;;11249:135:5:o;1730:111:9:-;1802:7;;;;1788:10;:21;1780:56;;;;;;;36506:2:105;1780:56:9;;;36488:21:105;36545:2;36525:18;;;36518:30;36584:24;36564:18;;;36557:52;36626:18;;1780:56:9;36304:346:105;2110:117:1;1168:7;;;;1669:41;;;;;;;36857:2:105;1669:41:1;;;36839:21:105;36896:2;36876:18;;;36869:30;36935:22;36915:18;;;36908:50;36975:18;;1669:41:1;36655:344:105;1669:41:1;2168:7:::1;:15:::0;;;::::1;::::0;;2198:22:::1;719:10:4::0;2207:12:1::1;2198:22;::::0;5704:42:105;5692:55;;;5674:74;;5662:2;5647:18;2198:22:1::1;;;;;;;2110:117::o:0;30791:681:14:-;30937:20;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30937:20:14;30965:16;30984:12;;;:8;:12;;;;;:23;;;;;;30965:16;31049:14;:12;:14::i;:::-;31013:50;;;;31069:22;31094:38;31110:6;31118:13;31094:15;:38::i;:::-;31069:63;;31138:21;31162:58;31186:8;31196:14;31212:7;31162:23;:58::i;:::-;31240:227;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;31240:227:14;;;;;;;;-1:-1:-1;31240:227:14;-1:-1:-1;30791:681:14;;;;;;:::o;28885:996::-;29017:12;1744:1:2;2325:7;;:19;2317:63;;;;;;;37206:2:105;2317:63:2;;;37188:21:105;37245:2;37225:18;;;37218:30;37284:33;37264:18;;;37257:61;37335:18;;2317:63:2;37004:355:105;2317:63:2;1744:1;2455:18;;28993:9:14::1;::::0;;::::1;::::0;31583:12:::1;::::0;;;:8:::1;:12:::0;;;;;;;:32:::1;;::::0;31619:12:::1;31583:32:::0;;;::::1;;;:48;31579:78;;31640:17;;;;;;;;;;;;;;31579:78;29071:9:::2;::::0;;::::2;::::0;29039:20:::2;29062:19:::0;;;:8:::2;:19:::0;;;;;;;29039:42;;::::2;::::0;::::2;::::0;;;;::::2;::::0;;::::2;::::0;;::::2;::::0;;;;::::2;::::0;::::2;::::0;;::::2;::::0;;;;;;::::2;::::0;::::2;::::0;::::2;::::0;;;;;;;::::2;::::0;;::::2;;::::0;;;;;;;;;;;::::2;::::0;::::2;::::0;;;;::::2;::::0;;::::2;::::0;;;::::2;::::0;;;;;;::::2;::::0;;::::2;::::0;;;;29113:11;;29126:21;;::::2;::::0;29087:61:::2;::::0;29039:42;;29087:17:::2;:61::i;:::-;29155:15;29173:9;29155:27;;29188:21;1488:48;;;29253:6;:18;;;29212:60;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;29188:84;;29288:59;29306:6;:15;;;29323:6;:13;;;29338:8;29288:17;:59::i;:::-;29278:69;;29373:9;29363:19;::::0;:7;:19:::2;:::i;:::-;29353:29;;29389:14;29406:71;29430:7;29439:6;:21;;;29462:6;:14;;;29406:23;:71::i;:::-;29523:9;::::0;;::::2;::::0;29514:19:::2;::::0;;;:8:::2;:19:::0;;;;;;:27;29389:88;;-1:-1:-1;29514:37:14::2;::::0;29389:88;;29514:27:::2;;:37;:::i;:::-;29493:9;::::0;;::::2;::::0;;29484:19:::2;::::0;;;:8:::2;:19:::0;;;;;;;:67;;;::::2;;::::0;;::::2;;::::0;;29600:9;;29591:19;;;;:31:::2;;::::0;:41:::2;::::0;29625:7;;29591:31:::2;:41;:::i;:::-;29566:9;::::0;;::::2;::::0;;29557:19:::2;::::0;;;:8:::2;:19:::0;;;;;;:31:::2;;:75:::0;;;::::2;;::::0;;::::2;;::::0;;29671:11;;29647:9;;29638:19;;;;;:44;;;::::2;::::0;::::2;::::0;;::::2;;;::::0;;29737:11;;29724:25;;::::2;::::0;;:12:::2;:25:::0;;;;;:33;:43:::2;::::0;29760:7;;29724:33;;::::2;;:43;:::i;:::-;29688:12;:25;29701:6;:11;;;29688:25;;;;;;;;;;;;;;;:33;;;:79;;;;;;;;;;;;;;;;;;29815:6;:11;;;29779:77;;29806:7;29779:77;;29795:6;:9;;;29779:77;29828:7;29837:6;:18;;;29779:77;;;;;;;:::i;:::-;;;;;;;;29862:14;;;;-1:-1:-1::0;1701:1:2;2628:7;:22;28885:996:14;;-1:-1:-1;28885:996:14:o;1863:115:1:-;1168:7;;;;1411:9;1403:38;;;;;;;20830:2:105;1403:38:1;;;20812:21:105;20869:2;20849:18;;;20842:30;20908:18;20888;;;20881:46;20944:18;;1403:38:1;20628:340:105;1403:38:1;1922:7:::1;:14:::0;;;::::1;1932:4;1922:14;::::0;;1951:20:::1;1958:12;719:10:4::0;;640:96;10354:135:5;10424:4;10447:35;10455:3;10475:5;10447:7;:35::i;25348:678:14:-;1168:7:1;;;;1411:9;1403:38;;;;;;;20830:2:105;1403:38:1;;;20812:21:105;20869:2;20849:18;;;20842:30;20908:18;20888;;;20881:46;20944:18;;1403:38:1;20628:340:105;1403:38:1;25533:17:14::1;::::0;::::1;1465:19:3::0;25528:47:14::1;;25561:14;;;;;;;;;;;;;;25528:47;1583:5;25585:8;:26;;;:64;;;-1:-1:-1::0;25626:23:14;;::::1;::::0;;::::1;25615:34:::0;;::::1;;25585:64;25581:99;;;25658:22;;;;;;;;;;;;;;25581:99;25701:201;;;;;;;;25775:7;25701:201;;;;;;1260:1;25701:201;;;;;;25750:8;25701:201;;;;;;1848:9;25701:201:::0;::::1;;;;;25724:6;25701:201;;;;;;25894:1;25701:201;;;;;;25797:5;25701:201;;;;::::0;25686:8:::1;:12;25695:2;25686:12;;;;;;;;;;;:216;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25956:7;25932:31;;:21;;:31;;;;:::i;:::-;25908:21;:55:::0;25969:15:::1;::::0;;;:11:::1;:15;::::0;;;;;;;:27;;::::1;::::0;;::::1;::::0;::::1;:::i;:::-;-1:-1:-1::0;26002:19:14::1;:11;26018:2:::0;26002:15:::1;:19::i;:::-;;25348:678:::0;;;;;;:::o;26341:771::-;26387:14;26403:15;26426:23;26452:9;:26;;;;;;;;;;;;26426:52;;;;26484:18;26524:1;26505:16;:20;;;26484:41;;26531:17;26554:16;26607:13;:29;;;:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;26576:62:14;-1:-1:-1;26576:62:14;;-1:-1:-1;26649:13:14;;-1:-1:-1;;26649:63:14;;;;-1:-1:-1;26685:27:14;26703:9;26685:15;:27;:::i;:::-;26666:16;:46;;;26649:63;26648:83;;;;26730:1;26717:9;:14;;26648:83;26644:179;;;26750:18;;26741:27;;26644:179;;;26806:9;26789:27;;26644:179;26859:13;:29;;;:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;26828:62:14;-1:-1:-1;26828:62:14;;-1:-1:-1;26901:13:14;;-1:-1:-1;;26901:63:14;;;;-1:-1:-1;26937:27:14;26955:9;26937:15;:27;:::i;:::-;26918:16;:46;;;26901:63;26900:83;;;;26982:1;26969:9;:14;;26900:83;26896:182;;;27003:19;;26993:29;;26896:182;;;27061:9;27043:28;;26896:182;27083:24;;;;26341:771;;:::o;30425:269::-;30562:9;:30;30508:21;;30553:39;;30562:30;;;;;30553:6;:39;:::i;:::-;30537:55;;30602:13;:44;;;;;30633:13;30619:11;:27;30602:44;30598:92;;;-1:-1:-1;30672:11:14;30425:269;;;;:::o;27209:521::-;27331:14;;27383:32;1748:6;27383:8;:32;:::i;:::-;27373:43;;:6;:43;:::i;:::-;27451:9;:27;27353:63;;-1:-1:-1;27422:15:14;;27440:38;;27451:27;;1794:13;27440:38;:::i;:::-;27557:9;:26;27422:56;;-1:-1:-1;27484:13:14;;27549:44;;27557:26;;;;;27588:4;27549:44;:::i;:::-;27536:7;27523;27502:17;:9;27515:3;27502:17;:::i;:::-;:29;;;;:::i;:::-;27501:43;;;;:::i;:::-;27500:94;;;;:::i;:::-;27484:110;-1:-1:-1;1905:4:14;27604:25;;27600:65;;;27638:27;;;;;;;;;;;;;;27600:65;27685:5;27209:521;-1:-1:-1;;;;;;27209:521:14:o;244:125:11:-;296:9;:23;292:73;;336:22;;;;;;;;;;;;;;29976:320:14;30103:18;;;;;;;:12;:18;;;;;:25;;;;;30098:58;;30137:19;;;;;;;;;;;;;;30098:58;30166:14;;:31;;;-1:-1:-1;30162:63:14;;;30206:19;;;;;;;;;;;;;;30162:63;30256:4;30235:25;;:6;:17;;;:25;;;30231:60;;30269:22;;;;;;;;;;;;;;30231:60;29976:320;;;:::o;1497:188:9:-;1565:10;1559:16;;;;1551:52;;;;;;;39065:2:105;1551:52:9;;;39047:21:105;39104:2;39084:18;;;39077:30;39143:25;39123:18;;;39116:53;39186:18;;1551:52:9;38863:347:105;1551:52:9;1610:14;:19;;;;;;;;;;;;;;-1:-1:-1;1668:7:9;;1641:39;;1610:19;;1668:7;;1641:39;;-1:-1:-1;1641:39:9;1497:188;:::o;4395:118:5:-;4462:7;4488:3;:11;;4500:5;4488:18;;;;;;;;:::i;:::-;;;;;;;;;4481:25;;4395:118;;;;:::o;27884:854:14:-;27998:12;28044:5;28131:19;28128:1;28125:26;28122:60;;;28172:1;28169;28162:12;28122:60;28201:19;28198:1;28194:27;28189:32;;28352:9;28346:2;28343:1;28339:10;28336:1;28332:18;28329:33;28319:75;;28384:1;28381;28374:12;28319:75;;28523:6;28511:19;28501:61;;28552:1;28549;28542:12;28501:61;28706:1;28703;28696:4;28690:11;28683:4;28677;28673:15;28670:1;28662:6;28651:9;28646:62;28635:73;27884:854;-1:-1:-1;;;;27884:854:14:o;2269:1388:5:-;2335:4;2472:19;;;:12;;;:19;;;;;;2506:15;;2502:1149;;2875:21;2899:14;2912:1;2899:10;:14;:::i;:::-;2947:18;;2875:38;;-1:-1:-1;2927:17:5;;2947:22;;2968:1;;2947:22;:::i;:::-;2927:42;;3001:13;2988:9;:26;2984:398;;3034:17;3054:3;:11;;3066:9;3054:22;;;;;;;;:::i;:::-;;;;;;;;;3034:42;;3205:9;3176:3;:11;;3188:13;3176:26;;;;;;;;:::i;:::-;;;;;;;;;;;;:38;;;;3288:23;;;:12;;;:23;;;;;:36;;;2984:398;3460:17;;:3;;:17;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;3552:3;:12;;:19;3565:5;3552:19;;;;;;;;;;;3545:26;;;3593:4;3586:11;;;;;;;2502:1149;3635:5;3628:12;;;;;10057:129;10124:4;3834:19;;;:12;;;:19;;;;;;10147:32;;10152:3;;10172:5;;10124:4;;1776:319;;-1:-1:-1;1818:23:5;;;;;;;;:11;:23;;;;;;;;;;;;;1998:18;;1976:19;;;:12;;;:19;;;;;;:40;;;;2030:11;;1776:319;-1:-1:-1;2079:5:5;2072:12;;-1:-1:-1;;;;;;;:::i;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14:248:105;82:6;90;143:2;131:9;122:7;118:23;114:32;111:52;;;159:1;156;149:12;111:52;-1:-1:-1;;182:23:105;;;252:2;237:18;;;224:32;;-1:-1:-1;14:248:105:o;433:632::-;604:2;656:21;;;726:13;;629:18;;;748:22;;;575:4;;604:2;827:15;;;;801:2;786:18;;;575:4;870:169;884:6;881:1;878:13;870:169;;;945:13;;933:26;;1014:15;;;;979:12;;;;906:1;899:9;870:169;;;-1:-1:-1;1056:3:105;;433:632;-1:-1:-1;;;;;;433:632:105:o;1070:258::-;1142:1;1152:113;1166:6;1163:1;1160:13;1152:113;;;1242:11;;;1236:18;1223:11;;;1216:39;1188:2;1181:10;1152:113;;;1283:6;1280:1;1277:13;1274:48;;;1318:1;1309:6;1304:3;1300:16;1293:27;1274:48;;1070:258;;;:::o;1333:317::-;1375:3;1413:5;1407:12;1440:6;1435:3;1428:19;1456:63;1512:6;1505:4;1500:3;1496:14;1489:4;1482:5;1478:16;1456:63;:::i;:::-;1564:2;1552:15;1569:66;1548:88;1539:98;;;;1639:4;1535:109;;1333:317;-1:-1:-1;;1333:317:105:o;1655:220::-;1804:2;1793:9;1786:21;1767:4;1824:45;1865:2;1854:9;1850:18;1842:6;1824:45;:::i;2418:1304::-;2496:12;;1956:10;1945:22;1933:35;;2560:4;2553:5;2549:16;2543:23;2575:47;2616:4;2611:3;2607:14;2593:12;1956:10;1945:22;1933:35;;1880:94;2575:47;;2670:4;2663:5;2659:16;2653:23;2685:49;2728:4;2723:3;2719:14;2703;2170:8;2159:20;2147:33;;2094:92;2685:49;;2782:4;2775:5;2771:16;2765:23;2797:49;2840:4;2835:3;2831:14;2815;1956:10;1945:22;1933:35;;1880:94;2797:49;;2894:4;2887:5;2883:16;2877:23;2909:49;2952:4;2947:3;2943:14;2927;2170:8;2159:20;2147:33;;2094:92;2909:49;;3006:4;2999:5;2995:16;2989:23;3021:49;3064:4;3059:3;3055:14;3039;2267:6;2256:18;2244:31;;2191:90;3021:49;;3118:4;3111:5;3107:16;3101:23;3133:49;3176:4;3171:3;3167:14;3151;2055:26;2044:38;2032:51;;1979:110;3133:49;;3230:4;3223:5;3219:16;3213:23;3245:49;3288:4;3283:3;3279:14;3263;1956:10;1945:22;1933:35;;1880:94;3245:49;-1:-1:-1;3313:6:105;3355:14;;;3349:21;3335:12;;;3328:43;3390:6;3432:14;;;3426:21;3412:12;;;3405:43;3467:6;3510:14;;;3504:21;2363:42;2352:54;;3569:12;;;2340:67;-1:-1:-1;;3601:6:105;3644:14;;;3638:21;2363:42;2352:54;;3703:12;;;2340:67;3668:48;2286:127;3727:1208;4023:4;4052:3;4093:2;4082:9;4078:18;4142:10;4133:6;4127:13;4123:30;4112:9;4105:49;4173:4;4241:26;4235:2;4227:6;4223:15;4217:22;4213:55;4208:2;4197:9;4193:18;4186:83;4325:4;4317:6;4313:17;4307:24;4300:4;4289:9;4285:20;4278:54;4388:4;4380:6;4376:17;4370:24;4363:4;4352:9;4348:20;4341:54;4404:53;4452:3;4441:9;4437:19;4429:6;4404:53;:::i;:::-;4488:3;4473:19;;4466:31;;;;4546:13;;4568:22;;;;4621:3;4606:19;;;4648:15;;;;4681:1;4691:218;4705:6;4702:1;4699:13;4691:218;;;4770:13;;4785:42;4766:62;4754:75;;4849:12;;;;4884:15;;;;4727:1;4720:9;4691:218;;;-1:-1:-1;4926:3:105;;3727:1208;-1:-1:-1;;;;;;;;3727:1208:105:o;4940:196::-;5008:20;;5068:42;5057:54;;5047:65;;5037:93;;5126:1;5123;5116:12;5037:93;4940:196;;;:::o;5141:354::-;5233:6;5241;5294:2;5282:9;5273:7;5269:23;5265:32;5262:52;;;5310:1;5307;5300:12;5262:52;5333:29;5352:9;5333:29;:::i;:::-;5323:39;;5412:2;5401:9;5397:18;5384:32;5445:1;5438:5;5435:12;5425:40;;5461:1;5458;5451:12;5425:40;5484:5;5474:15;;;5141:354;;;;;:::o;5759:186::-;5818:6;5871:2;5859:9;5850:7;5846:23;5842:32;5839:52;;;5887:1;5884;5877:12;5839:52;5910:29;5929:9;5910:29;:::i;6626:184::-;6678:77;6675:1;6668:88;6775:4;6772:1;6765:15;6799:4;6796:1;6789:15;6815:143;6899:1;6892:5;6889:12;6879:46;;6905:18;;:::i;:::-;6934;;6815:143::o;6963:214::-;7112:2;7097:18;;7124:47;7101:9;7153:6;7124:47;:::i;7374:254::-;7442:6;7450;7503:2;7491:9;7482:7;7478:23;7474:32;7471:52;;;7519:1;7516;7509:12;7471:52;7555:9;7542:23;7532:33;;7584:38;7618:2;7607:9;7603:18;7584:38;:::i;:::-;7574:48;;7374:254;;;;;:::o;7633:347::-;7684:8;7694:6;7748:3;7741:4;7733:6;7729:17;7725:27;7715:55;;7766:1;7763;7756:12;7715:55;-1:-1:-1;7789:20:105;;7832:18;7821:30;;7818:50;;;7864:1;7861;7854:12;7818:50;7901:4;7893:6;7889:17;7877:29;;7953:3;7946:4;7937:6;7929;7925:19;7921:30;7918:39;7915:59;;;7970:1;7967;7960:12;7915:59;7633:347;;;;;:::o;7985:477::-;8064:6;8072;8080;8133:2;8121:9;8112:7;8108:23;8104:32;8101:52;;;8149:1;8146;8139:12;8101:52;8185:9;8172:23;8162:33;;8246:2;8235:9;8231:18;8218:32;8273:18;8265:6;8262:30;8259:50;;;8305:1;8302;8295:12;8259:50;8344:58;8394:7;8385:6;8374:9;8370:22;8344:58;:::i;:::-;7985:477;;8421:8;;-1:-1:-1;8318:84:105;;-1:-1:-1;;;;7985:477:105:o;8467:367::-;8530:8;8540:6;8594:3;8587:4;8579:6;8575:17;8571:27;8561:55;;8612:1;8609;8602:12;8561:55;-1:-1:-1;8635:20:105;;8678:18;8667:30;;8664:50;;;8710:1;8707;8700:12;8664:50;8747:4;8739:6;8735:17;8723:29;;8807:3;8800:4;8790:6;8787:1;8783:14;8775:6;8771:27;8767:38;8764:47;8761:67;;;8824:1;8821;8814:12;8839:511;8934:6;8942;8950;9003:2;8991:9;8982:7;8978:23;8974:32;8971:52;;;9019:1;9016;9009:12;8971:52;9059:9;9046:23;9092:18;9084:6;9081:30;9078:50;;;9124:1;9121;9114:12;9078:50;9163:70;9225:7;9216:6;9205:9;9201:22;9163:70;:::i;:::-;9252:8;;-1:-1:-1;9137:96:105;-1:-1:-1;9306:38:105;;-1:-1:-1;9340:2:105;9325:18;;9306:38;:::i;:::-;9296:48;;8839:511;;;;;:::o;9586:409::-;9656:6;9664;9717:2;9705:9;9696:7;9692:23;9688:32;9685:52;;;9733:1;9730;9723:12;9685:52;9773:9;9760:23;9806:18;9798:6;9795:30;9792:50;;;9838:1;9835;9828:12;9792:50;9877:58;9927:7;9918:6;9907:9;9903:22;9877:58;:::i;:::-;9954:8;;9851:84;;-1:-1:-1;9586:409:105;-1:-1:-1;;;;9586:409:105:o;10000:180::-;10059:6;10112:2;10100:9;10091:7;10087:23;10083:32;10080:52;;;10128:1;10125;10118:12;10080:52;-1:-1:-1;10151:23:105;;10000:180;-1:-1:-1;10000:180:105:o;10398:179::-;10465:20;;10525:26;10514:38;;10504:49;;10494:77;;10567:1;10564;10557:12;10582:252;10649:6;10657;10710:2;10698:9;10689:7;10685:23;10681:32;10678:52;;;10726:1;10723;10716:12;10678:52;10762:9;10749:23;10739:33;;10791:37;10824:2;10813:9;10809:18;10791:37;:::i;10839:551::-;10927:6;10935;10943;10951;11004:2;10992:9;10983:7;10979:23;10975:32;10972:52;;;11020:1;11017;11010:12;10972:52;11043:29;11062:9;11043:29;:::i;:::-;11033:39;;11119:2;11108:9;11104:18;11091:32;11081:42;;11174:2;11163:9;11159:18;11146:32;11201:18;11193:6;11190:30;11187:50;;;11233:1;11230;11223:12;11187:50;11272:58;11322:7;11313:6;11302:9;11298:22;11272:58;:::i;:::-;10839:551;;;;-1:-1:-1;11349:8:105;-1:-1:-1;;;;10839:551:105:o;11395:260::-;11463:6;11471;11524:2;11512:9;11503:7;11499:23;11495:32;11492:52;;;11540:1;11537;11530:12;11492:52;11563:29;11582:9;11563:29;:::i;:::-;11553:39;;11611:38;11645:2;11634:9;11630:18;11611:38;:::i;11660:163::-;11727:20;;11787:10;11776:22;;11766:33;;11756:61;;11813:1;11810;11803:12;11828:252;11895:6;11903;11956:2;11944:9;11935:7;11931:23;11927:32;11924:52;;;11972:1;11969;11962:12;11924:52;12008:9;11995:23;11985:33;;12037:37;12070:2;12059:9;12055:18;12037:37;:::i;12085:773::-;12207:6;12215;12223;12231;12284:2;12272:9;12263:7;12259:23;12255:32;12252:52;;;12300:1;12297;12290:12;12252:52;12340:9;12327:23;12369:18;12410:2;12402:6;12399:14;12396:34;;;12426:1;12423;12416:12;12396:34;12465:70;12527:7;12518:6;12507:9;12503:22;12465:70;:::i;:::-;12554:8;;-1:-1:-1;12439:96:105;-1:-1:-1;12642:2:105;12627:18;;12614:32;;-1:-1:-1;12658:16:105;;;12655:36;;;12687:1;12684;12677:12;12655:36;;12726:72;12790:7;12779:8;12768:9;12764:24;12726:72;:::i;12863:505::-;13122:3;13111:9;13104:22;13085:4;13143:46;13184:3;13173:9;13169:19;13161:6;13143:46;:::i;:::-;13135:54;;13225:6;13220:2;13209:9;13205:18;13198:34;13268:6;13263:2;13252:9;13248:18;13241:34;13311:6;13306:2;13295:9;13291:18;13284:34;13355:6;13349:3;13338:9;13334:19;13327:35;12863:505;;;;;;;;:::o;13373:926::-;13671:4;13700:3;13722:42;13803:2;13795:6;13791:15;13780:9;13773:34;13855:10;13847:6;13843:23;13838:2;13827:9;13823:18;13816:51;13903:2;13898;13887:9;13883:18;13876:30;13923:45;13964:2;13953:9;13949:18;13941:6;13923:45;:::i;:::-;13987:26;14049:15;;;14044:2;14029:18;;14022:43;14102:15;;;14096:3;14081:19;;14074:44;14155:15;;;;14149:3;14134:19;;14127:44;-1:-1:-1;14220:18:105;14208:31;;;;14202:3;14187:19;;14180:60;14277:15;;;14271:3;14256:19;;;14249:44;;;;13915:53;13373:926;-1:-1:-1;;;;13373:926:105:o;14304:630::-;14400:6;14408;14416;14424;14432;14485:3;14473:9;14464:7;14460:23;14456:33;14453:53;;;14502:1;14499;14492:12;14453:53;14525:29;14544:9;14525:29;:::i;:::-;14515:39;;14573:37;14606:2;14595:9;14591:18;14573:37;:::i;:::-;14563:47;;14629:38;14663:2;14652:9;14648:18;14629:38;:::i;:::-;14619:48;;14718:2;14707:9;14703:18;14690:32;14745:18;14737:6;14734:30;14731:50;;;14777:1;14774;14767:12;14731:50;14816:58;14866:7;14857:6;14846:9;14842:22;14816:58;:::i;:::-;14304:630;;;;-1:-1:-1;14304:630:105;;-1:-1:-1;14893:8:105;;14790:84;14304:630;-1:-1:-1;;;14304:630:105:o;15121:184::-;15173:77;15170:1;15163:88;15270:4;15267:1;15260:15;15294:4;15291:1;15284:15;15310:252;15382:2;15376:9;15424:3;15412:16;;15458:18;15443:34;;15479:22;;;15440:62;15437:88;;;15505:18;;:::i;:::-;15541:2;15534:22;15310:252;:::o;15567:253::-;15639:2;15633:9;15681:4;15669:17;;15716:18;15701:34;;15737:22;;;15698:62;15695:88;;;15763:18;;:::i;15825:334::-;15896:2;15890:9;15952:2;15942:13;;15957:66;15938:86;15926:99;;16055:18;16040:34;;16076:22;;;16037:62;16034:88;;;16102:18;;:::i;:::-;16138:2;16131:22;15825:334;;-1:-1:-1;15825:334:105:o;16164:161::-;16231:20;;16291:8;16280:20;;16270:31;;16260:59;;16315:1;16312;16305:12;16330:159;16397:20;;16457:6;16446:18;;16436:29;;16426:57;;16479:1;16476;16469:12;16494:1142;16578:6;16631:3;16619:9;16610:7;16606:23;16602:33;16599:53;;;16648:1;16645;16638:12;16599:53;16674:22;;:::i;:::-;16719:28;16737:9;16719:28;:::i;:::-;16712:5;16705:43;16780:37;16813:2;16802:9;16798:18;16780:37;:::i;:::-;16775:2;16768:5;16764:14;16757:61;16850:37;16883:2;16872:9;16868:18;16850:37;:::i;:::-;16845:2;16838:5;16834:14;16827:61;16920:37;16953:2;16942:9;16938:18;16920:37;:::i;:::-;16915:2;16908:5;16904:14;16897:61;16991:38;17024:3;17013:9;17009:19;16991:38;:::i;:::-;16985:3;16978:5;16974:15;16967:63;17063:38;17096:3;17085:9;17081:19;17063:38;:::i;:::-;17057:3;17050:5;17046:15;17039:63;17135:38;17168:3;17157:9;17153:19;17135:38;:::i;:::-;17129:3;17122:5;17118:15;17111:63;17207:38;17240:3;17229:9;17225:19;17207:38;:::i;:::-;17201:3;17190:15;;17183:63;17265:3;17313:18;;;17300:32;17284:14;;;17277:56;17352:3;17400:18;;;17387:32;17371:14;;;17364:56;17439:3;17474:38;17493:18;;;17474:38;:::i;:::-;17458:14;;;17451:62;17532:3;17567:38;17586:18;;;17567:38;:::i;:::-;17551:14;;;17544:62;17555:5;16494:1142;-1:-1:-1;;;16494:1142:105:o;17641:255::-;17797:2;17782:18;;17830:1;17819:13;;17809:47;;17836:18;;:::i;:::-;17865:25;;;17641:255;:::o;17901:184::-;17953:77;17950:1;17943:88;18050:4;18047:1;18040:15;18074:4;18071:1;18064:15;18090:125;18130:4;18158:1;18155;18152:8;18149:34;;;18163:18;;:::i;:::-;-1:-1:-1;18200:9:105;;18090:125::o;18220:128::-;18260:3;18291:1;18287:6;18284:1;18281:13;18278:39;;;18297:18;;:::i;:::-;-1:-1:-1;18333:9:105;;18220:128::o;18353:184::-;18405:77;18402:1;18395:88;18502:4;18499:1;18492:15;18526:4;18523:1;18516:15;18542:195;18581:3;18612:66;18605:5;18602:77;18599:103;;18682:18;;:::i;:::-;-1:-1:-1;18729:1:105;18718:13;;18542:195::o;18742:237::-;18781:4;18810:26;18886:10;;;;18856;;18908:12;;;18905:38;;;18923:18;;:::i;:::-;18960:13;;18742:237;-1:-1:-1;;;18742:237:105:o;18984:244::-;19023:3;19051:26;19104:2;19101:1;19097:10;19134:2;19131:1;19127:10;19165:3;19161:2;19157:12;19152:3;19149:21;19146:47;;;19173:18;;:::i;:::-;19209:13;;18984:244;-1:-1:-1;;;;18984:244:105:o;19901:164::-;19977:13;;20026;;20019:21;20009:32;;19999:60;;20055:1;20052;20045:12;20070:202;20137:6;20190:2;20178:9;20169:7;20165:23;20161:32;20158:52;;;20206:1;20203;20196:12;20158:52;20229:37;20256:9;20229:37;:::i;20973:437::-;21052:1;21048:12;;;;21095;;;21116:61;;21170:4;21162:6;21158:17;21148:27;;21116:61;21223:2;21215:6;21212:14;21192:18;21189:38;21186:218;;21260:77;21257:1;21250:88;21361:4;21358:1;21351:15;21389:4;21386:1;21379:15;21186:218;;20973:437;;;:::o;21415:615::-;21466:3;21504:5;21498:12;21531:6;21526:3;21519:19;21557:4;21598:2;21593:3;21589:12;21623:11;21650;21643:18;;21700:6;21697:1;21693:14;21686:5;21682:26;21670:38;;21742:2;21735:5;21731:14;21763:1;21773:231;21787:6;21784:1;21781:13;21773:231;;;21858:5;21852:4;21848:16;21843:3;21836:29;21886:38;21919:4;21910:6;21904:13;21886:38;:::i;:::-;21982:12;;;;21878:46;-1:-1:-1;21947:15:105;;;;21809:1;21802:9;21773:231;;;-1:-1:-1;22020:4:105;;21415:615;-1:-1:-1;;;;;;;21415:615:105:o;22035:2109::-;22409:4;22438:2;22467;22456:9;22449:21;22506:6;22501:2;22490:9;22486:18;22479:34;22532:3;22558:66;22550:6;22547:78;22544:98;;;22638:1;22635;22628:12;22544:98;22672:6;22669:1;22665:14;22729:6;22721;22716:2;22705:9;22701:18;22688:48;22770:6;22759:9;22755:22;22745:32;;22804:2;22800;22796:11;22827:1;22823:2;22816:13;22848:4;22912:2;22900:9;22896:2;22892:18;22888:27;22883:2;22872:9;22868:18;22861:55;22936:2;22969:6;22963:13;22996:8;22992:2;22985:20;23024:3;23014:13;;23051:2;23047;23043:11;23036:18;;23089:2;23081:6;23077:15;23063:29;;23110:1;23120:904;23134:8;23131:1;23128:15;23120:904;;;23195:13;;23237:9;;23248:26;23233:42;23221:55;;23315:11;;;23309:18;23350:42;23426:21;;;23412:12;;;23405:43;23471:4;23519:11;;;23513:18;23533:10;23509:35;23495:12;;;23488:57;23589:11;;;23583:18;23603;23579:43;23565:12;;;23558:65;23667:11;;;23661:18;23657:27;23643:12;;;23636:49;23726:11;;;23720:18;23751:47;23785:12;;;23720:18;2055:26;2044:38;2032:51;;1979:110;23751:47;-1:-1:-1;23822:4:105;23867:12;;;23861:19;2363:42;2352:54;23928:13;;;2340:67;23999:15;;;;23971:4;23962:14;;;;23158:1;23151:9;23120:904;;;23124:3;;24071:9;24066:3;24062:19;24055:4;24044:9;24040:20;24033:49;24099:39;24134:3;24126:6;24099:39;:::i;:::-;24091:47;22035:2109;-1:-1:-1;;;;;;;;;;;;22035:2109:105:o;24149:277::-;24236:6;24289:2;24277:9;24268:7;24264:23;24260:32;24257:52;;;24305:1;24302;24295:12;24257:52;24337:9;24331:16;24376:1;24369:5;24366:12;24356:40;;24392:1;24389;24382:12;24431:434;24646:47;24683:9;24675:6;24646:47;:::i;:::-;24702:56;24754:2;24743:9;24739:18;24731:6;24702:56;:::i;:::-;24794:2;24789;24778:9;24774:18;24767:30;24627:4;24814:45;24855:2;24844:9;24840:18;24832:6;24814:45;:::i;24870:245::-;24918:4;24951:18;24943:6;24940:30;24937:56;;;24973:18;;:::i;:::-;-1:-1:-1;25030:2:105;25018:15;25035:66;25014:88;25104:4;25010:99;;24870:245::o;25120:428::-;25173:5;25226:3;25219:4;25211:6;25207:17;25203:27;25193:55;;25244:1;25241;25234:12;25193:55;25273:6;25267:13;25304:48;25320:31;25348:2;25320:31;:::i;:::-;25304:48;:::i;:::-;25377:2;25368:7;25361:19;25423:3;25416:4;25411:2;25403:6;25399:15;25395:26;25392:35;25389:55;;;25440:1;25437;25430:12;25389:55;25453:64;25514:2;25507:4;25498:7;25494:18;25487:4;25479:6;25475:17;25453:64;:::i;25553:335::-;25632:6;25685:2;25673:9;25664:7;25660:23;25656:32;25653:52;;;25701:1;25698;25691:12;25653:52;25734:9;25728:16;25767:18;25759:6;25756:30;25753:50;;;25799:1;25796;25789:12;25753:50;25822:60;25874:7;25865:6;25854:9;25850:22;25822:60;:::i;26418:183::-;26478:4;26511:18;26503:6;26500:30;26497:56;;;26533:18;;:::i;:::-;-1:-1:-1;26578:1:105;26574:14;26590:4;26570:25;;26418:183::o;26606:1631::-;26666:5;26719:3;26712:4;26704:6;26700:17;26696:27;26686:55;;26737:1;26734;26727:12;26686:55;26773:6;26760:20;26799:4;26823:60;26839:43;26879:2;26839:43;:::i;26823:60::-;26917:15;;;26979:4;27022:11;;;27010:24;;27006:33;;;26948:12;;;;26905:3;27051:15;;;27048:35;;;27079:1;27076;27069:12;27048:35;27115:2;27107:6;27103:15;27127:1081;27143:6;27138:3;27135:15;27127:1081;;;27219:2;27213:3;27208;27204:13;27200:22;27197:112;;;27263:1;27292:2;27288;27281:14;27197:112;27335:22;;:::i;:::-;27384;27402:3;27384:22;:::i;:::-;27377:5;27370:37;27443:32;27471:2;27466:3;27462:12;27443:32;:::i;:::-;27438:2;27431:5;27427:14;27420:56;27499:2;27537:31;27564:2;27559:3;27555:12;27537:31;:::i;:::-;27521:14;;;27514:55;27592:2;27635:12;;;27622:26;27696:18;27683:32;;27671:45;;27661:143;;27758:1;27787:2;27783;27776:14;27661:143;27824:14;;;27817:31;27871:3;27910:32;27929:12;;;27910:32;:::i;:::-;27894:14;;;27887:56;27966:3;28005:31;28023:12;;;28005:31;:::i;:::-;27989:14;;;27982:55;28061:3;28101:33;28120:13;;;28101:33;:::i;:::-;28084:15;;;28077:58;28148:18;;28186:12;;;;27160;;27127:1081;;;-1:-1:-1;28226:5:105;;26606:1631;-1:-1:-1;;;;;;;26606:1631:105:o;28242:1438::-;28294:5;28347:3;28340:4;28332:6;28328:17;28324:27;28314:55;;28365:1;28362;28355:12;28314:55;28401:6;28388:20;28427:4;28451:60;28467:43;28507:2;28467:43;:::i;28451:60::-;28545:15;;;28631:1;28627:10;;;;28615:23;;28611:32;;;28576:12;;;;28655:15;;;28652:35;;;28683:1;28680;28673:12;28652:35;28719:2;28711:6;28707:15;28731:920;28747:6;28742:3;28739:15;28731:920;;;28833:3;28820:17;28869:18;28856:11;28853:35;28850:125;;;28929:1;28958:2;28954;28947:14;28850:125;28998:24;;29057:2;29049:11;;29045:21;-1:-1:-1;29035:119:105;;29108:1;29137:2;29133;29126:14;29035:119;29198:2;29194;29190:11;29177:25;29225:2;29255:48;29271:31;29299:2;29271:31;:::i;29255:48::-;29332:2;29323:7;29316:19;29376:3;29371:2;29366;29362;29358:11;29354:20;29351:29;29348:119;;;29421:1;29450:2;29446;29439:14;29348:119;29524:2;29519;29515;29511:11;29506:2;29497:7;29493:16;29480:47;29574:1;29551:16;;;29547:25;;29540:36;;;;-1:-1:-1;29589:20:105;;-1:-1:-1;29629:12:105;;;;28764;;28731:920;;;-1:-1:-1;29669:5:105;28242:1438;-1:-1:-1;;;;;;28242:1438:105:o;29685:1403::-;29870:6;29878;29886;29939:2;29927:9;29918:7;29914:23;29910:32;29907:52;;;29955:1;29952;29945:12;29907:52;29995:9;29982:23;30024:18;30065:2;30057:6;30054:14;30051:34;;;30081:1;30078;30071:12;30051:34;30119:6;30108:9;30104:22;30094:32;;30164:7;30157:4;30153:2;30149:13;30145:27;30135:55;;30186:1;30183;30176:12;30135:55;30222:2;30209:16;30244:4;30268:60;30284:43;30324:2;30284:43;:::i;30268:60::-;30362:15;;;30444:1;30440:10;;;;30432:19;;30428:28;;;30393:12;;;;30468:19;;;30465:39;;;30500:1;30497;30490:12;30465:39;30524:11;;;;30544:142;30560:6;30555:3;30552:15;30544:142;;;30626:17;;30614:30;;30577:12;;;;30664;;;;30544:142;;;30705:5;-1:-1:-1;;30748:18:105;;30735:32;;-1:-1:-1;;30779:16:105;;;30776:36;;;30808:1;30805;30798:12;30776:36;30831:69;30892:7;30881:8;30870:9;30866:24;30831:69;:::i;:::-;30821:79;;30953:2;30942:9;30938:18;30925:32;30909:48;;30982:2;30972:8;30969:16;30966:36;;;30998:1;30995;30988:12;30966:36;;31021:61;31074:7;31063:8;31052:9;31048:24;31021:61;:::i;:::-;31011:71;;;29685:1403;;;;;:::o;31725:184::-;31795:6;31848:2;31836:9;31827:7;31823:23;31819:32;31816:52;;;31864:1;31861;31854:12;31816:52;-1:-1:-1;31887:16:105;;31725:184;-1:-1:-1;31725:184:105:o;31914:470::-;32014:6;32009:3;32002:19;31984:3;32040:4;32069:2;32064:3;32060:12;32053:19;;32095:5;32118:1;32128:231;32142:6;32139:1;32136:13;32128:231;;;32235:42;32207:26;32226:6;32207:26;:::i;:::-;32203:75;32191:88;;32299:12;;;;32334:15;;;;32164:1;32157:9;32128:231;;;-1:-1:-1;32375:3:105;;31914:470;-1:-1:-1;;;;;31914:470:105:o;32389:519::-;32666:2;32655:9;32648:21;32629:4;32692:73;32761:2;32750:9;32746:18;32738:6;32730;32692:73;:::i;:::-;32813:9;32805:6;32801:22;32796:2;32785:9;32781:18;32774:50;32841:61;32895:6;32887;32879;32841:61;:::i;:::-;32833:69;32389:519;-1:-1:-1;;;;;;;32389:519:105:o;33038:1309::-;33145:4;33174:2;33203;33192:9;33185:21;33226:1;33259:6;33253:13;33289:3;33311:1;33339:9;33335:2;33331:18;33321:28;;33399:2;33388:9;33384:18;33421;33411:61;;33465:4;33457:6;33453:17;33443:27;;33411:61;33518:2;33510:6;33507:14;33487:18;33484:38;33481:222;;33557:77;33552:3;33545:90;33658:4;33655:1;33648:15;33688:4;33683:3;33676:17;33481:222;33770:18;;;365:19;;;417:4;408:14;33813:18;33840:158;;;;34012:1;34007:314;;;;33806:515;;33840:158;33888:66;33877:9;33873:82;33868:3;33861:95;33985:2;33980:3;33976:12;33969:19;;33840:158;;34007:314;32985:1;32978:14;;;33022:4;33009:18;;34101:1;34115:165;34129:6;34126:1;34123:13;34115:165;;;34207:14;;34194:11;;;34187:35;34250:16;;;;34144:10;;34115:165;;;34300:11;;;-1:-1:-1;;33806:515:105;-1:-1:-1;34338:3:105;;33038:1309;-1:-1:-1;;;;;;;;;33038:1309:105:o;34352:274::-;34481:3;34519:6;34513:13;34535:53;34581:6;34576:3;34569:4;34561:6;34557:17;34535:53;:::i;:::-;34604:16;;;;;34352:274;-1:-1:-1;;34352:274:105:o;34631:414::-;34716:6;34724;34777:2;34765:9;34756:7;34752:23;34748:32;34745:52;;;34793:1;34790;34783:12;34745:52;34816:37;34843:9;34816:37;:::i;:::-;34806:47;;34897:2;34886:9;34882:18;34876:25;34924:18;34916:6;34913:30;34910:50;;;34956:1;34953;34946:12;34910:50;34979:60;35031:7;35022:6;35011:9;35007:22;34979:60;:::i;:::-;34969:70;;;34631:414;;;;;:::o;35530:201::-;35568:3;35596:10;35641:2;35634:5;35630:14;35668:2;35659:7;35656:15;35653:41;;35674:18;;:::i;:::-;35723:1;35710:15;;35530:201;-1:-1:-1;;;35530:201:105:o;36053:246::-;36237:3;36222:19;;36250:43;36226:9;36275:6;36250:43;:::i;37364:320::-;37549:26;37541:6;37537:39;37526:9;37519:58;37613:2;37608;37597:9;37593:18;37586:30;37500:4;37633:45;37674:2;37663:9;37659:18;37651:6;37633:45;:::i;37689:179::-;37767:13;;37820:22;37809:34;;37799:45;;37789:73;;37858:1;37855;37848:12;37873:473;37976:6;37984;37992;38000;38008;38061:3;38049:9;38040:7;38036:23;38032:33;38029:53;;;38078:1;38075;38068:12;38029:53;38101:39;38130:9;38101:39;:::i;:::-;38091:49;;38180:2;38169:9;38165:18;38159:25;38149:35;;38224:2;38213:9;38209:18;38203:25;38193:35;;38268:2;38257:9;38253:18;38247:25;38237:35;;38291:49;38335:3;38324:9;38320:19;38291:49;:::i;:::-;38281:59;;37873:473;;;;;;;;:::o;38351:228::-;38391:7;38517:1;38449:66;38445:74;38442:1;38439:81;38434:1;38427:9;38420:17;38416:105;38413:131;;;38524:18;;:::i;:::-;-1:-1:-1;38564:9:105;;38351:228::o;38584:274::-;38624:1;38650;38640:189;;38685:77;38682:1;38675:88;38786:4;38783:1;38776:15;38814:4;38811:1;38804:15;38640:189;-1:-1:-1;38843:9:105;;38584:274::o;39215:184::-;39267:77;39264:1;39257:88;39364:4;39361:1;39354:15;39388:4;39385:1;39378:15
Swarm Source
none
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Validator ID :
0 FTM
Amount Staked
0
Amount Delegated
0
Staking Total
0
Staking Start Epoch
0
Staking Start Time
0
Proof of Importance
0
Origination Score
0
Validation Score
0
Active
0
Online
0
Downtime
0 s
Address | Amount | claimed Rewards | Created On Epoch | Created On |
---|