false
false
The Sokol Testnet is currently lacking validators. Please consider using Goerli or Mumbai for testing purposes.

Contract Address Details
contract

0x73FF8cdEeA2770BA2B8bA1FD000090A7dd598F0b

Contract Name
AirnodeProtocol
Creator
0x4e59b4–b4956c at 0x9fb608–e5942d
Balance
0 SPOA
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
27655397
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

Sol2uml
new
// 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