Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- AirnodeProtocol
- Optimization enabled
- true
- Compiler version
- v0.8.9+commit.e5eed63a
- Optimization runs
- 1000
- EVM Version
- default
- Verified at
- 2022-08-28T15:58:00.326186Z
contracts/protocol/AirnodeProtocol.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./StorageUtils.sol";
import "./SponsorshipUtils.sol";
import "./WithdrawalUtils.sol";
import "../utils/ExtendedMulticall.sol";
import "./interfaces/IAirnodeProtocol.sol";
/// @title Airnode request–response protocol (RRP) and its relayed version
/// @notice Similar to HTTP, RRP allows the requester to specify a one-off
/// request that the Airnode is expected to respond to as soon as possible.
/// The relayed version allows the requester to specify an Airnode that will
/// sign the fulfillment data and a relayer that will report the signed
/// fulfillment.
/// @dev This contract inherits Multicall for Airnodes to be able to make batch
/// static calls to read sponsorship states, and template and subscription
/// details.
/// StorageUtils, SponsorshipUtils and WithdrawalUtils also implement some
/// auxiliary functionality for PSP.
contract AirnodeProtocol is
StorageUtils,
SponsorshipUtils,
WithdrawalUtils,
ExtendedMulticall,
IAirnodeProtocol
{
using ECDSA for bytes32;
/// @notice Number of requests the requester made
/// @dev This can be used to calculate the ID of the next request that the
/// requester will make
mapping(address => uint256) public override requesterToRequestCount;
mapping(bytes32 => bytes32) private requestIdToFulfillmentParameters;
/// @notice Called by the requester to make a request
/// @param templateId Template ID
/// @param parameters Parameters provided by the requester in addition to
/// the parameters in the template
/// @param sponsor Sponsor address
/// @param fulfillFunctionId Selector of the function to be called for
/// fulfillment
/// @return requestId Request ID
function makeRequest(
address airnode,
bytes32 templateId,
bytes calldata parameters,
address sponsor,
bytes4 fulfillFunctionId
) external override returns (bytes32 requestId) {
require(airnode != address(0), "Airnode address zero");
require(templateId != bytes32(0), "Template ID zero");
require(
parameters.length <= MAXIMUM_PARAMETER_LENGTH,
"Parameters too long"
);
require(sponsor != address(0), "Sponsor address zero");
require(fulfillFunctionId != bytes4(0), "Fulfill function ID zero");
uint256 requesterRequestCount = ++requesterToRequestCount[msg.sender];
requestId = keccak256(
abi.encodePacked(
block.chainid,
address(this),
msg.sender,
requesterRequestCount,
airnode,
templateId,
parameters,
sponsor,
fulfillFunctionId
)
);
requestIdToFulfillmentParameters[requestId] = keccak256(
abi.encodePacked(airnode, msg.sender, fulfillFunctionId)
);
emit MadeRequest(
airnode,
requestId,
msg.sender,
requesterRequestCount,
templateId,
parameters,
sponsor,
fulfillFunctionId
);
}
/// @notice Called by the Airnode using the sponsor wallet to fulfill the
/// request
/// @dev Airnodes attest to controlling their respective sponsor wallets by
/// signing a message with the address of the sponsor wallet. A timestamp
/// is added to this signature for it to act as an expiring token if the
/// requester contract checks for freshness.
/// This will not revert depending on the external call. However, it will
/// return `false` if the external call reverts or if there is no function
/// with a matching signature at `fulfillAddress`. On the other hand, it
/// will return `true` if the external call returns successfully or if
/// there is no contract deployed at `fulfillAddress`.
/// If `callSuccess` is `false`, `callData` can be decoded to retrieve the
/// revert string.
/// This function emits its event after an untrusted low-level call,
/// meaning that the log indices of these events may be off.
/// @param requestId Request ID
/// @param airnode Airnode address
/// @param requester Requester address
/// @param fulfillFunctionId Selector of the function to be called for
/// fulfillment
/// @param timestamp Timestamp used in the signature
/// @param data Fulfillment data, encoded in contract ABI
/// @param signature Request ID, a timestamp and the sponsor wallet address
/// signed by the Airnode wallet
/// @return callSuccess If the fulfillment call succeeded
/// @return callData Data returned by the fulfillment call (if there is
/// any)
function fulfillRequest(
bytes32 requestId,
address airnode,
address requester,
bytes4 fulfillFunctionId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external override returns (bool callSuccess, bytes memory callData) {
require(
keccak256(
abi.encodePacked(airnode, requester, fulfillFunctionId)
) == requestIdToFulfillmentParameters[requestId],
"Invalid request fulfillment"
);
require(
(
keccak256(abi.encodePacked(requestId, timestamp, msg.sender))
.toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
delete requestIdToFulfillmentParameters[requestId];
(callSuccess, callData) = requester.call( // solhint-disable-line avoid-low-level-calls
abi.encodeWithSelector(
fulfillFunctionId,
requestId,
timestamp,
data
)
);
if (callSuccess) {
emit FulfilledRequest(airnode, requestId, timestamp, data);
} else {
// We do not bubble up the revert string from `callData`
emit FailedRequest(
airnode,
requestId,
timestamp,
"Fulfillment failed unexpectedly"
);
}
}
/// @notice Called by the Airnode using the sponsor wallet if the request
/// cannot be fulfilled
/// @dev The Airnode should fall back to this if a request cannot be
/// fulfilled because of an error, including the static call to `fulfill()`
/// returning `false` for `callSuccess`.
/// @param requestId Request ID
/// @param airnode Airnode address
/// @param requester Requester address
/// @param fulfillFunctionId Selector of the function to be called for
/// fulfillment
/// @param timestamp Timestamp used in the signature
/// @param errorMessage A message that explains why the request has failed
/// @param signature Request ID, a timestamp and the sponsor wallet address
/// signed by the Airnode address
function failRequest(
bytes32 requestId,
address airnode,
address requester,
bytes4 fulfillFunctionId,
uint256 timestamp,
string calldata errorMessage,
bytes calldata signature
) external override {
require(
keccak256(
abi.encodePacked(airnode, requester, fulfillFunctionId)
) == requestIdToFulfillmentParameters[requestId],
"Invalid request fulfillment"
);
require(
(
keccak256(abi.encodePacked(requestId, timestamp, msg.sender))
.toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
delete requestIdToFulfillmentParameters[requestId];
emit FailedRequest(airnode, requestId, timestamp, errorMessage);
}
/// @notice Called by the requester to make a request to be fulfilled by a
/// relayer
/// @dev The relayer address indexed in the relayed protocol logs because
/// it will be the relayer that will be listening to these logs
/// @param templateId Template ID
/// @param parameters Parameters provided by the requester in addition to
/// the parameters in the template
/// @param relayer Relayer address
/// @param sponsor Sponsor address
/// @param fulfillFunctionId Selector of the function to be called for
/// fulfillment
/// @return requestId Request ID
function makeRequestRelayed(
address airnode,
bytes32 templateId,
bytes calldata parameters,
address relayer,
address sponsor,
bytes4 fulfillFunctionId
) external override returns (bytes32 requestId) {
require(airnode != address(0), "Airnode address zero");
require(templateId != bytes32(0), "Template ID zero");
require(
parameters.length <= MAXIMUM_PARAMETER_LENGTH,
"Parameters too long"
);
require(relayer != address(0), "Relayer address zero");
require(sponsor != address(0), "Sponsor address zero");
require(fulfillFunctionId != bytes4(0), "Fulfill function ID zero");
uint256 requesterRequestCount = ++requesterToRequestCount[msg.sender];
requestId = keccak256(
abi.encodePacked(
block.chainid,
address(this),
msg.sender,
requesterRequestCount,
airnode,
templateId,
parameters,
relayer,
sponsor,
fulfillFunctionId
)
);
requestIdToFulfillmentParameters[requestId] = keccak256(
abi.encodePacked(airnode, msg.sender, relayer, fulfillFunctionId)
);
emit MadeRequestRelayed(
relayer,
requestId,
airnode,
msg.sender,
requesterRequestCount,
templateId,
parameters,
sponsor,
fulfillFunctionId
);
}
/// @notice Called by the relayer using the sponsor wallet to fulfill the
/// request with the Airnode-signed response
/// @dev The Airnode must verify the integrity of the request details,
/// template details, sponsor address–sponsor wallet address consistency
/// before signing the response
/// @param requestId Request ID
/// @param airnode Airnode address
/// @param requester Requester address
/// @param relayer Relayer address
/// @param fulfillFunctionId Selector of the function to be called for
/// fulfillment
/// @param timestamp Timestamp used in the signature
/// @param data Fulfillment data
/// @param signature Request ID, a timestamp, the sponsor wallet address
/// and the fulfillment data signed by the Airnode address
/// @return callSuccess If the fulfillment call succeeded
/// @return callData Data returned by the fulfillment call (if there is
/// any)
function fulfillRequestRelayed(
bytes32 requestId,
address airnode,
address requester,
address relayer,
bytes4 fulfillFunctionId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external override returns (bool callSuccess, bytes memory callData) {
require(
keccak256(
abi.encodePacked(airnode, requester, relayer, fulfillFunctionId)
) == requestIdToFulfillmentParameters[requestId],
"Invalid request fulfillment"
);
require(
(
keccak256(
abi.encodePacked(requestId, timestamp, msg.sender, data)
).toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
delete requestIdToFulfillmentParameters[requestId];
(callSuccess, callData) = requester.call( // solhint-disable-line avoid-low-level-calls
abi.encodeWithSelector(
fulfillFunctionId,
requestId,
timestamp,
data
)
);
if (callSuccess) {
emit FulfilledRequestRelayed(
relayer,
requestId,
airnode,
timestamp,
data
);
} else {
// We do not bubble up the revert string from `callData`
emit FailedRequestRelayed(
relayer,
requestId,
airnode,
timestamp,
"Fulfillment failed unexpectedly"
);
}
}
/// @notice Called by the relayer using the sponsor wallet if the request
/// cannot be fulfilled
/// @dev Since failure may also include problems at the Airnode end (such
/// as it being unavailable), we are content with a signature from the
/// relayer to validate failures. This is acceptable because explicit
/// failures are mainly for easy debugging of issues, and the requester
/// should always consider denial of service from a relayer or an Airnode
/// to be a possibility.
/// @param requestId Request ID
/// @param airnode Airnode address
/// @param requester Requester address
/// @param relayer Relayer address
/// @param timestamp Timestamp used in the signature
/// @param errorMessage A message that explains why the request has failed
/// @param signature Request ID, a timestamp and the sponsor wallet address
/// signed by the relayer address
function failRequestRelayed(
bytes32 requestId,
address airnode,
address requester,
address relayer,
bytes4 fulfillFunctionId,
uint256 timestamp,
string calldata errorMessage,
bytes calldata signature
) external override {
require(
keccak256(
abi.encodePacked(airnode, requester, relayer, fulfillFunctionId)
) == requestIdToFulfillmentParameters[requestId],
"Invalid request fulfillment"
);
require(
(
keccak256(abi.encodePacked(requestId, timestamp, msg.sender))
.toEthSignedMessageHash()
).recover(signature) == relayer,
"Signature mismatch"
);
delete requestIdToFulfillmentParameters[requestId];
emit FailedRequestRelayed(
relayer,
requestId,
airnode,
timestamp,
errorMessage
);
}
/// @notice Returns if the request with the ID is made but not
/// fulfilled/failed yet
/// @dev If a requester has made a request, received a request ID but did
/// not hear back, it can call this method to check if the Airnode/relayer
/// called back `failRequest()`/`failRequestRelayed()` instead.
/// @param requestId Request ID
/// @return If the request is awaiting fulfillment
function requestIsAwaitingFulfillment(bytes32 requestId)
external
view
override
returns (bool)
{
return requestIdToFulfillmentParameters[requestId] != bytes32(0);
}
}
@openzeppelin/contracts/utils/cryptography/ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
@openzeppelin/contracts/utils/Address.sol
// 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);
}
}
}
}
@openzeppelin/contracts/utils/Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
pragma solidity ^0.8.0;
import "./Address.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
abstract contract Multicall {
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
return results;
}
}
@openzeppelin/contracts/utils/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
contracts/protocol/SponsorshipUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "./interfaces/ISponsorshipUtils.sol";
/// @title Contract that sponsors can use to announce their willingness to
/// sponsor a particular RRP requester or PSP subscription
/// @notice The sponsorship status is not checked during requests or
/// fulfillments, which means the respective Airnode is trusted to make this
/// check through a static call to this contract. The Airnode may skip this
/// check if it has received an off-chain assurance.
/// @dev An Airnode (or relayer) has a "sponsor wallet" dedicated for each
/// account through an HD wallet. When a requester makes a request specifying a
/// sponsor, the Airnode verifies the sponsorship my making a static call to
/// this contract, and uses the respective sponsor wallet to fulfill the
/// request. This allows the sponsor to cover the gas costs of the
/// fulfillments, as they know that funds they have deposited in the respective
/// sponsor wallet will only be used for use-cases they have sponsored.
contract SponsorshipUtils is ISponsorshipUtils {
/// @notice Sponsorship status for a sponsor–RRP requester pair
mapping(address => mapping(address => bool))
public
override sponsorToRequesterToRrpSponsorshipStatus;
/// @notice Sponsorship status for a sponsor–PSP subscription pair
mapping(address => mapping(bytes32 => bool))
public
override sponsorToSubscriptionIdToPspSponsorshipStatus;
/// @notice Called by the sponsor to set the sponsorship status of an RRP
/// requester
/// @dev This applies for both regular and relayed RRP requests.
/// In all contracts, we use the "set" verb to refer to setting a value
/// without considering its previous value, and emitting an event whether
/// a state change has occurred or not.
/// @param requester RRP requester address
/// @param status Sponsorship status
function setRrpSponsorshipStatus(address requester, bool status)
external
override
{
require(requester != address(0), "Requester address zero");
sponsorToRequesterToRrpSponsorshipStatus[msg.sender][
requester
] = status;
emit SetRrpSponsorshipStatus(msg.sender, requester, status);
}
/// @notice Called by the sponsor to set the sponsorship status of a PSP
/// subscription
/// @param subscriptionId Subscription ID
/// @param status Sponsorship status
function setPspSponsorshipStatus(bytes32 subscriptionId, bool status)
external
override
{
require(subscriptionId != bytes32(0), "Subscription ID zero");
sponsorToSubscriptionIdToPspSponsorshipStatus[msg.sender][
subscriptionId
] = status;
emit SetPspSponsorshipStatus(msg.sender, subscriptionId, status);
}
}
contracts/protocol/StorageUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "./interfaces/IStorageUtils.sol";
/// @title Contract that can be used to store template and subscriptions
/// details on-chain
/// @notice The Airnode protocol does not depend on the templates or
/// subscriptions to be stored on-chain, which means using this functionality
/// is entirely optional. However, if you are not storing your templates or
/// subscriptions on-chain, you should make sure that the respective Airnode
/// already knows about them (e.g., their details are hardcoded in the
/// Airnode configuration file).
/// @dev Templates and subscriptions need to persist, which is why we do not
/// depend on storing them in logs and the respective Airnode being able to
/// retrieve them from there. Compared to that, storage is a much safer bet.
/// However, this may end up being prohibitively expensive on some chains, in
/// which case the user should consider off-chain channels to pass the details.
contract StorageUtils is IStorageUtils {
struct Template {
bytes32 endpointId;
bytes parameters;
}
struct Subscription {
uint256 chainId;
address airnode;
bytes32 templateId;
bytes parameters;
bytes conditions;
address relayer;
address sponsor;
address requester;
bytes4 fulfillFunctionId;
}
/// @notice Maximum parameter length for byte strings that Airnodes will
/// need to read from storage or logs
/// @dev A very generous limit is applied, under the assumption that
/// anything larger than this is a grief attempt. If the user needs to use
/// longer parameters, they will need to use off-chain channels to pass
/// the respective template/subscription details to the Airnode operator
/// for them to be specified in the configuration file.
uint256 public constant override MAXIMUM_PARAMETER_LENGTH = 4096;
/// @notice Template details with the ID
mapping(bytes32 => Template) public override templates;
/// @notice Subscription details with the ID
mapping(bytes32 => Subscription) public override subscriptions;
/// @notice Stores template details
/// @dev Templates fully or partially define requests. By referencing a
/// template, requesters can omit specifying the "boilerplate" sections of
/// requests.
/// A zero endpoint ID means the Airnode does not need to use one of its
/// endpoints, and can move directly on to fulfillment. This is
/// particularly useful for defining traditional keeper jobs that do not
/// require off-chain data.
/// @param endpointId Endpoint ID (allowed to be `bytes32(0)`)
/// @param parameters Template parameters, encoded in Airnode ABI
/// @return templateId Template ID
function storeTemplate(bytes32 endpointId, bytes calldata parameters)
external
override
returns (bytes32 templateId)
{
require(
parameters.length <= MAXIMUM_PARAMETER_LENGTH,
"Parameters too long"
);
templateId = keccak256(abi.encodePacked(endpointId, parameters));
templates[templateId] = Template({
endpointId: endpointId,
parameters: parameters
});
emit StoredTemplate(templateId, endpointId, parameters);
}
/// @notice Stores subscription details
/// @dev `airnode` should make the query specified by `templateId` and
/// `parameters`. If the returned data satisfies `conditions`, it should
/// call `requester`'s `fulfillFunctionId` on `chainId` with the returned
/// data, using the wallet dedicated to `sponsor`.
/// If `relayer` is not `airnode`, the relayer is responsible with checking
/// `condition` and using the wallet dedicated to `sponsor` to deliver the
/// data.
/// In most cases, `conditions` will specify a static call to a function on
/// `chainId` with the data. The extent of its flexibility depends on the
/// node implementation and is outside the scope of the on-chain protocol.
/// Similarly, `conditions` can specify with what frequency it should be
/// verified, and the details of this is outside the scope.
/// `templateId` being zero is similar to the endpoint ID being zero for
/// templates, means the endpoint query can be skipped. In this case,
/// `parameters` will be treated as the data that is returned by the
/// endpoint while verifying `conditions`.
/// @param chainId Chain ID
/// @param airnode Airnode address
/// @param templateId Template ID (allowed to be `bytes32(0)`)
/// @param parameters Parameters provided by the subscription in addition
/// to the parameters in the template, encoded in Airnode ABI
/// @param conditions Conditions under which the subscription is requested
/// to be fulfilled, encoded in Airnode ABI
/// @param relayer Relayer address
/// @param sponsor Sponsor address
/// @param requester Requester address
/// @param fulfillFunctionId Selector of the function to be called for
/// fulfillment
/// @return subscriptionId Subscription ID
function storeSubscription(
uint256 chainId,
address airnode,
bytes32 templateId,
bytes calldata parameters,
bytes calldata conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
) external override returns (bytes32 subscriptionId) {
require(chainId != 0, "Chain ID zero");
require(airnode != address(0), "Airnode address zero");
require(
parameters.length <= MAXIMUM_PARAMETER_LENGTH,
"Parameters too long"
);
require(
conditions.length <= MAXIMUM_PARAMETER_LENGTH,
"Conditions too long"
);
require(relayer != address(0), "Relayer address zero");
require(sponsor != address(0), "Sponsor address zero");
require(requester != address(0), "Requester address zero");
require(fulfillFunctionId != bytes4(0), "Fulfill function ID zero");
subscriptionId = keccak256(
abi.encode(
chainId,
airnode,
templateId,
parameters,
conditions,
relayer,
sponsor,
requester,
fulfillFunctionId
)
);
subscriptions[subscriptionId] = Subscription({
chainId: chainId,
airnode: airnode,
templateId: templateId,
parameters: parameters,
conditions: conditions,
relayer: relayer,
sponsor: sponsor,
requester: requester,
fulfillFunctionId: fulfillFunctionId
});
emit StoredSubscription(
subscriptionId,
chainId,
airnode,
templateId,
parameters,
conditions,
relayer,
sponsor,
requester,
fulfillFunctionId
);
}
}
contracts/protocol/WithdrawalUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./interfaces/IWithdrawalUtils.sol";
/// @title Contract that can be used by sponsors to request withdrawals from
/// sponsor wallets and Airnodes/relayers to fulfill these
/// @notice The respective Airnode/relayer may not support withdrawals for the
/// specified protocol, or at all. Similarly, an Airnode/relayer may deposit
/// funds directly to the sponsor address without being prompted, e.g., because
/// they are ceasing operations. In general, no guarantee is provided for the
/// funds deposited to sponsor wallets at the protocol level. Therefore, the
/// sponsors should limit their deposits to the minimum amount required for
/// their operations, and assume they will not receive these funds back.
/// @dev Withdrawals are implemented in the form of pull payments. The sponsor
/// requests a withdrawal from a sponsor wallet, and the Airnode/relayer uses
/// the specified sponsor wallet to deposit the entire balance at this
/// contract. Then, the sponsor claims/pulls the payment from this contract.
/// Different protocols (RRP, PSP, etc.) use different sponsor wallets for a
/// particular Airnode/relayer–sponsor pair, which is why sponsor wallet
/// derivation includes a protocol ID. Refer to the node documentation for what
/// these protocol IDs are.
contract WithdrawalUtils is IWithdrawalUtils {
using ECDSA for bytes32;
/// @notice Sponsor balance that is withdrawn but not claimed
mapping(address => uint256) public override sponsorToBalance;
/// @notice Number of withdrawal requests the sponsor made
/// @dev This can be used to calculate the ID of the next withdrawal
/// request the sponsor will make
mapping(address => uint256) public override sponsorToWithdrawalRequestCount;
mapping(bytes32 => bytes32) private withdrawalRequestIdToParameters;
/// @notice Called by a sponsor to request a withdrawal
/// @param airnodeOrRelayer Airnode/relayer address
/// @param protocolId Protocol ID
function requestWithdrawal(address airnodeOrRelayer, uint256 protocolId)
external
override
{
require(airnodeOrRelayer != address(0), "Airnode/relayer address zero");
require(protocolId != 0, "Protocol ID zero");
bytes32 withdrawalRequestId = keccak256(
abi.encodePacked(
block.chainid,
address(this),
msg.sender,
++sponsorToWithdrawalRequestCount[msg.sender]
)
);
withdrawalRequestIdToParameters[withdrawalRequestId] = keccak256(
abi.encodePacked(airnodeOrRelayer, protocolId, msg.sender)
);
emit RequestedWithdrawal(
airnodeOrRelayer,
msg.sender,
withdrawalRequestId,
protocolId
);
}
/// @notice Called by the Airnode/relayer using the sponsor wallet to
/// fulfill the withdrawal request made by the sponsor
/// @param withdrawalRequestId Withdrawal request ID
/// @param airnodeOrRelayer Airnode/relayer address
/// @param protocolId Protocol ID
/// @param sponsor Sponsor address
function fulfillWithdrawal(
bytes32 withdrawalRequestId,
address airnodeOrRelayer,
uint256 protocolId,
address sponsor,
uint256 timestamp,
bytes calldata signature
) external payable override {
require(
withdrawalRequestIdToParameters[withdrawalRequestId] ==
keccak256(
abi.encodePacked(airnodeOrRelayer, protocolId, sponsor)
),
"Invalid withdrawal fulfillment"
);
require(
timestamp + 1 hours > block.timestamp &&
timestamp < block.timestamp + 15 minutes,
"Timestamp not valid"
);
require(
(
keccak256(
abi.encodePacked(withdrawalRequestId, timestamp, msg.sender)
).toEthSignedMessageHash()
).recover(signature) == airnodeOrRelayer,
"Signature mismatch"
);
delete withdrawalRequestIdToParameters[withdrawalRequestId];
sponsorToBalance[sponsor] += msg.value;
emit FulfilledWithdrawal(
airnodeOrRelayer,
sponsor,
withdrawalRequestId,
protocolId,
msg.sender,
msg.value
);
}
/// @notice Called by the sponsor to claim the withdrawn funds
/// @dev The sponsor must be able to receive funds. For example, if the
/// sponsor is a contract without a default `payable` function, this will
/// revert.
function claimBalance() external override {
uint256 sponsorBalance = sponsorToBalance[msg.sender];
require(sponsorBalance != 0, "Sender balance zero");
sponsorToBalance[msg.sender] = 0;
emit ClaimedBalance(msg.sender, sponsorBalance);
(bool success, ) = msg.sender.call{value: sponsorBalance}(""); // solhint-disable-line avoid-low-level-calls
require(success, "Transfer failed");
}
/// @notice Returns if the withdrawal request with the ID is made but not
/// fulfilled yet
/// @param withdrawalRequestId Withdrawal request ID
/// @return isAwaitingFulfillment If the withdrawal request is awaiting
/// fulfillment
function withdrawalRequestIsAwaitingFulfillment(bytes32 withdrawalRequestId)
external
view
override
returns (bool)
{
return
withdrawalRequestIdToParameters[withdrawalRequestId] != bytes32(0);
}
}
contracts/protocol/interfaces/IAirnodeProtocol.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IStorageUtils.sol";
import "./ISponsorshipUtils.sol";
import "./IWithdrawalUtils.sol";
interface IAirnodeProtocol is
IStorageUtils,
ISponsorshipUtils,
IWithdrawalUtils
{
event MadeRequest(
address indexed airnode,
bytes32 indexed requestId,
address requester,
uint256 requesterRequestCount,
bytes32 templateId,
bytes parameters,
address sponsor,
bytes4 fulfillFunctionId
);
event FulfilledRequest(
address indexed airnode,
bytes32 indexed requestId,
uint256 timestamp,
bytes data
);
event FailedRequest(
address indexed airnode,
bytes32 indexed requestId,
uint256 timestamp,
string errorMessage
);
event MadeRequestRelayed(
address indexed relayer,
bytes32 indexed requestId,
address indexed airnode,
address requester,
uint256 requesterRequestCount,
bytes32 templateId,
bytes parameters,
address sponsor,
bytes4 fulfillFunctionId
);
event FulfilledRequestRelayed(
address indexed relayer,
bytes32 indexed requestId,
address indexed airnode,
uint256 timestamp,
bytes data
);
event FailedRequestRelayed(
address indexed relayer,
bytes32 indexed requestId,
address indexed airnode,
uint256 timestamp,
string errorMessage
);
function makeRequest(
address airnode,
bytes32 templateId,
bytes calldata parameters,
address sponsor,
bytes4 fulfillFunctionId
) external returns (bytes32 requestId);
function fulfillRequest(
bytes32 requestId,
address airnode,
address requester,
bytes4 fulfillFunctionId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external returns (bool callSuccess, bytes memory callData);
function failRequest(
bytes32 requestId,
address airnode,
address requester,
bytes4 fulfillFunctionId,
uint256 timestamp,
string calldata errorMessage,
bytes calldata signature
) external;
function makeRequestRelayed(
address airnode,
bytes32 templateId,
bytes calldata parameters,
address relayer,
address sponsor,
bytes4 fulfillFunctionId
) external returns (bytes32 requestId);
function fulfillRequestRelayed(
bytes32 requestId,
address airnode,
address requester,
address relayer,
bytes4 fulfillFunctionId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external returns (bool callSuccess, bytes memory callData);
function failRequestRelayed(
bytes32 requestId,
address airnode,
address requester,
address relayer,
bytes4 fulfillFunctionId,
uint256 timestamp,
string calldata errorMessage,
bytes calldata signature
) external;
function requestIsAwaitingFulfillment(bytes32 requestId)
external
view
returns (bool);
function requesterToRequestCount(address requester)
external
view
returns (uint256);
}
contracts/protocol/interfaces/ISponsorshipUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISponsorshipUtils {
event SetRrpSponsorshipStatus(
address indexed sponsor,
address indexed requester,
bool status
);
event SetPspSponsorshipStatus(
address indexed sponsor,
bytes32 indexed subscriptionId,
bool status
);
function setRrpSponsorshipStatus(address requester, bool status) external;
function setPspSponsorshipStatus(bytes32 subscriptionId, bool status)
external;
function sponsorToRequesterToRrpSponsorshipStatus(
address sponsor,
address requester
) external view returns (bool status);
function sponsorToSubscriptionIdToPspSponsorshipStatus(
address sponsor,
bytes32 subscriptionId
) external view returns (bool status);
}
contracts/protocol/interfaces/IStorageUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IStorageUtils {
event StoredTemplate(
bytes32 indexed templateId,
bytes32 endpointId,
bytes parameters
);
event StoredSubscription(
bytes32 indexed subscriptionId,
uint256 chainId,
address airnode,
bytes32 templateId,
bytes parameters,
bytes conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
);
function storeTemplate(bytes32 endpointId, bytes calldata parameters)
external
returns (bytes32 templateId);
function storeSubscription(
uint256 chainId,
address airnode,
bytes32 templateId,
bytes calldata parameters,
bytes calldata conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
) external returns (bytes32 subscriptionId);
// solhint-disable-next-line func-name-mixedcase
function MAXIMUM_PARAMETER_LENGTH() external view returns (uint256);
function templates(bytes32 templateId)
external
view
returns (bytes32 endpointId, bytes memory parameters);
function subscriptions(bytes32 subscriptionId)
external
view
returns (
uint256 chainId,
address airnode,
bytes32 templateId,
bytes memory parameters,
bytes memory conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
);
}
contracts/protocol/interfaces/IWithdrawalUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IWithdrawalUtils {
event RequestedWithdrawal(
address indexed airnodeOrRelayer,
address indexed sponsor,
bytes32 indexed withdrawalRequestId,
uint256 protocolId
);
event FulfilledWithdrawal(
address indexed airnodeOrRelayer,
address indexed sponsor,
bytes32 indexed withdrawalRequestId,
uint256 protocolId,
address sponsorWallet,
uint256 amount
);
event ClaimedBalance(address indexed sponsor, uint256 amount);
function requestWithdrawal(address airnodeOrRelayer, uint256 protocolId)
external;
function fulfillWithdrawal(
bytes32 withdrawalRequestId,
address airnodeOrRelayer,
uint256 protocolId,
address sponsor,
uint256 timestamp,
bytes calldata signature
) external payable;
function claimBalance() external;
function withdrawalRequestIsAwaitingFulfillment(bytes32 withdrawalRequestId)
external
view
returns (bool);
function sponsorToBalance(address sponsor) external view returns (uint256);
function sponsorToWithdrawalRequestCount(address sponsor)
external
view
returns (uint256);
}
contracts/utils/ExtendedMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Multicall.sol";
/// @notice Contract that extends the functionality of Multicall to cover the
/// retrieval of some globally available variables
contract ExtendedMulticall is Multicall {
/// @notice Returns the chain ID
/// @return Chain ID
function getChainId() external view returns (uint256) {
return block.chainid;
}
/// @notice Returns the account balance
/// @param account Account address
/// @return Account balance
function getBalance(address account) external view returns (uint256) {
return account.balance;
}
/// @notice Returns the current block number
/// @return Current block number
function getBlockNumber() external view returns (uint256) {
return block.number;
}
/// @notice Returns the current block timestamp
/// @return Current block timestamp
function getBlockTimestamp() external view returns (uint256) {
return block.timestamp;
}
/// @notice Returns the current block basefee
/// @return Current block basefee
function getBlockBasefee() external view returns (uint256) {
return block.basefee;
}
}
Contract ABI
[{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAXIMUM_PARAMETER_LENGTH","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimBalance","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"failRequest","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"requester","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"string","name":"errorMessage","internalType":"string"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"failRequestRelayed","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"requester","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"string","name":"errorMessage","internalType":"string"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"callSuccess","internalType":"bool"},{"type":"bytes","name":"callData","internalType":"bytes"}],"name":"fulfillRequest","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"requester","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"callSuccess","internalType":"bool"},{"type":"bytes","name":"callData","internalType":"bytes"}],"name":"fulfillRequestRelayed","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"requester","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"fulfillWithdrawal","inputs":[{"type":"bytes32","name":"withdrawalRequestId","internalType":"bytes32"},{"type":"address","name":"airnodeOrRelayer","internalType":"address"},{"type":"uint256","name":"protocolId","internalType":"uint256"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBalance","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockBasefee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getChainId","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}],"name":"makeRequest","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"bytes","name":"parameters","internalType":"bytes"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}],"name":"makeRequestRelayed","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"bytes","name":"parameters","internalType":"bytes"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes[]","name":"results","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"requestIsAwaitingFulfillment","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"requestWithdrawal","inputs":[{"type":"address","name":"airnodeOrRelayer","internalType":"address"},{"type":"uint256","name":"protocolId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"requesterToRequestCount","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPspSponsorshipStatus","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRrpSponsorshipStatus","inputs":[{"type":"address","name":"requester","internalType":"address"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"sponsorToBalance","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"sponsorToRequesterToRrpSponsorshipStatus","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"sponsorToSubscriptionIdToPspSponsorshipStatus","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"sponsorToWithdrawalRequestCount","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"}],"name":"storeSubscription","inputs":[{"type":"uint256","name":"chainId","internalType":"uint256"},{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"bytes","name":"parameters","internalType":"bytes"},{"type":"bytes","name":"conditions","internalType":"bytes"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"address","name":"requester","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"templateId","internalType":"bytes32"}],"name":"storeTemplate","inputs":[{"type":"bytes32","name":"endpointId","internalType":"bytes32"},{"type":"bytes","name":"parameters","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"chainId","internalType":"uint256"},{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"bytes","name":"parameters","internalType":"bytes"},{"type":"bytes","name":"conditions","internalType":"bytes"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"address","name":"requester","internalType":"address"},{"type":"bytes4","name":"fulfillFunctionId","internalType":"bytes4"}],"name":"subscriptions","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"endpointId","internalType":"bytes32"},{"type":"bytes","name":"parameters","internalType":"bytes"}],"name":"templates","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"withdrawalRequestIsAwaitingFulfillment","inputs":[{"type":"bytes32","name":"withdrawalRequestId","internalType":"bytes32"}]},{"type":"event","name":"ClaimedBalance","inputs":[{"type":"address","name":"sponsor","indexed":true},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"FailedRequest","inputs":[{"type":"address","name":"airnode","indexed":true},{"type":"bytes32","name":"requestId","indexed":true},{"type":"uint256","name":"timestamp","indexed":false},{"type":"string","name":"errorMessage","indexed":false}],"anonymous":false},{"type":"event","name":"FailedRequestRelayed","inputs":[{"type":"address","name":"relayer","indexed":true},{"type":"bytes32","name":"requestId","indexed":true},{"type":"address","name":"airnode","indexed":true},{"type":"uint256","name":"timestamp","indexed":false},{"type":"string","name":"errorMessage","indexed":false}],"anonymous":false},{"type":"event","name":"FulfilledRequest","inputs":[{"type":"address","name":"airnode","indexed":true},{"type":"bytes32","name":"requestId","indexed":true},{"type":"uint256","name":"timestamp","indexed":false},{"type":"bytes","name":"data","indexed":false}],"anonymous":false},{"type":"event","name":"FulfilledRequestRelayed","inputs":[{"type":"address","name":"relayer","indexed":true},{"type":"bytes32","name":"requestId","indexed":true},{"type":"address","name":"airnode","indexed":true},{"type":"uint256","name":"timestamp","indexed":false},{"type":"bytes","name":"data","indexed":false}],"anonymous":false},{"type":"event","name":"FulfilledWithdrawal","inputs":[{"type":"address","name":"airnodeOrRelayer","indexed":true},{"type":"address","name":"sponsor","indexed":true},{"type":"bytes32","name":"withdrawalRequestId","indexed":true},{"type":"uint256","name":"protocolId","indexed":false},{"type":"address","name":"sponsorWallet","indexed":false},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"MadeRequest","inputs":[{"type":"address","name":"airnode","indexed":true},{"type":"bytes32","name":"requestId","indexed":true},{"type":"address","name":"requester","indexed":false},{"type":"uint256","name":"requesterRequestCount","indexed":false},{"type":"bytes32","name":"templateId","indexed":false},{"type":"bytes","name":"parameters","indexed":false},{"type":"address","name":"sponsor","indexed":false},{"type":"bytes4","name":"fulfillFunctionId","indexed":false}],"anonymous":false},{"type":"event","name":"MadeRequestRelayed","inputs":[{"type":"address","name":"relayer","indexed":true},{"type":"bytes32","name":"requestId","indexed":true},{"type":"address","name":"airnode","indexed":true},{"type":"address","name":"requester","indexed":false},{"type":"uint256","name":"requesterRequestCount","indexed":false},{"type":"bytes32","name":"templateId","indexed":false},{"type":"bytes","name":"parameters","indexed":false},{"type":"address","name":"sponsor","indexed":false},{"type":"bytes4","name":"fulfillFunctionId","indexed":false}],"anonymous":false},{"type":"event","name":"RequestedWithdrawal","inputs":[{"type":"address","name":"airnodeOrRelayer","indexed":true},{"type":"address","name":"sponsor","indexed":true},{"type":"bytes32","name":"withdrawalRequestId","indexed":true},{"type":"uint256","name":"protocolId","indexed":false}],"anonymous":false},{"type":"event","name":"SetPspSponsorshipStatus","inputs":[{"type":"address","name":"sponsor","indexed":true},{"type":"bytes32","name":"subscriptionId","indexed":true},{"type":"bool","name":"status","indexed":false}],"anonymous":false},{"type":"event","name":"SetRrpSponsorshipStatus","inputs":[{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"requester","indexed":true},{"type":"bool","name":"status","indexed":false}],"anonymous":false},{"type":"event","name":"StoredSubscription","inputs":[{"type":"bytes32","name":"subscriptionId","indexed":true},{"type":"uint256","name":"chainId","indexed":false},{"type":"address","name":"airnode","indexed":false},{"type":"bytes32","name":"templateId","indexed":false},{"type":"bytes","name":"parameters","indexed":false},{"type":"bytes","name":"conditions","indexed":false},{"type":"address","name":"relayer","indexed":false},{"type":"address","name":"sponsor","indexed":false},{"type":"address","name":"requester","indexed":false},{"type":"bytes4","name":"fulfillFunctionId","indexed":false}],"anonymous":false},{"type":"event","name":"StoredTemplate","inputs":[{"type":"bytes32","name":"templateId","indexed":true},{"type":"bytes32","name":"endpointId","indexed":false},{"type":"bytes","name":"parameters","indexed":false}],"anonymous":false}]
Contract Creation Code
0x608060405234801561001057600080fd5b5061379b806100206000396000f3fe6080604052600436106101c25760003560e01c8063796b89b9116100f7578063ac9650d811610095578063eacd06e511610064578063eacd06e514610562578063eebecf6914610582578063f8b2cb4f146105a2578063f8fa73a1146105ca57600080fd5b8063ac9650d8146104e2578063c5a9550f1461050f578063da95ebf71461052f578063dfdeaef41461054f57600080fd5b806394259c6c116100d157806394259c6c1461044d5780639d6a6bf114610482578063a7e0c85e146104a2578063abb67ca5146104c257600080fd5b8063796b89b9146103df57806389ebd555146103f25780638ae59e0e1461041257600080fd5b806345ac480d116101645780634dcc19fe1161013e5780634dcc19fe1461035057806350743bb9146103635780636014a2b2146103925780636d6d2ca0146103bf57600080fd5b806345ac480d146102ed5780634b1c1a47146103035780634b8985d91461033057600080fd5b806330509bca116101a057806330509bca1461026b5780633408e4701461028257806336f0cd751461029f57806342cbb15c146102da57600080fd5b80630a631576146101c75780630c8eeead146101fe5780632bdf46e71461022c575b600080fd5b3480156101d357600080fd5b506101e76101e2366004612bf1565b6105f7565b6040516101f5929190612c66565b60405180910390f35b34801561020a57600080fd5b5061021e610219366004612cfd565b61069c565b6040516101f5929190612db4565b34801561023857600080fd5b5061025b610247366004612bf1565b600090815260066020526040902054151590565b60405190151581526020016101f5565b34801561027757600080fd5b50610280610a2d565b005b34801561028e57600080fd5b50465b6040519081526020016101f5565b3480156102ab57600080fd5b5061025b6102ba366004612dcf565b600260209081526000928352604080842090915290825290205460ff1681565b3480156102e657600080fd5b5043610291565b3480156102f957600080fd5b5061029161100081565b34801561030f57600080fd5b5061029161031e366004612e02565b60046020526000908152604090205481565b34801561033c57600080fd5b5061029161034b366004612e1d565b610b72565b34801561035c57600080fd5b5048610291565b34801561036f57600080fd5b5061025b61037e366004612bf1565b600090815260086020526040902054151590565b34801561039e57600080fd5b506102916103ad366004612e02565b60076020526000908152604090205481565b3480156103cb57600080fd5b506102916103da366004612e69565b610ca4565b3480156103eb57600080fd5b5042610291565b3480156103fe57600080fd5b5061028061040d366004612cfd565b6111c2565b34801561041e57600080fd5b5061025b61042d366004612f43565b600360209081526000928352604080842090915290825290205460ff1681565b34801561045957600080fd5b5061046d610468366004612bf1565b61139d565b6040516101f599989796959493929190612f6d565b34801561048e57600080fd5b5061021e61049d366004612fe8565b611515565b3480156104ae57600080fd5b506102916104bd3660046130b1565b61183f565b3480156104ce57600080fd5b506102806104dd366004613151565b611b52565b3480156104ee57600080fd5b506105026104fd36600461317b565b611c15565b6040516101f591906131f0565b34801561051b57600080fd5b5061028061052a366004612fe8565b611d0a565b34801561053b57600080fd5b5061028061054a366004612f43565b611ef5565b61028061055d366004613252565b6120ae565b34801561056e57600080fd5b5061028061057d3660046132d4565b612316565b34801561058e57600080fd5b5061029161059d3660046132f7565b6123bd565b3480156105ae57600080fd5b506102916105bd366004612e02565b6001600160a01b03163190565b3480156105d657600080fd5b506102916105e5366004612e02565b60056020526000908152604090205481565b6000602081905290815260409020805460018201805491929161061990613375565b80601f016020809104026020016040519081016040528092919081815260200182805461064590613375565b80156106925780601f1061066757610100808354040283529160200191610692565b820191906000526020600020905b81548152906001019060200180831161067557829003601f168201915b5050505050905082565b60008981526008602090815260408083205490516001600160601b031960608d811b8216948301949094528b841b1660348201526001600160e01b03198a166048820152604c01604051602081830303815290604052805190602001201461074b5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e74000000000060448201526064015b60405180910390fd5b896001600160a01b031661083985858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506108338e8b336040516020016107d393929190928352602083019190915260601b6001600160601b031916604082015260540190565b60408051601f1981840301815282825280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000084830152603c8085019190915282518085039091018152605c909301909152815191012090565b9061266e565b6001600160a01b0316146108845760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b60008b81526008602052604080822091909155516001600160a01b038a169089906108b9908e908b908b908b906024016133d9565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990941693909317909252905161090c91906133f9565b6000604051808303816000865af19150503d8060008114610949576040519150601f19603f3d011682016040523d82523d6000602084013e61094e565b606091505b50909250905081156109a5578a8a6001600160a01b03167fd1cc11d12363af4b6022e66d14b18ba1779ecd85a5b41891349d530fb6eee06689898960405161099893929190613415565b60405180910390a3610a1f565b8a8a6001600160a01b03167faee73eafb4f543f82048dfc7d8c9a4ee62506ffa76e0051b749d102c4ac7924c89604051610a16918152604060208201819052601f908201527f46756c66696c6c6d656e74206661696c656420756e65787065637465646c7900606082015260800190565b60405180910390a35b995099975050505050505050565b3360009081526004602052604090205480610a8a5760405162461bcd60e51b815260206004820152601360248201527f53656e6465722062616c616e6365207a65726f000000000000000000000000006044820152606401610742565b3360008181526004602052604080822091909155517ffa58005128630bfb12e51c379d9bed40a8c3d7e8fdaec50028f79074fa41c7c390610ace9084815260200190565b60405180910390a2604051600090339083908381818185875af1925050503d8060008114610b18576040519150601f19603f3d011682016040523d82523d6000602084013e610b1d565b606091505b5050905080610b6e5760405162461bcd60e51b815260206004820152600f60248201527f5472616e73666572206661696c656400000000000000000000000000000000006044820152606401610742565b5050565b6000611000821115610bbc5760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b838383604051602001610bd193929190613438565b604051602081830303815290604052805190602001209050604051806040016040528085815260200184848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509390945250508381526020818152604090912083518155838201518051919350610c5d926001850192910190612b58565b50905050807f40743c23e28a8b92f5219dee916cac13f53334209cee6dd9b7fd365e6607c8e7858585604051610c9593929190613415565b60405180910390a29392505050565b60008b610cf35760405162461bcd60e51b815260206004820152600d60248201527f436861696e204944207a65726f000000000000000000000000000000000000006044820152606401610742565b6001600160a01b038b16610d495760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f0000000000000000000000006044820152606401610742565b611000881115610d915760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b611000861115610de35760405162461bcd60e51b815260206004820152601360248201527f436f6e646974696f6e7320746f6f206c6f6e67000000000000000000000000006044820152606401610742565b6001600160a01b038516610e395760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160a01b038416610e8f5760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160a01b038316610ee55760405162461bcd60e51b815260206004820152601660248201527f5265717565737465722061646472657373207a65726f000000000000000000006044820152606401610742565b6001600160e01b03198216610f3c5760405162461bcd60e51b815260206004820152601860248201527f46756c66696c6c2066756e6374696f6e204944207a65726f00000000000000006044820152606401610742565b8b8b8b8b8b8b8b8b8b8b8b604051602001610f619b9a99989796959493929190613452565b6040516020818303038152906040528051906020012090506040518061012001604052808d81526020018c6001600160a01b031681526020018b81526020018a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250604080516020601f8b0181900481028201810190925289815291810191908a908a908190840183828082843760009201829052509385525050506001600160a01b038881166020808501919091528882166040808601919091528883166060808701919091526001600160e01b031989166080909601959095528684526001808352938190208651815586830151948101805473ffffffffffffffffffffffffffffffffffffffff1916959094169490941790925590840151600283015591830151805191926110b192600385019290910190612b58565b50608082015180516110cd916004840191602090910190612b58565b5060a08201516005820180546001600160a01b0392831673ffffffffffffffffffffffffffffffffffffffff199182161790915560c08401516006840180549184169190921617905560e080840151600790930180546101009095015190911c600160a01b027fffffffffffffffff000000000000000000000000000000000000000000000000909416929091169190911791909117905560405181907f544f5b2023290d39a7c3271c6ff6f3f0720e696fa4459d41c949cc02a98314ba906111ab908f908f908f908f908f908f908f908f908f908f908f90613452565b60405180910390a29b9a5050505050505050505050565b6000898152600860209081526040918290205491516001600160601b031960608c811b821693830193909352918a901b90911660348201526001600160e01b031988166048820152604c0160405160208183030381529060405280519060200120146112705760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e7400000000006044820152606401610742565b876001600160a01b03166112ea83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060405161083392506107d391508e908b903390602001928352602083019190915260601b6001600160601b031916604082015260540190565b6001600160a01b0316146113355760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b600860008a81526020019081526020016000206000905588886001600160a01b03167faee73eafb4f543f82048dfc7d8c9a4ee62506ffa76e0051b749d102c4ac7924c87878760405161138a93929190613415565b60405180910390a3505050505050505050565b600160208190526000918252604090912080549181015460028201546003830180546001600160a01b039093169391926113d690613375565b80601f016020809104026020016040519081016040528092919081815260200182805461140290613375565b801561144f5780601f106114245761010080835404028352916020019161144f565b820191906000526020600020905b81548152906001019060200180831161143257829003601f168201915b50505050509080600401805461146490613375565b80601f016020809104026020016040519081016040528092919081815260200182805461149090613375565b80156114dd5780601f106114b2576101008083540402835291602001916114dd565b820191906000526020600020905b8154815290600101906020018083116114c057829003601f168201915b5050506005840154600685015460079095015493946001600160a01b039182169490821693509081169150600160a01b900460e01b89565b60008a81526008602090815260408083205490516001600160601b031960608e811b8216948301949094528c841b811660348301528b841b1660488201526001600160e01b03198a16605c820152820160405160208183030381529060405280519060200120146115c85760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e7400000000006044820152606401610742565b8a6001600160a01b031661163685858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506108338f8b338c8c6040516020016107d39594939291906134d1565b6001600160a01b0316146116815760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b60008c81526008602052604080822091909155516001600160a01b038b169089906116b6908f908b908b908b906024016133d9565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990941693909317909252905161170991906133f9565b6000604051808303816000865af19150503d8060008114611746576040519150601f19603f3d011682016040523d82523d6000602084013e61174b565b606091505b50909250905081156117ac578a6001600160a01b03168c8a6001600160a01b03167fbc2b56fbde291a214ed04ac9fd94c2e53c29b63d1cbdacedfe922d7523cf69eb8a8a8a60405161179f93929190613415565b60405180910390a4611830565b8a6001600160a01b03168c8a6001600160a01b03167f5bfd527f98a7329226e18789dcd6caead784424ffb409bae4584f8e88ee398498a604051611827918152604060208201819052601f908201527f46756c66696c6c6d656e74206661696c656420756e65787065637465646c7900606082015260800190565b60405180910390a45b9a509a98505050505050505050565b60006001600160a01b0388166118975760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f0000000000000000000000006044820152606401610742565b866118e45760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f000000000000000000000000000000006044820152606401610742565b61100085111561192c5760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b6001600160a01b0384166119825760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160a01b0383166119d85760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160e01b03198216611a2f5760405162461bcd60e51b815260206004820152601860248201527f46756c66696c6c2066756e6374696f6e204944207a65726f00000000000000006044820152606401610742565b33600090815260076020526040812080548290611a4b90613521565b9190508190559050463033838c8c8c8c8c8c8c604051602001611a789b9a9998979695949392919061353c565b60408051601f198184030181529082905280516020918201206001600160601b031960608d811b82169385019390935233831b8116603485015288831b1660488401526001600160e01b03198616605c84015293500160408051601f19818403018152828252805160209182012060008681526008909252919020556001600160a01b03808b169184918816907fb1fea87872f337aefbe87f34814e8769f6f84c265bf74098ac9122c4b6268cbc90611b3e90339087908f908f908f908e908e906135be565b60405180910390a450979650505050505050565b6001600160a01b038216611ba85760405162461bcd60e51b815260206004820152601660248201527f5265717565737465722061646472657373207a65726f000000000000000000006044820152606401610742565b3360008181526002602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917ff2b2684e2948004382be1d231e64abcee2a088028b4a47e7a800ba9aa9dc966291015b60405180910390a35050565b60608167ffffffffffffffff811115611c3057611c30613612565b604051908082528060200260200182016040528015611c6357816020015b6060815260200190600190039081611c4e5790505b50905060005b82811015611d0357611cd330858584818110611c8757611c87613628565b9050602002810190611c99919061363e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061269292505050565b828281518110611ce557611ce5613628565b60200260200101819052508080611cfb90613521565b915050611c69565b5092915050565b60008a8152600860209081526040918290205491516001600160601b031960608d811b8216938301939093528b831b811660348301528a831b1660488201526001600160e01b03198916605c820152016040516020818303038152906040528051906020012014611dbd5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e7400000000006044820152606401610742565b866001600160a01b0316611e3783838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060405161083392506107d391508f908b903390602001928352602083019190915260601b6001600160601b031916604082015260540190565b6001600160a01b031614611e825760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b600860008b815260200190815260200160002060009055886001600160a01b03168a886001600160a01b03167f5bfd527f98a7329226e18789dcd6caead784424ffb409bae4584f8e88ee39849888888604051611ee193929190613415565b60405180910390a450505050505050505050565b6001600160a01b038216611f4b5760405162461bcd60e51b815260206004820152601c60248201527f4169726e6f64652f72656c617965722061646472657373207a65726f000000006044820152606401610742565b80611f985760405162461bcd60e51b815260206004820152601060248201527f50726f746f636f6c204944207a65726f000000000000000000000000000000006044820152606401610742565b3360008181526005602052604081208054919246923092908590611fbb90613521565b91829055506040805160208101959095526001600160601b0319606094851b8116918601919091529190921b166054830152606882015260880160408051601f198184030181529082905280516020918201206001600160601b0319606087811b821693850193909352603484018690523390921b9091166054830152915060680160408051601f1981840301815282825280516020918201206000858152600690925291902055819033906001600160a01b038616907fe97817ee3466ddc9745f4712aa283248845d739fae3db92899a018300c03119d906120a19087815260200190565b60405180910390a4505050565b6040516001600160601b0319606088811b821660208401526034830188905286901b16605482015260680160408051601f19818403018152918152815160209283012060008a815260069093529120541461214b5760405162461bcd60e51b815260206004820152601e60248201527f496e76616c6964207769746864726177616c2066756c66696c6c6d656e7400006044820152606401610742565b4261215884610e10613685565b11801561216f575061216c42610384613685565b83105b6121bb5760405162461bcd60e51b815260206004820152601360248201527f54696d657374616d70206e6f742076616c6964000000000000000000000000006044820152606401610742565b856001600160a01b031661223583838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060405161083392506107d391508c9089903390602001928352602083019190915260601b6001600160601b031916604082015260540190565b6001600160a01b0316146122805760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b60008781526006602090815260408083208390556001600160a01b03871683526004909152812080543492906122b7908490613685565b9091555050604080518681523360208201523481830152905188916001600160a01b0387811692908a16917f9d4a5358c05c3610156fa7112e5ab33a373ee3d8aab7d6d0477d60eaf8c129f2919081900360600190a450505050505050565b816123635760405162461bcd60e51b815260206004820152601460248201527f537562736372697074696f6e204944207a65726f0000000000000000000000006044820152606401610742565b336000818152600360209081526040808320868452825291829020805460ff191685151590811790915591519182528492917f8307a3c18068fc6f3ad98bcadc19671fb7855dbd3e44d42501c4cced3e732a7f9101611c09565b60006001600160a01b0387166124155760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f0000000000000000000000006044820152606401610742565b856124625760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f000000000000000000000000000000006044820152606401610742565b6110008411156124aa5760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b6001600160a01b0383166125005760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160e01b031982166125575760405162461bcd60e51b815260206004820152601860248201527f46756c66696c6c2066756e6374696f6e204944207a65726f00000000000000006044820152606401610742565b3360009081526007602052604081208054829061257390613521565b9190508190559050463033838b8b8b8b8b8b60405160200161259e9a9998979695949392919061369d565b60408051601f198184030181529082905280516020918201206001600160601b031960608c811b8216938501939093523390921b90911660348301526001600160e01b0319851660488301529250604c0160408051601f198184030181528282528051602091820120600086815260089092529190205582906001600160a01b038a16907f6537a04eb75e255385ee16033b5cb61105ea1b3ab614f6455fcad6ee88a601739061265b90339086908d908d908d908d908d906135be565b60405180910390a3509695505050505050565b600080600061267d85856126be565b9150915061268a8161272e565b509392505050565b60606126b7838360405180606001604052806027815260200161373f602791396128ec565b9392505050565b6000808251604114156126f55760208301516040840151606085015160001a6126e9878285856129e0565b94509450505050612727565b82516040141561271f5760208301516040840151612714868383612acd565b935093505050612727565b506000905060025b9250929050565b600081600481111561274257612742613715565b141561274b5750565b600181600481111561275f5761275f613715565b14156127ad5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610742565b60028160048111156127c1576127c1613715565b141561280f5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610742565b600381600481111561282357612823613715565b141561287c5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610742565b600481600481111561289057612890613715565b14156128e95760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610742565b50565b60606001600160a01b0384163b61296b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610742565b600080856001600160a01b03168560405161298691906133f9565b600060405180830381855af49150503d80600081146129c1576040519150601f19603f3d011682016040523d82523d6000602084013e6129c6565b606091505b50915091506129d6828286612b1f565b9695505050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612a175750600090506003612ac4565b8460ff16601b14158015612a2f57508460ff16601c14155b15612a405750600090506004612ac4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612a94573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612abd57600060019250925050612ac4565b9150600090505b94509492505050565b6000807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831681612b0360ff86901c601b613685565b9050612b11878288856129e0565b935093505050935093915050565b60608315612b2e5750816126b7565b825115612b3e5782518084602001fd5b8160405162461bcd60e51b8152600401610742919061372b565b828054612b6490613375565b90600052602060002090601f016020900481019282612b865760008555612bcc565b82601f10612b9f57805160ff1916838001178555612bcc565b82800160010185558215612bcc579182015b82811115612bcc578251825591602001919060010190612bb1565b50612bd8929150612bdc565b5090565b5b80821115612bd85760008155600101612bdd565b600060208284031215612c0357600080fd5b5035919050565b60005b83811015612c25578181015183820152602001612c0d565b83811115612c34576000848401525b50505050565b60008151808452612c52816020860160208601612c0a565b601f01601f19169290920160200192915050565b828152604060208201526000612c7f6040830184612c3a565b949350505050565b80356001600160a01b0381168114612c9e57600080fd5b919050565b80356001600160e01b031981168114612c9e57600080fd5b60008083601f840112612ccd57600080fd5b50813567ffffffffffffffff811115612ce557600080fd5b60208301915083602082850101111561272757600080fd5b600080600080600080600080600060e08a8c031215612d1b57600080fd5b89359850612d2b60208b01612c87565b9750612d3960408b01612c87565b9650612d4760608b01612ca3565b955060808a0135945060a08a013567ffffffffffffffff80821115612d6b57600080fd5b612d778d838e01612cbb565b909650945060c08c0135915080821115612d9057600080fd5b50612d9d8c828d01612cbb565b915080935050809150509295985092959850929598565b8215158152604060208201526000612c7f6040830184612c3a565b60008060408385031215612de257600080fd5b612deb83612c87565b9150612df960208401612c87565b90509250929050565b600060208284031215612e1457600080fd5b6126b782612c87565b600080600060408486031215612e3257600080fd5b83359250602084013567ffffffffffffffff811115612e5057600080fd5b612e5c86828701612cbb565b9497909650939450505050565b60008060008060008060008060008060006101208c8e031215612e8b57600080fd5b8b359a50612e9b60208d01612c87565b995060408c0135985067ffffffffffffffff8060608e01351115612ebe57600080fd5b612ece8e60608f01358f01612cbb565b909950975060808d0135811015612ee457600080fd5b50612ef58d60808e01358e01612cbb565b9096509450612f0660a08d01612c87565b9350612f1460c08d01612c87565b9250612f2260e08d01612c87565b9150612f316101008d01612ca3565b90509295989b509295989b9093969950565b60008060408385031215612f5657600080fd5b612f5f83612c87565b946020939093013593505050565b60006101208b83526001600160a01b03808c1660208501528a6040850152816060850152612f9d8285018b612c3a565b91508382036080850152612fb1828a612c3a565b97811660a085015295861660c084015250509190921660e08201526001600160e01b03199091166101009091015295945050505050565b6000806000806000806000806000806101008b8d03121561300857600080fd5b8a35995061301860208c01612c87565b985061302660408c01612c87565b975061303460608c01612c87565b965061304260808c01612ca3565b955060a08b0135945060c08b013567ffffffffffffffff8082111561306657600080fd5b6130728e838f01612cbb565b909650945060e08d013591508082111561308b57600080fd5b506130988d828e01612cbb565b915080935050809150509295989b9194979a5092959850565b600080600080600080600060c0888a0312156130cc57600080fd5b6130d588612c87565b965060208801359550604088013567ffffffffffffffff8111156130f857600080fd5b6131048a828b01612cbb565b9096509450613117905060608901612c87565b925061312560808901612c87565b915061313360a08901612ca3565b905092959891949750929550565b80358015158114612c9e57600080fd5b6000806040838503121561316457600080fd5b61316d83612c87565b9150612df960208401613141565b6000806020838503121561318e57600080fd5b823567ffffffffffffffff808211156131a657600080fd5b818501915085601f8301126131ba57600080fd5b8135818111156131c957600080fd5b8660208260051b85010111156131de57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561324557603f19888603018452613233858351612c3a565b94509285019290850190600101613217565b5092979650505050505050565b600080600080600080600060c0888a03121561326d57600080fd5b8735965061327d60208901612c87565b95506040880135945061329260608901612c87565b93506080880135925060a088013567ffffffffffffffff8111156132b557600080fd5b6132c18a828b01612cbb565b989b979a50959850939692959293505050565b600080604083850312156132e757600080fd5b82359150612df960208401613141565b60008060008060008060a0878903121561331057600080fd5b61331987612c87565b955060208701359450604087013567ffffffffffffffff81111561333c57600080fd5b61334889828a01612cbb565b909550935061335b905060608801612c87565b915061336960808801612ca3565b90509295509295509295565b600181811c9082168061338957607f821691505b602082108114156133aa57634e487b7160e01b600052602260045260246000fd5b50919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481528360208201526060604082015260006129d66060830184866133b0565b6000825161340b818460208701612c0a565b9190910192915050565b83815260406020820152600061342f6040830184866133b0565b95945050505050565b838152818360208301376000910160200190815292915050565b60006101208d83526001600160a01b03808e1660208501528c60408501528160608501526134838285018c8e6133b0565b91508382036080850152613498828a8c6133b0565b97811660a085015295861660c084015250509190921660e08201526001600160e01b031990911661010090910152979650505050505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b634e487b7160e01b600052601160045260246000fd5b60006000198214156135355761353561350b565b5060010190565b8b815260006bffffffffffffffffffffffff19808d60601b166020840152808c60601b1660348401528a6048840152808a60601b16606884015288607c8401528688609c850137606095861b811696909201609c810196909652509190921b1660b08301526001600160e01b03191660c482015260c801979650505050505050565b60006001600160a01b03808a16835288602084015287604084015260c060608401526135ee60c0840187896133b0565b94166080830152506001600160e01b03199190911660a09091015295945050505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261365557600080fd5b83018035915067ffffffffffffffff82111561367057600080fd5b60200191503681900382131561272757600080fd5b600082198211156136985761369861350b565b500190565b8a815260006bffffffffffffffffffffffff19808c60601b166020840152808b60601b166034840152896048840152808960601b16606884015287607c8401528587609c85013760609490941b9093169301609c8101939093526001600160e01b03191660b08301525060b401979650505050505050565b634e487b7160e01b600052602160045260246000fd5b6020815260006126b76020830184612c3a56fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e6af691e69c5d655ec7258789eca4faeabb529d5be4c12549ff0c0be771d8f3464736f6c63430008090033
Deployed ByteCode
0x6080604052600436106101c25760003560e01c8063796b89b9116100f7578063ac9650d811610095578063eacd06e511610064578063eacd06e514610562578063eebecf6914610582578063f8b2cb4f146105a2578063f8fa73a1146105ca57600080fd5b8063ac9650d8146104e2578063c5a9550f1461050f578063da95ebf71461052f578063dfdeaef41461054f57600080fd5b806394259c6c116100d157806394259c6c1461044d5780639d6a6bf114610482578063a7e0c85e146104a2578063abb67ca5146104c257600080fd5b8063796b89b9146103df57806389ebd555146103f25780638ae59e0e1461041257600080fd5b806345ac480d116101645780634dcc19fe1161013e5780634dcc19fe1461035057806350743bb9146103635780636014a2b2146103925780636d6d2ca0146103bf57600080fd5b806345ac480d146102ed5780634b1c1a47146103035780634b8985d91461033057600080fd5b806330509bca116101a057806330509bca1461026b5780633408e4701461028257806336f0cd751461029f57806342cbb15c146102da57600080fd5b80630a631576146101c75780630c8eeead146101fe5780632bdf46e71461022c575b600080fd5b3480156101d357600080fd5b506101e76101e2366004612bf1565b6105f7565b6040516101f5929190612c66565b60405180910390f35b34801561020a57600080fd5b5061021e610219366004612cfd565b61069c565b6040516101f5929190612db4565b34801561023857600080fd5b5061025b610247366004612bf1565b600090815260066020526040902054151590565b60405190151581526020016101f5565b34801561027757600080fd5b50610280610a2d565b005b34801561028e57600080fd5b50465b6040519081526020016101f5565b3480156102ab57600080fd5b5061025b6102ba366004612dcf565b600260209081526000928352604080842090915290825290205460ff1681565b3480156102e657600080fd5b5043610291565b3480156102f957600080fd5b5061029161100081565b34801561030f57600080fd5b5061029161031e366004612e02565b60046020526000908152604090205481565b34801561033c57600080fd5b5061029161034b366004612e1d565b610b72565b34801561035c57600080fd5b5048610291565b34801561036f57600080fd5b5061025b61037e366004612bf1565b600090815260086020526040902054151590565b34801561039e57600080fd5b506102916103ad366004612e02565b60076020526000908152604090205481565b3480156103cb57600080fd5b506102916103da366004612e69565b610ca4565b3480156103eb57600080fd5b5042610291565b3480156103fe57600080fd5b5061028061040d366004612cfd565b6111c2565b34801561041e57600080fd5b5061025b61042d366004612f43565b600360209081526000928352604080842090915290825290205460ff1681565b34801561045957600080fd5b5061046d610468366004612bf1565b61139d565b6040516101f599989796959493929190612f6d565b34801561048e57600080fd5b5061021e61049d366004612fe8565b611515565b3480156104ae57600080fd5b506102916104bd3660046130b1565b61183f565b3480156104ce57600080fd5b506102806104dd366004613151565b611b52565b3480156104ee57600080fd5b506105026104fd36600461317b565b611c15565b6040516101f591906131f0565b34801561051b57600080fd5b5061028061052a366004612fe8565b611d0a565b34801561053b57600080fd5b5061028061054a366004612f43565b611ef5565b61028061055d366004613252565b6120ae565b34801561056e57600080fd5b5061028061057d3660046132d4565b612316565b34801561058e57600080fd5b5061029161059d3660046132f7565b6123bd565b3480156105ae57600080fd5b506102916105bd366004612e02565b6001600160a01b03163190565b3480156105d657600080fd5b506102916105e5366004612e02565b60056020526000908152604090205481565b6000602081905290815260409020805460018201805491929161061990613375565b80601f016020809104026020016040519081016040528092919081815260200182805461064590613375565b80156106925780601f1061066757610100808354040283529160200191610692565b820191906000526020600020905b81548152906001019060200180831161067557829003601f168201915b5050505050905082565b60008981526008602090815260408083205490516001600160601b031960608d811b8216948301949094528b841b1660348201526001600160e01b03198a166048820152604c01604051602081830303815290604052805190602001201461074b5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e74000000000060448201526064015b60405180910390fd5b896001600160a01b031661083985858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506108338e8b336040516020016107d393929190928352602083019190915260601b6001600160601b031916604082015260540190565b60408051601f1981840301815282825280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000084830152603c8085019190915282518085039091018152605c909301909152815191012090565b9061266e565b6001600160a01b0316146108845760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b60008b81526008602052604080822091909155516001600160a01b038a169089906108b9908e908b908b908b906024016133d9565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990941693909317909252905161090c91906133f9565b6000604051808303816000865af19150503d8060008114610949576040519150601f19603f3d011682016040523d82523d6000602084013e61094e565b606091505b50909250905081156109a5578a8a6001600160a01b03167fd1cc11d12363af4b6022e66d14b18ba1779ecd85a5b41891349d530fb6eee06689898960405161099893929190613415565b60405180910390a3610a1f565b8a8a6001600160a01b03167faee73eafb4f543f82048dfc7d8c9a4ee62506ffa76e0051b749d102c4ac7924c89604051610a16918152604060208201819052601f908201527f46756c66696c6c6d656e74206661696c656420756e65787065637465646c7900606082015260800190565b60405180910390a35b995099975050505050505050565b3360009081526004602052604090205480610a8a5760405162461bcd60e51b815260206004820152601360248201527f53656e6465722062616c616e6365207a65726f000000000000000000000000006044820152606401610742565b3360008181526004602052604080822091909155517ffa58005128630bfb12e51c379d9bed40a8c3d7e8fdaec50028f79074fa41c7c390610ace9084815260200190565b60405180910390a2604051600090339083908381818185875af1925050503d8060008114610b18576040519150601f19603f3d011682016040523d82523d6000602084013e610b1d565b606091505b5050905080610b6e5760405162461bcd60e51b815260206004820152600f60248201527f5472616e73666572206661696c656400000000000000000000000000000000006044820152606401610742565b5050565b6000611000821115610bbc5760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b838383604051602001610bd193929190613438565b604051602081830303815290604052805190602001209050604051806040016040528085815260200184848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509390945250508381526020818152604090912083518155838201518051919350610c5d926001850192910190612b58565b50905050807f40743c23e28a8b92f5219dee916cac13f53334209cee6dd9b7fd365e6607c8e7858585604051610c9593929190613415565b60405180910390a29392505050565b60008b610cf35760405162461bcd60e51b815260206004820152600d60248201527f436861696e204944207a65726f000000000000000000000000000000000000006044820152606401610742565b6001600160a01b038b16610d495760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f0000000000000000000000006044820152606401610742565b611000881115610d915760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b611000861115610de35760405162461bcd60e51b815260206004820152601360248201527f436f6e646974696f6e7320746f6f206c6f6e67000000000000000000000000006044820152606401610742565b6001600160a01b038516610e395760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160a01b038416610e8f5760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160a01b038316610ee55760405162461bcd60e51b815260206004820152601660248201527f5265717565737465722061646472657373207a65726f000000000000000000006044820152606401610742565b6001600160e01b03198216610f3c5760405162461bcd60e51b815260206004820152601860248201527f46756c66696c6c2066756e6374696f6e204944207a65726f00000000000000006044820152606401610742565b8b8b8b8b8b8b8b8b8b8b8b604051602001610f619b9a99989796959493929190613452565b6040516020818303038152906040528051906020012090506040518061012001604052808d81526020018c6001600160a01b031681526020018b81526020018a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250604080516020601f8b0181900481028201810190925289815291810191908a908a908190840183828082843760009201829052509385525050506001600160a01b038881166020808501919091528882166040808601919091528883166060808701919091526001600160e01b031989166080909601959095528684526001808352938190208651815586830151948101805473ffffffffffffffffffffffffffffffffffffffff1916959094169490941790925590840151600283015591830151805191926110b192600385019290910190612b58565b50608082015180516110cd916004840191602090910190612b58565b5060a08201516005820180546001600160a01b0392831673ffffffffffffffffffffffffffffffffffffffff199182161790915560c08401516006840180549184169190921617905560e080840151600790930180546101009095015190911c600160a01b027fffffffffffffffff000000000000000000000000000000000000000000000000909416929091169190911791909117905560405181907f544f5b2023290d39a7c3271c6ff6f3f0720e696fa4459d41c949cc02a98314ba906111ab908f908f908f908f908f908f908f908f908f908f908f90613452565b60405180910390a29b9a5050505050505050505050565b6000898152600860209081526040918290205491516001600160601b031960608c811b821693830193909352918a901b90911660348201526001600160e01b031988166048820152604c0160405160208183030381529060405280519060200120146112705760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e7400000000006044820152606401610742565b876001600160a01b03166112ea83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060405161083392506107d391508e908b903390602001928352602083019190915260601b6001600160601b031916604082015260540190565b6001600160a01b0316146113355760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b600860008a81526020019081526020016000206000905588886001600160a01b03167faee73eafb4f543f82048dfc7d8c9a4ee62506ffa76e0051b749d102c4ac7924c87878760405161138a93929190613415565b60405180910390a3505050505050505050565b600160208190526000918252604090912080549181015460028201546003830180546001600160a01b039093169391926113d690613375565b80601f016020809104026020016040519081016040528092919081815260200182805461140290613375565b801561144f5780601f106114245761010080835404028352916020019161144f565b820191906000526020600020905b81548152906001019060200180831161143257829003601f168201915b50505050509080600401805461146490613375565b80601f016020809104026020016040519081016040528092919081815260200182805461149090613375565b80156114dd5780601f106114b2576101008083540402835291602001916114dd565b820191906000526020600020905b8154815290600101906020018083116114c057829003601f168201915b5050506005840154600685015460079095015493946001600160a01b039182169490821693509081169150600160a01b900460e01b89565b60008a81526008602090815260408083205490516001600160601b031960608e811b8216948301949094528c841b811660348301528b841b1660488201526001600160e01b03198a16605c820152820160405160208183030381529060405280519060200120146115c85760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e7400000000006044820152606401610742565b8a6001600160a01b031661163685858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506108338f8b338c8c6040516020016107d39594939291906134d1565b6001600160a01b0316146116815760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b60008c81526008602052604080822091909155516001600160a01b038b169089906116b6908f908b908b908b906024016133d9565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990941693909317909252905161170991906133f9565b6000604051808303816000865af19150503d8060008114611746576040519150601f19603f3d011682016040523d82523d6000602084013e61174b565b606091505b50909250905081156117ac578a6001600160a01b03168c8a6001600160a01b03167fbc2b56fbde291a214ed04ac9fd94c2e53c29b63d1cbdacedfe922d7523cf69eb8a8a8a60405161179f93929190613415565b60405180910390a4611830565b8a6001600160a01b03168c8a6001600160a01b03167f5bfd527f98a7329226e18789dcd6caead784424ffb409bae4584f8e88ee398498a604051611827918152604060208201819052601f908201527f46756c66696c6c6d656e74206661696c656420756e65787065637465646c7900606082015260800190565b60405180910390a45b9a509a98505050505050505050565b60006001600160a01b0388166118975760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f0000000000000000000000006044820152606401610742565b866118e45760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f000000000000000000000000000000006044820152606401610742565b61100085111561192c5760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b6001600160a01b0384166119825760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160a01b0383166119d85760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160e01b03198216611a2f5760405162461bcd60e51b815260206004820152601860248201527f46756c66696c6c2066756e6374696f6e204944207a65726f00000000000000006044820152606401610742565b33600090815260076020526040812080548290611a4b90613521565b9190508190559050463033838c8c8c8c8c8c8c604051602001611a789b9a9998979695949392919061353c565b60408051601f198184030181529082905280516020918201206001600160601b031960608d811b82169385019390935233831b8116603485015288831b1660488401526001600160e01b03198616605c84015293500160408051601f19818403018152828252805160209182012060008681526008909252919020556001600160a01b03808b169184918816907fb1fea87872f337aefbe87f34814e8769f6f84c265bf74098ac9122c4b6268cbc90611b3e90339087908f908f908f908e908e906135be565b60405180910390a450979650505050505050565b6001600160a01b038216611ba85760405162461bcd60e51b815260206004820152601660248201527f5265717565737465722061646472657373207a65726f000000000000000000006044820152606401610742565b3360008181526002602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917ff2b2684e2948004382be1d231e64abcee2a088028b4a47e7a800ba9aa9dc966291015b60405180910390a35050565b60608167ffffffffffffffff811115611c3057611c30613612565b604051908082528060200260200182016040528015611c6357816020015b6060815260200190600190039081611c4e5790505b50905060005b82811015611d0357611cd330858584818110611c8757611c87613628565b9050602002810190611c99919061363e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061269292505050565b828281518110611ce557611ce5613628565b60200260200101819052508080611cfb90613521565b915050611c69565b5092915050565b60008a8152600860209081526040918290205491516001600160601b031960608d811b8216938301939093528b831b811660348301528a831b1660488201526001600160e01b03198916605c820152016040516020818303038152906040528051906020012014611dbd5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726571756573742066756c66696c6c6d656e7400000000006044820152606401610742565b866001600160a01b0316611e3783838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060405161083392506107d391508f908b903390602001928352602083019190915260601b6001600160601b031916604082015260540190565b6001600160a01b031614611e825760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b600860008b815260200190815260200160002060009055886001600160a01b03168a886001600160a01b03167f5bfd527f98a7329226e18789dcd6caead784424ffb409bae4584f8e88ee39849888888604051611ee193929190613415565b60405180910390a450505050505050505050565b6001600160a01b038216611f4b5760405162461bcd60e51b815260206004820152601c60248201527f4169726e6f64652f72656c617965722061646472657373207a65726f000000006044820152606401610742565b80611f985760405162461bcd60e51b815260206004820152601060248201527f50726f746f636f6c204944207a65726f000000000000000000000000000000006044820152606401610742565b3360008181526005602052604081208054919246923092908590611fbb90613521565b91829055506040805160208101959095526001600160601b0319606094851b8116918601919091529190921b166054830152606882015260880160408051601f198184030181529082905280516020918201206001600160601b0319606087811b821693850193909352603484018690523390921b9091166054830152915060680160408051601f1981840301815282825280516020918201206000858152600690925291902055819033906001600160a01b038616907fe97817ee3466ddc9745f4712aa283248845d739fae3db92899a018300c03119d906120a19087815260200190565b60405180910390a4505050565b6040516001600160601b0319606088811b821660208401526034830188905286901b16605482015260680160408051601f19818403018152918152815160209283012060008a815260069093529120541461214b5760405162461bcd60e51b815260206004820152601e60248201527f496e76616c6964207769746864726177616c2066756c66696c6c6d656e7400006044820152606401610742565b4261215884610e10613685565b11801561216f575061216c42610384613685565b83105b6121bb5760405162461bcd60e51b815260206004820152601360248201527f54696d657374616d70206e6f742076616c6964000000000000000000000000006044820152606401610742565b856001600160a01b031661223583838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060405161083392506107d391508c9089903390602001928352602083019190915260601b6001600160601b031916604082015260540190565b6001600160a01b0316146122805760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b6044820152606401610742565b60008781526006602090815260408083208390556001600160a01b03871683526004909152812080543492906122b7908490613685565b9091555050604080518681523360208201523481830152905188916001600160a01b0387811692908a16917f9d4a5358c05c3610156fa7112e5ab33a373ee3d8aab7d6d0477d60eaf8c129f2919081900360600190a450505050505050565b816123635760405162461bcd60e51b815260206004820152601460248201527f537562736372697074696f6e204944207a65726f0000000000000000000000006044820152606401610742565b336000818152600360209081526040808320868452825291829020805460ff191685151590811790915591519182528492917f8307a3c18068fc6f3ad98bcadc19671fb7855dbd3e44d42501c4cced3e732a7f9101611c09565b60006001600160a01b0387166124155760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f0000000000000000000000006044820152606401610742565b856124625760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f000000000000000000000000000000006044820152606401610742565b6110008411156124aa5760405162461bcd60e51b8152602060048201526013602482015272506172616d657465727320746f6f206c6f6e6760681b6044820152606401610742565b6001600160a01b0383166125005760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f0000000000000000000000006044820152606401610742565b6001600160e01b031982166125575760405162461bcd60e51b815260206004820152601860248201527f46756c66696c6c2066756e6374696f6e204944207a65726f00000000000000006044820152606401610742565b3360009081526007602052604081208054829061257390613521565b9190508190559050463033838b8b8b8b8b8b60405160200161259e9a9998979695949392919061369d565b60408051601f198184030181529082905280516020918201206001600160601b031960608c811b8216938501939093523390921b90911660348301526001600160e01b0319851660488301529250604c0160408051601f198184030181528282528051602091820120600086815260089092529190205582906001600160a01b038a16907f6537a04eb75e255385ee16033b5cb61105ea1b3ab614f6455fcad6ee88a601739061265b90339086908d908d908d908d908d906135be565b60405180910390a3509695505050505050565b600080600061267d85856126be565b9150915061268a8161272e565b509392505050565b60606126b7838360405180606001604052806027815260200161373f602791396128ec565b9392505050565b6000808251604114156126f55760208301516040840151606085015160001a6126e9878285856129e0565b94509450505050612727565b82516040141561271f5760208301516040840151612714868383612acd565b935093505050612727565b506000905060025b9250929050565b600081600481111561274257612742613715565b141561274b5750565b600181600481111561275f5761275f613715565b14156127ad5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610742565b60028160048111156127c1576127c1613715565b141561280f5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610742565b600381600481111561282357612823613715565b141561287c5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610742565b600481600481111561289057612890613715565b14156128e95760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610742565b50565b60606001600160a01b0384163b61296b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610742565b600080856001600160a01b03168560405161298691906133f9565b600060405180830381855af49150503d80600081146129c1576040519150601f19603f3d011682016040523d82523d6000602084013e6129c6565b606091505b50915091506129d6828286612b1f565b9695505050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612a175750600090506003612ac4565b8460ff16601b14158015612a2f57508460ff16601c14155b15612a405750600090506004612ac4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612a94573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612abd57600060019250925050612ac4565b9150600090505b94509492505050565b6000807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831681612b0360ff86901c601b613685565b9050612b11878288856129e0565b935093505050935093915050565b60608315612b2e5750816126b7565b825115612b3e5782518084602001fd5b8160405162461bcd60e51b8152600401610742919061372b565b828054612b6490613375565b90600052602060002090601f016020900481019282612b865760008555612bcc565b82601f10612b9f57805160ff1916838001178555612bcc565b82800160010185558215612bcc579182015b82811115612bcc578251825591602001919060010190612bb1565b50612bd8929150612bdc565b5090565b5b80821115612bd85760008155600101612bdd565b600060208284031215612c0357600080fd5b5035919050565b60005b83811015612c25578181015183820152602001612c0d565b83811115612c34576000848401525b50505050565b60008151808452612c52816020860160208601612c0a565b601f01601f19169290920160200192915050565b828152604060208201526000612c7f6040830184612c3a565b949350505050565b80356001600160a01b0381168114612c9e57600080fd5b919050565b80356001600160e01b031981168114612c9e57600080fd5b60008083601f840112612ccd57600080fd5b50813567ffffffffffffffff811115612ce557600080fd5b60208301915083602082850101111561272757600080fd5b600080600080600080600080600060e08a8c031215612d1b57600080fd5b89359850612d2b60208b01612c87565b9750612d3960408b01612c87565b9650612d4760608b01612ca3565b955060808a0135945060a08a013567ffffffffffffffff80821115612d6b57600080fd5b612d778d838e01612cbb565b909650945060c08c0135915080821115612d9057600080fd5b50612d9d8c828d01612cbb565b915080935050809150509295985092959850929598565b8215158152604060208201526000612c7f6040830184612c3a565b60008060408385031215612de257600080fd5b612deb83612c87565b9150612df960208401612c87565b90509250929050565b600060208284031215612e1457600080fd5b6126b782612c87565b600080600060408486031215612e3257600080fd5b83359250602084013567ffffffffffffffff811115612e5057600080fd5b612e5c86828701612cbb565b9497909650939450505050565b60008060008060008060008060008060006101208c8e031215612e8b57600080fd5b8b359a50612e9b60208d01612c87565b995060408c0135985067ffffffffffffffff8060608e01351115612ebe57600080fd5b612ece8e60608f01358f01612cbb565b909950975060808d0135811015612ee457600080fd5b50612ef58d60808e01358e01612cbb565b9096509450612f0660a08d01612c87565b9350612f1460c08d01612c87565b9250612f2260e08d01612c87565b9150612f316101008d01612ca3565b90509295989b509295989b9093969950565b60008060408385031215612f5657600080fd5b612f5f83612c87565b946020939093013593505050565b60006101208b83526001600160a01b03808c1660208501528a6040850152816060850152612f9d8285018b612c3a565b91508382036080850152612fb1828a612c3a565b97811660a085015295861660c084015250509190921660e08201526001600160e01b03199091166101009091015295945050505050565b6000806000806000806000806000806101008b8d03121561300857600080fd5b8a35995061301860208c01612c87565b985061302660408c01612c87565b975061303460608c01612c87565b965061304260808c01612ca3565b955060a08b0135945060c08b013567ffffffffffffffff8082111561306657600080fd5b6130728e838f01612cbb565b909650945060e08d013591508082111561308b57600080fd5b506130988d828e01612cbb565b915080935050809150509295989b9194979a5092959850565b600080600080600080600060c0888a0312156130cc57600080fd5b6130d588612c87565b965060208801359550604088013567ffffffffffffffff8111156130f857600080fd5b6131048a828b01612cbb565b9096509450613117905060608901612c87565b925061312560808901612c87565b915061313360a08901612ca3565b905092959891949750929550565b80358015158114612c9e57600080fd5b6000806040838503121561316457600080fd5b61316d83612c87565b9150612df960208401613141565b6000806020838503121561318e57600080fd5b823567ffffffffffffffff808211156131a657600080fd5b818501915085601f8301126131ba57600080fd5b8135818111156131c957600080fd5b8660208260051b85010111156131de57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561324557603f19888603018452613233858351612c3a565b94509285019290850190600101613217565b5092979650505050505050565b600080600080600080600060c0888a03121561326d57600080fd5b8735965061327d60208901612c87565b95506040880135945061329260608901612c87565b93506080880135925060a088013567ffffffffffffffff8111156132b557600080fd5b6132c18a828b01612cbb565b989b979a50959850939692959293505050565b600080604083850312156132e757600080fd5b82359150612df960208401613141565b60008060008060008060a0878903121561331057600080fd5b61331987612c87565b955060208701359450604087013567ffffffffffffffff81111561333c57600080fd5b61334889828a01612cbb565b909550935061335b905060608801612c87565b915061336960808801612ca3565b90509295509295509295565b600181811c9082168061338957607f821691505b602082108114156133aa57634e487b7160e01b600052602260045260246000fd5b50919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481528360208201526060604082015260006129d66060830184866133b0565b6000825161340b818460208701612c0a565b9190910192915050565b83815260406020820152600061342f6040830184866133b0565b95945050505050565b838152818360208301376000910160200190815292915050565b60006101208d83526001600160a01b03808e1660208501528c60408501528160608501526134838285018c8e6133b0565b91508382036080850152613498828a8c6133b0565b97811660a085015295861660c084015250509190921660e08201526001600160e01b031990911661010090910152979650505050505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b634e487b7160e01b600052601160045260246000fd5b60006000198214156135355761353561350b565b5060010190565b8b815260006bffffffffffffffffffffffff19808d60601b166020840152808c60601b1660348401528a6048840152808a60601b16606884015288607c8401528688609c850137606095861b811696909201609c810196909652509190921b1660b08301526001600160e01b03191660c482015260c801979650505050505050565b60006001600160a01b03808a16835288602084015287604084015260c060608401526135ee60c0840187896133b0565b94166080830152506001600160e01b03199190911660a09091015295945050505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261365557600080fd5b83018035915067ffffffffffffffff82111561367057600080fd5b60200191503681900382131561272757600080fd5b600082198211156136985761369861350b565b500190565b8a815260006bffffffffffffffffffffffff19808c60601b166020840152808b60601b166034840152896048840152808960601b16606884015287607c8401528587609c85013760609490941b9093169301609c8101939093526001600160e01b03191660b08301525060b401979650505050505050565b634e487b7160e01b600052602160045260246000fd5b6020815260006126b76020830184612c3a56fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e6af691e69c5d655ec7258789eca4faeabb529d5be4c12549ff0c0be771d8f3464736f6c63430008090033