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

Contract Address Details
contract

0xC3E76D8829259f2A34541746de2F8A0509Dc1987

Contract Name
DapiServer
Creator
0x07b589–921de1 at 0xae3458–990319
Balance
0 SPOA
Tokens
Fetching tokens...
Transactions
1,072 Transactions
Transfers
0 Transfers
Gas Used
42,376,560
Last Balance Update
27729128
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
DapiServer




Optimization enabled
true
Compiler version
v0.8.9+commit.e5eed63a




Optimization runs
1000
EVM Version
default




Verified at
2022-08-28T16:59:44.548042Z

Constructor Arguments

0x00000000000000000000000092e5125adf385d86bedb950793526106143b6df100000000000000000000000000000000000000000000000000000000000000800000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a4600000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b0000000000000000000000000000000000000000000000000000000000000010446170695365727665722061646d696e00000000000000000000000000000000

Arg [0] (address) : 0x92e5125adf385d86bedb950793526106143b6df1
Arg [1] (string) : DapiServer admin
Arg [2] (address) : 0x7fc103cc17eb1764c14f8a1ac4745445cea62a46
Arg [3] (address) : 0x73ff8cdeea2770ba2b8ba1fd000090a7dd598f0b

              

@api3/airnode-protocol-v1/contracts/dapis/DapiServer.sol

Sol2uml
new
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import "../utils/ExtendedMulticall.sol";
import "../whitelist/WhitelistWithManager.sol";
import "../protocol/AirnodeRequester.sol";
import "./Median.sol";
import "./interfaces/IDapiServer.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/// @title Contract that serves Beacons, Beacon sets and dAPIs based on the
/// Airnode protocol
/// @notice A Beacon is a live data feed addressed by an ID, which is derived
/// from an Airnode address and a template ID. This is suitable where the more
/// recent data point is always more favorable, e.g., in the context of an
/// asset price data feed. Beacons can also be seen as one-Airnode data feeds
/// that can be used individually or combined to build Beacon sets. dAPIs are
/// an abstraction layer over Beacons and Beacon sets.
/// @dev DapiServer is a PSP requester contract. Unlike RRP, which is
/// implemented as a central contract, PSP implementation is built into the
/// requester for optimization. Accordingly, the checks that are not required
/// are omitted. Some examples:
/// - While executing a PSP Beacon update, the condition is not verified
/// because Beacon updates where the condition returns `false` (i.e., the
/// on-chain value is already close to the actual value) are not harmful, and
/// are even desirable.
/// - PSP Beacon set update subscription IDs are not verified, as the
/// Airnode/relayer cannot be made to "misreport a Beacon set update" by
/// spoofing a subscription ID.
/// - While executing a PSP Beacon set update, even the signature is not
/// checked because this is a purely keeper job that does not require off-chain
/// data. Similar to Beacon updates, any Beacon set update is welcome.
contract DapiServer is
    ExtendedMulticall,
    WhitelistWithManager,
    AirnodeRequester,
    Median,
    IDapiServer
{
    using ECDSA for bytes32;

    // Airnodes serve their fulfillment data along with timestamps. This
    // contract casts the reported data to `int224` and the timestamp to
    // `uint32`, which works until year 2106.
    struct DataFeed {
        int224 value;
        uint32 timestamp;
    }

    /// @notice dAPI name setter role description
    string public constant override DAPI_NAME_SETTER_ROLE_DESCRIPTION =
        "dAPI name setter";

    /// @notice Number that represents 100%
    /// @dev 10^8 (and not a larger number) is chosen to avoid overflows in
    /// `calculateUpdateInPercentage()`. Since the reported data needs to fit
    /// into 224 bits, its multiplication by 10^8 is guaranteed not to
    /// overflow.
    uint256 public constant override HUNDRED_PERCENT = 1e8;

    /// @notice dAPI name setter role
    bytes32 public immutable override dapiNameSetterRole;

    /// @notice If an account is an unlimited reader
    mapping(address => bool) public unlimitedReaderStatus;

    /// @notice If a sponsor has permitted an account to request RRP-based
    /// updates at this contract
    mapping(address => mapping(address => bool))
        public
        override sponsorToRrpBeaconUpdateRequesterToPermissionStatus;

    /// @notice ID of the Beacon that the subscription is registered to update
    mapping(bytes32 => bytes32) public override subscriptionIdToBeaconId;

    mapping(bytes32 => DataFeed) private dataFeeds;

    mapping(bytes32 => bytes32) private requestIdToBeaconId;

    mapping(bytes32 => bytes32) private subscriptionIdToHash;

    mapping(bytes32 => bytes32) private dapiNameHashToDataFeedId;

    /// @dev Reverts if the sender is not permitted to request an RRP-based
    /// update with the sponsor and is not the sponsor
    /// @param sponsor Sponsor address
    modifier onlyPermittedUpdateRequester(address sponsor) {
        require(
            sponsor == msg.sender ||
                sponsorToRrpBeaconUpdateRequesterToPermissionStatus[sponsor][
                    msg.sender
                ],
            "Sender not permitted"
        );
        _;
    }

    /// @param _accessControlRegistry AccessControlRegistry contract address
    /// @param _adminRoleDescription Admin role description
    /// @param _manager Manager address
    /// @param _airnodeProtocol AirnodeProtocol contract address
    constructor(
        address _accessControlRegistry,
        string memory _adminRoleDescription,
        address _manager,
        address _airnodeProtocol
    )
        WhitelistWithManager(
            _accessControlRegistry,
            _adminRoleDescription,
            _manager
        )
        AirnodeRequester(_airnodeProtocol)
    {
        dapiNameSetterRole = _deriveRole(
            _deriveAdminRole(manager),
            keccak256(abi.encodePacked(DAPI_NAME_SETTER_ROLE_DESCRIPTION))
        );
    }

    ///                     ~~~RRP Beacon updates~~~

    /// @notice Called by the sponsor to set the update request permission
    /// status of an account
    /// @param rrpBeaconUpdateRequester RRP-based Beacon update requester
    /// address
    /// @param status Permission status
    function setRrpBeaconUpdatePermissionStatus(
        address rrpBeaconUpdateRequester,
        bool status
    ) external override {
        require(
            rrpBeaconUpdateRequester != address(0),
            "Update requester zero"
        );
        sponsorToRrpBeaconUpdateRequesterToPermissionStatus[msg.sender][
            rrpBeaconUpdateRequester
        ] = status;
        emit SetRrpBeaconUpdatePermissionStatus(
            msg.sender,
            rrpBeaconUpdateRequester,
            status
        );
    }

    /// @notice Creates an RRP requests for the Beacon to be updated
    /// @dev In addition to the sponsor sponsoring this contract (by calling
    /// `setRrpSponsorshipStatus()`), the sponsor must also give update request
    /// permission to the sender (by calling
    /// `setRrpBeaconUpdatePermissionStatus()`) before this method is called.
    /// The template must specify a single point of data of type `int256` to be
    /// returned and for it to be small enough to be castable to `int224`
    /// because this is what `fulfillRrpBeaconUpdate()` expects.
    /// @param airnode Airnode address
    /// @param templateId Template ID
    /// @param sponsor Sponsor address
    /// @return requestId Request ID
    function requestRrpBeaconUpdate(
        address airnode,
        bytes32 templateId,
        address sponsor
    )
        external
        override
        onlyPermittedUpdateRequester(sponsor)
        returns (bytes32 requestId)
    {
        bytes32 beaconId = deriveBeaconId(airnode, templateId);
        requestId = IAirnodeProtocol(airnodeProtocol).makeRequest(
            airnode,
            templateId,
            "",
            sponsor,
            this.fulfillRrpBeaconUpdate.selector
        );
        requestIdToBeaconId[requestId] = beaconId;
        emit RequestedRrpBeaconUpdate(
            beaconId,
            sponsor,
            msg.sender,
            requestId,
            airnode,
            templateId
        );
    }

    /// @notice Creates an RRP requests for the Beacon to be updated by the relayer
    /// @param airnode Airnode address
    /// @param templateId Template ID
    /// @param relayer Relayer address
    /// @param sponsor Sponsor address
    /// @return requestId Request ID
    function requestRrpBeaconUpdateRelayed(
        address airnode,
        bytes32 templateId,
        address relayer,
        address sponsor
    )
        external
        override
        onlyPermittedUpdateRequester(sponsor)
        returns (bytes32 requestId)
    {
        bytes32 beaconId = deriveBeaconId(airnode, templateId);
        requestId = IAirnodeProtocol(airnodeProtocol).makeRequestRelayed(
            airnode,
            templateId,
            "",
            relayer,
            sponsor,
            this.fulfillRrpBeaconUpdate.selector
        );
        requestIdToBeaconId[requestId] = beaconId;
        emit RequestedRrpBeaconUpdateRelayed(
            beaconId,
            sponsor,
            msg.sender,
            requestId,
            airnode,
            relayer,
            templateId
        );
    }

    /// @notice Called by the Airnode/relayer using the sponsor wallet through
    /// AirnodeProtocol to fulfill the request
    /// @param requestId Request ID
    /// @param timestamp Timestamp used in the signature
    /// @param data Fulfillment data (an `int256` encoded in contract ABI)
    function fulfillRrpBeaconUpdate(
        bytes32 requestId,
        uint256 timestamp,
        bytes calldata data
    ) external override onlyAirnodeProtocol onlyValidTimestamp(timestamp) {
        bytes32 beaconId = requestIdToBeaconId[requestId];
        delete requestIdToBeaconId[requestId];
        int256 decodedData = processBeaconUpdate(beaconId, timestamp, data);
        emit UpdatedBeaconWithRrp(beaconId, requestId, decodedData, timestamp);
    }

    ///                     ~~~PSP Beacon updates~~~

    /// @notice Registers the Beacon update subscription
    /// @dev Similar to how one needs to call `requestRrpBeaconUpdate()` for
    /// this contract to recognize the incoming RRP fulfillment, this needs to
    /// be called before the subscription fulfillments.
    /// In addition to the subscription being registered, the sponsor must use
    /// `setPspSponsorshipStatus()` to give permission for its sponsor wallet
    /// to be used for the specific subscription.
    /// @param airnode Airnode address
    /// @param templateId Template ID
    /// @param conditions Conditions under which the subscription is requested
    /// to be fulfilled
    /// @param relayer Relayer address
    /// @param sponsor Sponsor address
    /// @return subscriptionId Subscription ID
    function registerBeaconUpdateSubscription(
        address airnode,
        bytes32 templateId,
        bytes memory conditions,
        address relayer,
        address sponsor
    ) external override returns (bytes32 subscriptionId) {
        require(relayer != address(0), "Relayer address zero");
        require(sponsor != address(0), "Sponsor address zero");
        subscriptionId = keccak256(
            abi.encode(
                block.chainid,
                airnode,
                templateId,
                "",
                conditions,
                relayer,
                sponsor,
                address(this),
                this.fulfillPspBeaconUpdate.selector
            )
        );
        subscriptionIdToHash[subscriptionId] = keccak256(
            abi.encodePacked(airnode, relayer, sponsor)
        );
        subscriptionIdToBeaconId[subscriptionId] = deriveBeaconId(
            airnode,
            templateId
        );
        emit RegisteredBeaconUpdateSubscription(
            subscriptionId,
            airnode,
            templateId,
            "",
            conditions,
            relayer,
            sponsor,
            address(this),
            this.fulfillPspBeaconUpdate.selector
        );
    }

    /// @notice Returns if the respective Beacon needs to be updated based on
    /// the fulfillment data and the condition parameters
    /// @dev Reverts if not called by a void signer with zero address because
    /// this method can be used to indirectly read a Beacon.
    /// `conditionParameters` are specified within the `conditions` field of a
    /// Subscription.
    /// @param subscriptionId Subscription ID
    /// @param data Fulfillment data (an `int256` encoded in contract ABI)
    /// @param conditionParameters Subscription condition parameters (a
    /// `uint256` encoded in contract ABI)
    /// @return If the Beacon update subscription should be fulfilled
    function conditionPspBeaconUpdate(
        bytes32 subscriptionId,
        bytes calldata data,
        bytes calldata conditionParameters
    ) external view override returns (bool) {
        require(msg.sender == address(0), "Sender not zero address");
        bytes32 beaconId = subscriptionIdToBeaconId[subscriptionId];
        require(beaconId != bytes32(0), "Subscription not registered");
        DataFeed storage beacon = dataFeeds[beaconId];
        return
            calculateUpdateInPercentage(
                beacon.value,
                decodeFulfillmentData(data)
            ) >=
            decodeConditionParameters(conditionParameters) ||
            beacon.timestamp == 0;
    }

    /// @notice Called by the Airnode/relayer using the sponsor wallet to
    /// fulfill the Beacon update subscription
    /// @dev There is no need to verify that `conditionPspBeaconUpdate()`
    /// returns `true` because any Beacon update is a good Beacon update
    /// @param subscriptionId Subscription ID
    /// @param airnode Airnode address
    /// @param relayer Relayer address
    /// @param sponsor Sponsor address
    /// @param timestamp Timestamp used in the signature
    /// @param data Fulfillment data (a single `int256` encoded in contract
    /// ABI)
    /// @param signature Subscription ID, timestamp, sponsor wallet address
    /// (and fulfillment data if the relayer is not the Airnode) signed by the
    /// Airnode wallet
    function fulfillPspBeaconUpdate(
        bytes32 subscriptionId,
        address airnode,
        address relayer,
        address sponsor,
        uint256 timestamp,
        bytes calldata data,
        bytes calldata signature
    ) external override onlyValidTimestamp(timestamp) {
        require(
            subscriptionIdToHash[subscriptionId] ==
                keccak256(abi.encodePacked(airnode, relayer, sponsor)),
            "Subscription not registered"
        );
        if (airnode == relayer) {
            require(
                (
                    keccak256(
                        abi.encodePacked(subscriptionId, timestamp, msg.sender)
                    ).toEthSignedMessageHash()
                ).recover(signature) == airnode,
                "Signature mismatch"
            );
        } else {
            require(
                (
                    keccak256(
                        abi.encodePacked(
                            subscriptionId,
                            timestamp,
                            msg.sender,
                            data
                        )
                    ).toEthSignedMessageHash()
                ).recover(signature) == airnode,
                "Signature mismatch"
            );
        }
        bytes32 beaconId = subscriptionIdToBeaconId[subscriptionId];
        // Beacon ID is guaranteed to not be zero because the subscription is
        // registered
        int256 decodedData = processBeaconUpdate(beaconId, timestamp, data);
        emit UpdatedBeaconWithPsp(
            beaconId,
            subscriptionId,
            int224(decodedData),
            uint32(timestamp)
        );
    }

    ///                     ~~~Signed data Beacon updates~~~

    /// @notice Updates a Beacon using data signed by the respective Airnode,
    /// without requiring a request or subscription
    /// @param airnode Airnode address
    /// @param templateId Template ID
    /// @param timestamp Timestamp used in the signature
    /// @param data Response data (an `int256` encoded in contract ABI)
    /// @param signature Template ID, a timestamp and the response data signed
    /// by the Airnode address
    function updateBeaconWithSignedData(
        address airnode,
        bytes32 templateId,
        uint256 timestamp,
        bytes calldata data,
        bytes calldata signature
    ) external override onlyValidTimestamp(timestamp) {
        require(
            (
                keccak256(abi.encodePacked(templateId, timestamp, data))
                    .toEthSignedMessageHash()
            ).recover(signature) == airnode,
            "Signature mismatch"
        );
        bytes32 beaconId = deriveBeaconId(airnode, templateId);
        int256 decodedData = processBeaconUpdate(beaconId, timestamp, data);
        emit UpdatedBeaconWithSignedData(beaconId, decodedData, timestamp);
    }

    ///                     ~~~PSP Beacon set updates~~~

    /// @notice Updates the Beacon set using the current values of its Beacons
    /// @dev This function still works if some of the IDs in `beaconIds` belong
    /// to Beacon sets rather than Beacons. However, this is not the intended
    /// use.
    /// @param beaconIds Beacon IDs
    /// @return beaconSetId Beacon set ID
    function updateBeaconSetWithBeacons(bytes32[] memory beaconIds)
        public
        override
        returns (bytes32 beaconSetId)
    {
        uint256 beaconCount = beaconIds.length;
        require(beaconCount > 1, "Specified less than two Beacons");
        int256[] memory values = new int256[](beaconCount);
        uint256 accumulatedTimestamp = 0;
        for (uint256 ind = 0; ind < beaconCount; ind++) {
            DataFeed storage dataFeed = dataFeeds[beaconIds[ind]];
            values[ind] = dataFeed.value;
            accumulatedTimestamp += dataFeed.timestamp;
        }
        uint32 updatedTimestamp = uint32(accumulatedTimestamp / beaconCount);
        beaconSetId = deriveBeaconSetId(beaconIds);
        require(
            updatedTimestamp >= dataFeeds[beaconSetId].timestamp,
            "Updated value outdated"
        );
        int224 updatedValue = int224(median(values));
        dataFeeds[beaconSetId] = DataFeed({
            value: updatedValue,
            timestamp: updatedTimestamp
        });
        emit UpdatedBeaconSetWithBeacons(
            beaconSetId,
            updatedValue,
            updatedTimestamp
        );
    }

    /// @notice Updates the Beacon set using the current values of the Beacons
    /// and returns if this update was justified according to the deviation
    /// threshold
    /// @dev This method does not allow the caller to indirectly read a Beacon
    /// set, which is why it does not require the sender to be a void signer
    /// with zero address. This allows the implementation of incentive
    /// mechanisms that rewards keepers that trigger valid dAPI updates.
    /// @param beaconIds Beacon IDs
    /// @param deviationThresholdInPercentage Deviation threshold in percentage
    /// where 100% is represented as `HUNDRED_PERCENT`
    function updateBeaconSetWithBeaconsAndReturnCondition(
        bytes32[] memory beaconIds,
        uint256 deviationThresholdInPercentage
    ) public override returns (bool) {
        bytes32 beaconSetId = deriveBeaconSetId(beaconIds);
        DataFeed memory initialBeaconSet = dataFeeds[beaconSetId];
        updateBeaconSetWithBeacons(beaconIds);
        DataFeed storage updatedBeaconSet = dataFeeds[beaconSetId];
        return
            calculateUpdateInPercentage(
                initialBeaconSet.value,
                updatedBeaconSet.value
            ) >=
            deviationThresholdInPercentage ||
            (initialBeaconSet.timestamp == 0 && updatedBeaconSet.timestamp > 0);
    }

    /// @notice Returns if the respective Beacon set needs to be updated based
    /// on the condition parameters
    /// @dev The template ID used in the respective Subscription is expected to
    /// be zero, which means the `parameters` field of the Subscription will be
    /// forwarded to this function as `data`. This field should be the Beacon
    /// ID array encoded in contract ABI.
    /// @param subscriptionId Subscription ID
    /// @param data Fulfillment data (array of Beacon IDs, i.e., `bytes32[]`
    /// encoded in contract ABI)
    /// @param conditionParameters Subscription condition parameters (a
    /// `uint256` encoded in contract ABI)
    /// @return If the Beacon set update subscription should be fulfilled
    function conditionPspBeaconSetUpdate(
        bytes32 subscriptionId, // solhint-disable-line no-unused-vars
        bytes calldata data,
        bytes calldata conditionParameters
    ) external override returns (bool) {
        require(msg.sender == address(0), "Sender not zero address");
        bytes32[] memory beaconIds = abi.decode(data, (bytes32[]));
        require(
            keccak256(abi.encode(beaconIds)) == keccak256(data),
            "Data length not correct"
        );
        return
            updateBeaconSetWithBeaconsAndReturnCondition(
                beaconIds,
                decodeConditionParameters(conditionParameters)
            );
    }

    /// @notice Called by the Airnode/relayer using the sponsor wallet to
    /// fulfill the Beacon set update subscription
    /// @dev Similar to `conditionPspBeaconSetUpdate()`, if `templateId` of the
    /// Subscription is zero, its `parameters` field will be forwarded to
    /// `data` here, which is expect to be contract ABI-encoded array of Beacon
    /// IDs.
    /// It does not make sense for this subscription to be relayed, as there is
    /// no external data being delivered. Nevertheless, this is allowed for the
    /// lack of a reason to prevent it.
    /// Even though the consistency of the arguments are not being checked, if
    /// a standard implementation of Airnode is being used, these can be
    /// expected to be correct. Either way, the assumption is that it does not
    /// matter for the purposes of a Beacon set update subscription.
    /// @param subscriptionId Subscription ID
    /// @param airnode Airnode address
    /// @param relayer Relayer address
    /// @param sponsor Sponsor address
    /// @param timestamp Timestamp used in the signature
    /// @param data Fulfillment data (an `int256` encoded in contract ABI)
    /// @param signature Subscription ID, timestamp, sponsor wallet address
    /// (and fulfillment data if the relayer is not the Airnode) signed by the
    /// Airnode wallet
    function fulfillPspBeaconSetUpdate(
        bytes32 subscriptionId, // solhint-disable-line no-unused-vars
        address airnode, // solhint-disable-line no-unused-vars
        address relayer, // solhint-disable-line no-unused-vars
        address sponsor, // solhint-disable-line no-unused-vars
        uint256 timestamp, // solhint-disable-line no-unused-vars
        bytes calldata data,
        bytes calldata signature // solhint-disable-line no-unused-vars
    ) external override {
        require(
            keccak256(data) ==
                updateBeaconSetWithBeacons(abi.decode(data, (bytes32[]))),
            "Data length not correct"
        );
    }

    ///                     ~~~Signed data Beacon set updates~~~

    /// @notice Updates a Beacon set using data signed by the respective
    /// Airnodes without requiring a request or subscription. The Beacons for
    /// which the signature is omitted will be read from the storage.
    /// @param airnodes Airnode addresses
    /// @param templateIds Template IDs
    /// @param timestamps Timestamps used in the signatures
    /// @param data Response data (an `int256` encoded in contract ABI per
    /// Beacon)
    /// @param signatures Template ID, a timestamp and the response data signed
    /// by the respective Airnode address per Beacon
    /// @return beaconSetId Beacon set ID
    function updateBeaconSetWithSignedData(
        address[] memory airnodes,
        bytes32[] memory templateIds,
        uint256[] memory timestamps,
        bytes[] memory data,
        bytes[] memory signatures
    ) external override returns (bytes32 beaconSetId) {
        uint256 beaconCount = airnodes.length;
        require(
            beaconCount == templateIds.length &&
                beaconCount == timestamps.length &&
                beaconCount == data.length &&
                beaconCount == signatures.length,
            "Parameter length mismatch"
        );
        require(beaconCount > 1, "Specified less than two Beacons");
        bytes32[] memory beaconIds = new bytes32[](beaconCount);
        int256[] memory values = new int256[](beaconCount);
        uint256 accumulatedTimestamp = 0;
        for (uint256 ind = 0; ind < beaconCount; ind++) {
            if (signatures[ind].length != 0) {
                address airnode = airnodes[ind];
                uint256 timestamp = timestamps[ind];
                require(timestampIsValid(timestamp), "Timestamp not valid");
                require(
                    (
                        keccak256(
                            abi.encodePacked(
                                templateIds[ind],
                                timestamp,
                                data[ind]
                            )
                        ).toEthSignedMessageHash()
                    ).recover(signatures[ind]) == airnode,
                    "Signature mismatch"
                );
                values[ind] = decodeFulfillmentData(data[ind]);
                // Timestamp validity is already checked, which means it will
                // be small enough to be typecast into `uint32`
                accumulatedTimestamp += timestamp;
                beaconIds[ind] = deriveBeaconId(airnode, templateIds[ind]);
            } else {
                bytes32 beaconId = deriveBeaconId(
                    airnodes[ind],
                    templateIds[ind]
                );
                DataFeed storage dataFeed = dataFeeds[beaconId];
                values[ind] = dataFeed.value;
                accumulatedTimestamp += dataFeed.timestamp;
                beaconIds[ind] = beaconId;
            }
        }
        beaconSetId = deriveBeaconSetId(beaconIds);
        uint32 updatedTimestamp = uint32(accumulatedTimestamp / beaconCount);
        require(
            updatedTimestamp >= dataFeeds[beaconSetId].timestamp,
            "Updated value outdated"
        );
        int224 updatedValue = int224(median(values));
        dataFeeds[beaconSetId] = DataFeed({
            value: updatedValue,
            timestamp: updatedTimestamp
        });
        emit UpdatedBeaconSetWithSignedData(
            beaconSetId,
            updatedValue,
            updatedTimestamp
        );
    }

    /// @notice Called by the manager to add the unlimited reader indefinitely
    /// @dev Since the unlimited reader status cannot be revoked, only
    /// contracts that are adequately restricted should be given this status
    /// @param unlimitedReader Unlimited reader address
    function addUnlimitedReader(address unlimitedReader) external override {
        require(msg.sender == manager, "Sender not manager");
        unlimitedReaderStatus[unlimitedReader] = true;
        emit AddedUnlimitedReader(unlimitedReader);
    }

    /// @notice Sets the data feed ID the dAPI name points to
    /// @dev While a data feed ID refers to a specific Beacon or Beacon set,
    /// dAPI names provide a more abstract interface for convenience. This
    /// means a dAPI name that was pointing to a Beacon can be pointed to a
    /// Beacon set, then another Beacon set, etc.
    /// @param dapiName Human-readable dAPI name
    /// @param dataFeedId Data feed ID the dAPI name will point to
    function setDapiName(bytes32 dapiName, bytes32 dataFeedId)
        external
        override
    {
        require(dapiName != bytes32(0), "dAPI name zero");
        require(
            msg.sender == manager ||
                IAccessControlRegistry(accessControlRegistry).hasRole(
                    dapiNameSetterRole,
                    msg.sender
                ),
            "Sender cannot set dAPI name"
        );
        dapiNameHashToDataFeedId[
            keccak256(abi.encodePacked(dapiName))
        ] = dataFeedId;
        emit SetDapiName(dapiName, dataFeedId, msg.sender);
    }

    /// @notice Returns the data feed ID the dAPI name is set to
    /// @param dapiName dAPI name
    /// @return Data feed ID
    function dapiNameToDataFeedId(bytes32 dapiName)
        external
        view
        override
        returns (bytes32)
    {
        return dapiNameHashToDataFeedId[keccak256(abi.encodePacked(dapiName))];
    }

    /// @notice Reads the data feed with ID
    /// @param dataFeedId Data feed ID
    /// @return value Data feed value
    /// @return timestamp Data feed timestamp
    function readDataFeedWithId(bytes32 dataFeedId)
        external
        view
        override
        returns (int224 value, uint32 timestamp)
    {
        require(
            readerCanReadDataFeed(dataFeedId, msg.sender),
            "Sender cannot read"
        );
        DataFeed storage dataFeed = dataFeeds[dataFeedId];
        return (dataFeed.value, dataFeed.timestamp);
    }

    /// @notice Reads the data feed value with ID
    /// @param dataFeedId Data feed ID
    /// @return value Data feed value
    function readDataFeedValueWithId(bytes32 dataFeedId)
        external
        view
        override
        returns (int224 value)
    {
        require(
            readerCanReadDataFeed(dataFeedId, msg.sender),
            "Sender cannot read"
        );
        DataFeed storage dataFeed = dataFeeds[dataFeedId];
        require(dataFeed.timestamp != 0, "Data feed does not exist");
        return dataFeed.value;
    }

    /// @notice Reads the data feed with dAPI name
    /// @dev The read data feed may belong to a Beacon or dAPI. The reader
    /// must be whitelisted for the hash of the dAPI name.
    /// @param dapiName dAPI name
    /// @return value Data feed value
    /// @return timestamp Data feed timestamp
    function readDataFeedWithDapiName(bytes32 dapiName)
        external
        view
        override
        returns (int224 value, uint32 timestamp)
    {
        bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
        require(
            readerCanReadDataFeed(dapiNameHash, msg.sender),
            "Sender cannot read"
        );
        bytes32 dataFeedId = dapiNameHashToDataFeedId[dapiNameHash];
        require(dataFeedId != bytes32(0), "dAPI name not set");
        DataFeed storage dataFeed = dataFeeds[dataFeedId];
        return (dataFeed.value, dataFeed.timestamp);
    }

    /// @notice Reads the data feed value with dAPI name
    /// @param dapiName dAPI name
    /// @return value Data feed value
    function readDataFeedValueWithDapiName(bytes32 dapiName)
        external
        view
        override
        returns (int224 value)
    {
        bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
        require(
            readerCanReadDataFeed(dapiNameHash, msg.sender),
            "Sender cannot read"
        );
        DataFeed storage dataFeed = dataFeeds[
            dapiNameHashToDataFeedId[dapiNameHash]
        ];
        require(dataFeed.timestamp != 0, "Data feed does not exist");
        return dataFeed.value;
    }

    /// @notice Returns if a reader can read the data feed
    /// @param dataFeedId Data feed ID (or dAPI name hash)
    /// @param reader Reader address
    /// @return If the reader can read the data feed
    function readerCanReadDataFeed(bytes32 dataFeedId, address reader)
        public
        view
        override
        returns (bool)
    {
        return
            reader == address(0) ||
            userIsWhitelisted(dataFeedId, reader) ||
            unlimitedReaderStatus[reader];
    }

    /// @notice Returns the detailed whitelist status of the reader for the
    /// data feed
    /// @param dataFeedId Data feed ID (or dAPI name hash)
    /// @param reader Reader address
    /// @return expirationTimestamp Timestamp at which the whitelisting of the
    /// reader will expire
    /// @return indefiniteWhitelistCount Number of times `reader` was
    /// whitelisted indefinitely for `dataFeedId`
    function dataFeedIdToReaderToWhitelistStatus(
        bytes32 dataFeedId,
        address reader
    )
        external
        view
        override
        returns (uint64 expirationTimestamp, uint192 indefiniteWhitelistCount)
    {
        WhitelistStatus
            storage whitelistStatus = serviceIdToUserToWhitelistStatus[
                dataFeedId
            ][reader];
        expirationTimestamp = whitelistStatus.expirationTimestamp;
        indefiniteWhitelistCount = whitelistStatus.indefiniteWhitelistCount;
    }

    /// @notice Returns if an account has indefinitely whitelisted the reader
    /// for the data feed
    /// @param dataFeedId Data feed ID (or dAPI name hash)
    /// @param reader Reader address
    /// @param setter Address of the account that has potentially whitelisted
    /// the reader for the data feed indefinitely
    /// @return indefiniteWhitelistStatus If `setter` has indefinitely
    /// whitelisted reader for the data feed
    function dataFeedIdToReaderToSetterToIndefiniteWhitelistStatus(
        bytes32 dataFeedId,
        address reader,
        address setter
    ) external view override returns (bool indefiniteWhitelistStatus) {
        indefiniteWhitelistStatus = serviceIdToUserToSetterToIndefiniteWhitelistStatus[
            dataFeedId
        ][reader][setter];
    }

    /// @notice Derives the Beacon ID from the Airnode address and template ID
    /// @param airnode Airnode address
    /// @param templateId Template ID
    /// @return beaconId Beacon ID
    function deriveBeaconId(address airnode, bytes32 templateId)
        public
        pure
        override
        returns (bytes32 beaconId)
    {
        require(airnode != address(0), "Airnode address zero");
        require(templateId != bytes32(0), "Template ID zero");
        beaconId = keccak256(abi.encodePacked(airnode, templateId));
    }

    /// @notice Derives the Beacon set ID from the Beacon IDs
    /// @dev Notice that `abi.encode()` is used over `abi.encodePacked()`
    /// @param beaconIds Beacon IDs
    /// @return beaconSetId Beacon set ID
    function deriveBeaconSetId(bytes32[] memory beaconIds)
        public
        pure
        override
        returns (bytes32 beaconSetId)
    {
        beaconSetId = keccak256(abi.encode(beaconIds));
    }

    /// @notice Called privately to process the Beacon update
    /// @param beaconId Beacon ID
    /// @param timestamp Timestamp used in the signature
    /// @param data Fulfillment data (an `int256` encoded in contract ABI)
    /// @return updatedBeaconValue Updated Beacon value
    function processBeaconUpdate(
        bytes32 beaconId,
        uint256 timestamp,
        bytes calldata data
    ) private returns (int256 updatedBeaconValue) {
        updatedBeaconValue = decodeFulfillmentData(data);
        require(
            timestamp > dataFeeds[beaconId].timestamp,
            "Fulfillment older than Beacon"
        );
        // Timestamp validity is already checked by `onlyValidTimestamp`, which
        // means it will be small enough to be typecast into `uint32`
        dataFeeds[beaconId] = DataFeed({
            value: int224(updatedBeaconValue),
            timestamp: uint32(timestamp)
        });
    }

    /// @notice Called privately to decode the fulfillment data
    /// @param data Fulfillment data (an `int256` encoded in contract ABI)
    /// @return decodedData Decoded fulfillment data
    function decodeFulfillmentData(bytes memory data)
        private
        pure
        returns (int224)
    {
        require(data.length == 32, "Data length not correct");
        int256 decodedData = abi.decode(data, (int256));
        require(
            decodedData >= type(int224).min && decodedData <= type(int224).max,
            "Value typecasting error"
        );
        return int224(decodedData);
    }

    /// @notice Called privately to decode the condition parameters
    /// @param conditionParameters Condition parameters (a `uint256` encoded in
    /// contract ABI)
    /// @return deviationThresholdInPercentage Deviation threshold in
    /// percentage where 100% is represented as `HUNDRED_PERCENT`
    function decodeConditionParameters(bytes calldata conditionParameters)
        private
        pure
        returns (uint256 deviationThresholdInPercentage)
    {
        require(conditionParameters.length == 32, "Incorrect parameter length");
        deviationThresholdInPercentage = abi.decode(
            conditionParameters,
            (uint256)
        );
    }

    /// @notice Called privately to calculate the update magnitude in
    /// percentages where 100% is represented as `HUNDRED_PERCENT`
    /// @dev The percentage changes will be more pronounced when the first
    /// value is almost zero, which may trigger updates more frequently than
    /// wanted. To avoid this, Beacons should be defined in a way that the
    /// expected values are not small numbers floating around zero, i.e.,
    /// offset and scale.
    /// @param initialValue Initial value
    /// @param updatedValue Updated value
    /// @return updateInPercentage Update in percentage
    function calculateUpdateInPercentage(
        int224 initialValue,
        int224 updatedValue
    ) private pure returns (uint256 updateInPercentage) {
        int256 delta = int256(updatedValue) - int256(initialValue);
        uint256 absoluteDelta = delta > 0 ? uint256(delta) : uint256(-delta);
        uint256 absoluteInitialValue = initialValue > 0
            ? uint256(int256(initialValue))
            : uint256(-int256(initialValue));
        // Avoid division by 0
        if (absoluteInitialValue == 0) {
            absoluteInitialValue = 1;
        }
        updateInPercentage =
            (absoluteDelta * HUNDRED_PERCENT) /
            absoluteInitialValue;
    }
}
        

@api3/airnode-protocol-v1/contracts/protocol/interfaces/IAirnodeRequester.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IAirnodeRequester {
    function airnodeProtocol() external view returns (address);
}
          

@openzeppelin/contracts/utils/Multicall.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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 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;
    }
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/AccessControlRegistryAdminned.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Multicall.sol";
import "./RoleDeriver.sol";
import "./AccessControlRegistryUser.sol";
import "./interfaces/IAccessControlRegistryAdminned.sol";

/// @title Contract to be inherited by contracts whose adminship functionality
/// will be implemented using AccessControlRegistry
contract AccessControlRegistryAdminned is
    Multicall,
    RoleDeriver,
    AccessControlRegistryUser,
    IAccessControlRegistryAdminned
{
    /// @notice Admin role description
    string public override adminRoleDescription;

    bytes32 internal immutable adminRoleDescriptionHash;

    /// @dev Contracts deployed with the same admin role descriptions will have
    /// the same roles, meaning that granting an account a role will authorize
    /// it in multiple contracts. Unless you want your deployed contract to
    /// share the role configuration of another contract, use a unique admin
    /// role description.
    /// @param _accessControlRegistry AccessControlRegistry contract address
    /// @param _adminRoleDescription Admin role description
    constructor(
        address _accessControlRegistry,
        string memory _adminRoleDescription
    ) AccessControlRegistryUser(_accessControlRegistry) {
        require(
            bytes(_adminRoleDescription).length > 0,
            "Admin role description empty"
        );
        adminRoleDescription = _adminRoleDescription;
        adminRoleDescriptionHash = keccak256(
            abi.encodePacked(_adminRoleDescription)
        );
    }

    /// @notice Derives the admin role for the specific manager address
    /// @param manager Manager address
    /// @return adminRole Admin role
    function _deriveAdminRole(address manager)
        internal
        view
        returns (bytes32 adminRole)
    {
        adminRole = _deriveRole(
            _deriveRootRole(manager),
            adminRoleDescriptionHash
        );
    }
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/AccessControlRegistryAdminnedWithManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./AccessControlRegistryAdminned.sol";
import "./interfaces/IAccessControlRegistryAdminnedWithManager.sol";

/// @title Contract to be inherited by contracts with manager whose adminship
/// functionality will be implemented using AccessControlRegistry
/// @notice The manager address here is expected to belong to an
/// AccessControlRegistry user that is a multisig/DAO
contract AccessControlRegistryAdminnedWithManager is
    AccessControlRegistryAdminned,
    IAccessControlRegistryAdminnedWithManager
{
    /// @notice Address of the manager that manages the related
    /// AccessControlRegistry roles
    /// @dev The mutability of the manager role can be implemented by
    /// designating an OwnableCallForwarder contract as the manager. The
    /// ownership of this contract can then be transferred, effectively
    /// transferring managership.
    address public immutable override manager;

    /// @notice Admin role
    /// @dev Since `manager` is immutable, so is `adminRole`
    bytes32 public immutable override adminRole;

    /// @param _accessControlRegistry AccessControlRegistry contract address
    /// @param _adminRoleDescription Admin role description
    /// @param _manager Manager address
    constructor(
        address _accessControlRegistry,
        string memory _adminRoleDescription,
        address _manager
    )
        AccessControlRegistryAdminned(
            _accessControlRegistry,
            _adminRoleDescription
        )
    {
        require(_manager != address(0), "Manager address zero");
        manager = _manager;
        adminRole = _deriveAdminRole(_manager);
    }
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/AccessControlRegistryUser.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/IAccessControlRegistry.sol";
import "./interfaces/IAccessControlRegistryUser.sol";

/// @title Contract to be inherited by contracts that will interact with
/// AccessControlRegistry
contract AccessControlRegistryUser is IAccessControlRegistryUser {
    /// @notice AccessControlRegistry contract address
    address public immutable override accessControlRegistry;

    /// @param _accessControlRegistry AccessControlRegistry contract address
    constructor(address _accessControlRegistry) {
        require(_accessControlRegistry != address(0), "ACR address zero");
        accessControlRegistry = _accessControlRegistry;
    }
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/RoleDeriver.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contract to be inherited by contracts that will derive
/// AccessControlRegistry roles
/// @notice If a contract interfaces with AccessControlRegistry and needs to
/// derive roles, it should inherit this contract instead of re-implementing
/// the logic
contract RoleDeriver {
    /// @notice Derives the root role of the manager
    /// @param manager Manager address
    /// @return rootRole Root role
    function _deriveRootRole(address manager)
        internal
        pure
        returns (bytes32 rootRole)
    {
        rootRole = keccak256(abi.encodePacked(manager));
    }

    /// @notice Derives the role using its admin role and description
    /// @dev This implies that roles adminned by the same role cannot have the
    /// same description
    /// @param adminRole Admin role
    /// @param description Human-readable description of the role
    /// @return role Role
    function _deriveRole(bytes32 adminRole, string memory description)
        internal
        pure
        returns (bytes32 role)
    {
        role = _deriveRole(adminRole, keccak256(abi.encodePacked(description)));
    }

    /// @notice Derives the role using its admin role and description hash
    /// @dev This implies that roles adminned by the same role cannot have the
    /// same description
    /// @param adminRole Admin role
    /// @param descriptionHash Hash of the human-readable description of the
    /// role
    /// @return role Role
    function _deriveRole(bytes32 adminRole, bytes32 descriptionHash)
        internal
        pure
        returns (bytes32 role)
    {
        role = keccak256(abi.encodePacked(adminRole, descriptionHash));
    }
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistry.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/IAccessControl.sol";

interface IAccessControlRegistry is IAccessControl {
    event InitializedManager(bytes32 indexed rootRole, address indexed manager);

    event InitializedRole(
        bytes32 indexed role,
        bytes32 indexed adminRole,
        string description,
        address sender
    );

    function initializeManager(address manager) external;

    function initializeRoleAndGrantToSender(
        bytes32 adminRole,
        string calldata description
    ) external returns (bytes32 role);

    function deriveRootRole(address manager)
        external
        pure
        returns (bytes32 rootRole);

    function deriveRole(bytes32 adminRole, string calldata description)
        external
        pure
        returns (bytes32 role);
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistryAdminned.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IAccessControlRegistryUser.sol";

interface IAccessControlRegistryAdminned is IAccessControlRegistryUser {
    function adminRoleDescription() external view returns (string memory);
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistryAdminnedWithManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IAccessControlRegistryAdminned.sol";

interface IAccessControlRegistryAdminnedWithManager is
    IAccessControlRegistryAdminned
{
    function manager() external view returns (address);

    function adminRole() external view returns (bytes32);
}
          

@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistryUser.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IAccessControlRegistryUser {
    function accessControlRegistry() external view returns (address);
}
          

@api3/airnode-protocol-v1/contracts/dapis/Median.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./Sort.sol";
import "./QuickSelect.sol";

/// @title Contract to be inherited by contracts that will calculate the median
/// of an array
/// @notice The operation will be in-place, i.e., the array provided as the
/// argument will be modified.
contract Median is Sort, Quickselect {
    /// @notice Returns the median of the array
    /// @dev Uses an unrolled sorting implementation for shorter arrays and
    /// quickselect for longer arrays for gas cost efficiency
    /// @param array Array whose median is to be calculated
    /// @return Median of the array
    function median(int256[] memory array) internal pure returns (int256) {
        uint256 arrayLength = array.length;
        if (arrayLength <= MAX_SORT_LENGTH) {
            sort(array);
            if (arrayLength % 2 == 1) {
                return array[arrayLength / 2];
            } else {
                return
                    (array[arrayLength / 2 - 1] + array[arrayLength / 2]) / 2;
            }
        } else {
            if (arrayLength % 2 == 1) {
                return array[quickselectK(array, arrayLength / 2)];
            } else {
                (uint256 mid1, uint256 mid2) = quickselectKPlusOne(
                    array,
                    arrayLength / 2 - 1
                );
                return (array[mid1] + array[mid2]) / 2;
            }
        }
    }
}
          

@api3/airnode-protocol-v1/contracts/dapis/QuickSelect.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contract to be inherited by contracts that will calculate the index
/// of the k-th and optionally (k+1)-th largest elements in the array
/// @notice Uses quickselect, which operates in-place, i.e., the array provided
/// as the argument will be modified.
contract Quickselect {
    /// @notice Returns the index of the k-th largest element in the array
    /// @param array Array in which k-th largest element will be searched
    /// @param k K
    /// @return indK Index of the k-th largest element
    function quickselectK(int256[] memory array, uint256 k)
        internal
        pure
        returns (uint256 indK)
    {
        (indK, ) = quickselect(array, 0, array.length - 1, k, false);
    }

    /// @notice Returns the index of the k-th and (k+1)-th largest elements in
    /// the array
    /// @param array Array in which k-th and (k+1)-th largest elements will be
    /// searched
    /// @param k K
    /// @return indK Index of the k-th largest element
    /// @return indKPlusOne Index of the (k+1)-th largest element
    function quickselectKPlusOne(int256[] memory array, uint256 k)
        internal
        pure
        returns (uint256 indK, uint256 indKPlusOne)
    {
        uint256 arrayLength = array.length;
        require(arrayLength > 1, "Array too short to select k+1");
        return quickselect(array, 0, arrayLength - 1, k, true);
    }

    /// @notice Returns the index of the k-th largest element in the specified
    /// section of the (potentially unsorted) array
    /// @param array Array in which K will be searched for
    /// @param lo Starting index of the section of the array that K will be
    /// searched in
    /// @param hi Last index of the section of the array that K will be
    /// searched in
    /// @param k K
    /// @param selectKPlusOne If the index of the (k+1)-th largest element is
    /// to be returned
    /// @return indK Index of the k-th largest element
    /// @return indKPlusOne Index of the (k+1)-th largest element (only set if
    /// `selectKPlusOne` is `true`)
    function quickselect(
        int256[] memory array,
        uint256 lo,
        uint256 hi,
        uint256 k,
        bool selectKPlusOne
    ) private pure returns (uint256 indK, uint256 indKPlusOne) {
        if (lo == hi) {
            return (k, 0);
        }
        uint256 indPivot = partition(array, lo, hi);
        if (k < indPivot) {
            (indK, ) = quickselect(array, lo, indPivot - 1, k, false);
        } else if (k > indPivot) {
            (indK, ) = quickselect(array, indPivot + 1, hi, k, false);
        } else {
            indK = indPivot;
        }
        // Since Quickselect ends in the array being partitioned around the
        // k-th largest element, we can continue searching towards right for
        // the (k+1)-th largest element, which is useful in calculating the
        // median of an array with even length
        if (selectKPlusOne) {
            indKPlusOne = indK + 1;
            for (uint256 i = indKPlusOne + 1; i < array.length; i++) {
                if (array[i] < array[indKPlusOne]) {
                    indKPlusOne = i;
                }
            }
        }
    }

    /// @notice Partitions the array into two around a pivot
    /// @param array Array that will be partitioned
    /// @param lo Starting index of the section of the array that will be
    /// partitioned
    /// @param hi Last index of the section of the array that will be
    /// partitioned
    /// @return pivotInd Pivot index
    function partition(
        int256[] memory array,
        uint256 lo,
        uint256 hi
    ) private pure returns (uint256 pivotInd) {
        if (lo == hi) {
            return lo;
        }
        int256 pivot = array[lo];
        uint256 i = lo;
        pivotInd = hi + 1;
        while (true) {
            do {
                i++;
            } while (i < array.length && array[i] < pivot);
            do {
                pivotInd--;
            } while (array[pivotInd] > pivot);
            if (i >= pivotInd) {
                (array[lo], array[pivotInd]) = (array[pivotInd], array[lo]);
                return pivotInd;
            }
            (array[i], array[pivotInd]) = (array[pivotInd], array[i]);
        }
    }
}
          

@api3/airnode-protocol-v1/contracts/dapis/Sort.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contract to be inherited by contracts that will sort an array using
/// an unrolled implementation
/// @notice The operation will be in-place, i.e., the array provided as the
/// argument will be modified.
contract Sort {
    uint256 internal constant MAX_SORT_LENGTH = 9;

    /// @notice Sorts the array
    /// @param array Array to be sorted
    function sort(int256[] memory array) internal pure {
        uint256 arrayLength = array.length;
        require(arrayLength <= MAX_SORT_LENGTH, "Array too long to sort");
        // Do a binary search
        if (arrayLength < 6) {
            // Possible lengths: 1, 2, 3, 4, 5
            if (arrayLength < 4) {
                // Possible lengths: 1, 2, 3
                if (arrayLength == 3) {
                    // Length: 3
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 0, 1);
                } else if (arrayLength == 2) {
                    // Length: 2
                    swapIfFirstIsLarger(array, 0, 1);
                }
                // Do nothing for Length: 1
            } else {
                // Possible lengths: 4, 5
                if (arrayLength == 5) {
                    // Length: 5
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 3, 4);
                    swapIfFirstIsLarger(array, 1, 3);
                    swapIfFirstIsLarger(array, 0, 2);
                    swapIfFirstIsLarger(array, 2, 4);
                    swapIfFirstIsLarger(array, 0, 3);
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 1, 2);
                } else {
                    // Length: 4
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 1, 3);
                    swapIfFirstIsLarger(array, 0, 2);
                    swapIfFirstIsLarger(array, 1, 2);
                }
            }
        } else {
            // Possible lengths: 6, 7, 8, 9
            if (arrayLength < 8) {
                // Possible lengths: 6, 7
                if (arrayLength == 7) {
                    // Length: 7
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 3, 4);
                    swapIfFirstIsLarger(array, 5, 6);
                    swapIfFirstIsLarger(array, 0, 2);
                    swapIfFirstIsLarger(array, 4, 6);
                    swapIfFirstIsLarger(array, 3, 5);
                    swapIfFirstIsLarger(array, 2, 6);
                    swapIfFirstIsLarger(array, 1, 5);
                    swapIfFirstIsLarger(array, 0, 4);
                    swapIfFirstIsLarger(array, 2, 5);
                    swapIfFirstIsLarger(array, 0, 3);
                    swapIfFirstIsLarger(array, 2, 4);
                    swapIfFirstIsLarger(array, 1, 3);
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 4, 5);
                } else {
                    // Length: 6
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 4, 5);
                    swapIfFirstIsLarger(array, 1, 3);
                    swapIfFirstIsLarger(array, 3, 5);
                    swapIfFirstIsLarger(array, 1, 3);
                    swapIfFirstIsLarger(array, 2, 4);
                    swapIfFirstIsLarger(array, 0, 2);
                    swapIfFirstIsLarger(array, 2, 4);
                    swapIfFirstIsLarger(array, 3, 4);
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 2, 3);
                }
            } else {
                // Possible lengths: 8, 9
                if (arrayLength == 9) {
                    // Length: 9
                    swapIfFirstIsLarger(array, 1, 8);
                    swapIfFirstIsLarger(array, 2, 7);
                    swapIfFirstIsLarger(array, 3, 6);
                    swapIfFirstIsLarger(array, 4, 5);
                    swapIfFirstIsLarger(array, 1, 4);
                    swapIfFirstIsLarger(array, 5, 8);
                    swapIfFirstIsLarger(array, 0, 2);
                    swapIfFirstIsLarger(array, 6, 7);
                    swapIfFirstIsLarger(array, 2, 6);
                    swapIfFirstIsLarger(array, 7, 8);
                    swapIfFirstIsLarger(array, 0, 3);
                    swapIfFirstIsLarger(array, 4, 5);
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 3, 5);
                    swapIfFirstIsLarger(array, 6, 7);
                    swapIfFirstIsLarger(array, 2, 4);
                    swapIfFirstIsLarger(array, 1, 3);
                    swapIfFirstIsLarger(array, 5, 7);
                    swapIfFirstIsLarger(array, 4, 6);
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 3, 4);
                    swapIfFirstIsLarger(array, 5, 6);
                    swapIfFirstIsLarger(array, 7, 8);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 4, 5);
                } else {
                    // Length: 8
                    swapIfFirstIsLarger(array, 0, 7);
                    swapIfFirstIsLarger(array, 1, 6);
                    swapIfFirstIsLarger(array, 2, 5);
                    swapIfFirstIsLarger(array, 3, 4);
                    swapIfFirstIsLarger(array, 0, 3);
                    swapIfFirstIsLarger(array, 4, 7);
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 5, 6);
                    swapIfFirstIsLarger(array, 0, 1);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 4, 5);
                    swapIfFirstIsLarger(array, 6, 7);
                    swapIfFirstIsLarger(array, 3, 5);
                    swapIfFirstIsLarger(array, 2, 4);
                    swapIfFirstIsLarger(array, 1, 2);
                    swapIfFirstIsLarger(array, 3, 4);
                    swapIfFirstIsLarger(array, 5, 6);
                    swapIfFirstIsLarger(array, 2, 3);
                    swapIfFirstIsLarger(array, 4, 5);
                    swapIfFirstIsLarger(array, 3, 4);
                }
            }
        }
    }

    /// @notice Swaps two elements of an array if the first element is greater
    /// than the second
    /// @param array Array whose elements are to be swapped
    /// @param ind1 Index of the first element
    /// @param ind2 Index of the second element
    function swapIfFirstIsLarger(
        int256[] memory array,
        uint256 ind1,
        uint256 ind2
    ) private pure {
        if (array[ind1] > array[ind2]) {
            (array[ind1], array[ind2]) = (array[ind2], array[ind1]);
        }
    }
}
          

@api3/airnode-protocol-v1/contracts/dapis/interfaces/IDapiServer.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../../protocol/interfaces/IAirnodeRequester.sol";

interface IDapiServer is IAirnodeRequester {
    event SetRrpBeaconUpdatePermissionStatus(
        address indexed sponsor,
        address indexed rrpBeaconUpdateRequester,
        bool status
    );

    event RequestedRrpBeaconUpdate(
        bytes32 indexed beaconId,
        address indexed sponsor,
        address indexed requester,
        bytes32 requestId,
        address airnode,
        bytes32 templateId
    );

    event RequestedRrpBeaconUpdateRelayed(
        bytes32 indexed beaconId,
        address indexed sponsor,
        address indexed requester,
        bytes32 requestId,
        address airnode,
        address relayer,
        bytes32 templateId
    );

    event UpdatedBeaconWithRrp(
        bytes32 indexed beaconId,
        bytes32 requestId,
        int256 value,
        uint256 timestamp
    );

    event RegisteredBeaconUpdateSubscription(
        bytes32 indexed subscriptionId,
        address airnode,
        bytes32 templateId,
        bytes parameters,
        bytes conditions,
        address relayer,
        address sponsor,
        address requester,
        bytes4 fulfillFunctionId
    );

    event UpdatedBeaconWithPsp(
        bytes32 indexed beaconId,
        bytes32 subscriptionId,
        int224 value,
        uint32 timestamp
    );

    event UpdatedBeaconWithSignedData(
        bytes32 indexed beaconId,
        int256 value,
        uint256 timestamp
    );

    event UpdatedBeaconSetWithBeacons(
        bytes32 indexed beaconSetId,
        int224 value,
        uint32 timestamp
    );

    event UpdatedBeaconSetWithSignedData(
        bytes32 indexed dapiId,
        int224 value,
        uint32 timestamp
    );

    event AddedUnlimitedReader(address indexed unlimitedReader);

    event SetDapiName(
        bytes32 indexed dapiName,
        bytes32 dataFeedId,
        address indexed sender
    );

    function setRrpBeaconUpdatePermissionStatus(
        address rrpBeaconUpdateRequester,
        bool status
    ) external;

    function requestRrpBeaconUpdate(
        address airnode,
        bytes32 templateId,
        address sponsor
    ) external returns (bytes32 requestId);

    function requestRrpBeaconUpdateRelayed(
        address airnode,
        bytes32 templateId,
        address relayer,
        address sponsor
    ) external returns (bytes32 requestId);

    function fulfillRrpBeaconUpdate(
        bytes32 requestId,
        uint256 timestamp,
        bytes calldata data
    ) external;

    function registerBeaconUpdateSubscription(
        address airnode,
        bytes32 templateId,
        bytes memory conditions,
        address relayer,
        address sponsor
    ) external returns (bytes32 subscriptionId);

    function conditionPspBeaconUpdate(
        bytes32 subscriptionId,
        bytes calldata data,
        bytes calldata conditionParameters
    ) external view returns (bool);

    function fulfillPspBeaconUpdate(
        bytes32 subscriptionId,
        address airnode,
        address relayer,
        address sponsor,
        uint256 timestamp,
        bytes calldata data,
        bytes calldata signature
    ) external;

    function updateBeaconWithSignedData(
        address airnode,
        bytes32 beaconId,
        uint256 timestamp,
        bytes calldata data,
        bytes calldata signature
    ) external;

    function updateBeaconSetWithBeacons(bytes32[] memory beaconIds)
        external
        returns (bytes32 beaconSetId);

    function updateBeaconSetWithBeaconsAndReturnCondition(
        bytes32[] memory beaconIds,
        uint256 updateThresholdInPercentage
    ) external returns (bool);

    function conditionPspBeaconSetUpdate(
        bytes32 subscriptionId,
        bytes calldata data,
        bytes calldata conditionParameters
    ) external returns (bool);

    function fulfillPspBeaconSetUpdate(
        bytes32 subscriptionId,
        address airnode,
        address relayer,
        address sponsor,
        uint256 timestamp,
        bytes calldata data,
        bytes calldata signature
    ) external;

    function updateBeaconSetWithSignedData(
        address[] memory airnodes,
        bytes32[] memory templateIds,
        uint256[] memory timestamps,
        bytes[] memory data,
        bytes[] memory signatures
    ) external returns (bytes32 beaconSetId);

    function addUnlimitedReader(address unlimitedReader) external;

    function setDapiName(bytes32 dapiName, bytes32 dataFeedId) external;

    function dapiNameToDataFeedId(bytes32 dapiName)
        external
        view
        returns (bytes32);

    function readDataFeedWithId(bytes32 dataFeedId)
        external
        view
        returns (int224 value, uint32 timestamp);

    function readDataFeedValueWithId(bytes32 dataFeedId)
        external
        view
        returns (int224 value);

    function readDataFeedWithDapiName(bytes32 dapiName)
        external
        view
        returns (int224 value, uint32 timestamp);

    function readDataFeedValueWithDapiName(bytes32 dapiName)
        external
        view
        returns (int224 value);

    function readerCanReadDataFeed(bytes32 dataFeedId, address reader)
        external
        view
        returns (bool);

    function dataFeedIdToReaderToWhitelistStatus(
        bytes32 dataFeedId,
        address reader
    )
        external
        view
        returns (uint64 expirationTimestamp, uint192 indefiniteWhitelistCount);

    function dataFeedIdToReaderToSetterToIndefiniteWhitelistStatus(
        bytes32 dataFeedId,
        address reader,
        address setter
    ) external view returns (bool indefiniteWhitelistStatus);

    function deriveBeaconId(address airnode, bytes32 templateId)
        external
        pure
        returns (bytes32 beaconId);

    function deriveBeaconSetId(bytes32[] memory beaconIds)
        external
        pure
        returns (bytes32 beaconSetId);

    // solhint-disable-next-line func-name-mixedcase
    function DAPI_NAME_SETTER_ROLE_DESCRIPTION()
        external
        view
        returns (string memory);

    // solhint-disable-next-line func-name-mixedcase
    function HUNDRED_PERCENT() external view returns (uint256);

    function dapiNameSetterRole() external view returns (bytes32);

    function sponsorToRrpBeaconUpdateRequesterToPermissionStatus(
        address sponsor,
        address updateRequester
    ) external view returns (bool);

    function subscriptionIdToBeaconId(bytes32 subscriptionId)
        external
        view
        returns (bytes32);
}
          

@api3/airnode-protocol-v1/contracts/protocol/AirnodeRequester.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/IAirnodeProtocol.sol";
import "./interfaces/IAirnodeRequester.sol";

/// @title Contract to be inherited by contracts that will make Airnode
/// requests and receive fulfillments
contract AirnodeRequester is IAirnodeRequester {
    /// @notice AirnodeProtocol contract address
    address public immutable override airnodeProtocol;

    /// @dev Reverts if the sender is not the AirnodeProtocol contract. Use
    /// this modifier with methods that are meant to receive RRP fulfillments.
    modifier onlyAirnodeProtocol() {
        require(
            msg.sender == address(airnodeProtocol),
            "Sender not Airnode protocol"
        );
        _;
    }

    /// @dev Reverts if the timestamp is not valid. Use this modifier with
    /// methods that are meant to receive RRP and PSP fulfillments.
    /// @param timestamp Timestamp used in the signature
    modifier onlyValidTimestamp(uint256 timestamp) {
        require(timestampIsValid(timestamp), "Timestamp not valid");
        _;
    }

    /// @param _airnodeProtocol AirnodeProtocol contract address
    constructor(address _airnodeProtocol) {
        require(_airnodeProtocol != address(0), "AirnodeProtocol address zero");
        airnodeProtocol = _airnodeProtocol;
    }

    /// @notice Returns if the timestamp used in the signature is valid
    /// @dev Returns `false` if the timestamp is not at most 1 hour old to
    /// prevent replays. Returns `false` if the timestamp is not from the past,
    /// with some leeway to accomodate for some benign time drift. These values
    /// are appropriate in most cases, but you can adjust them if you are aware
    /// of the implications.
    /// @param timestamp Timestamp used in the signature
    function timestampIsValid(uint256 timestamp) internal view returns (bool) {
        return
            timestamp + 1 hours > block.timestamp &&
            timestamp < block.timestamp + 15 minutes;
    }
}
          

@api3/airnode-protocol-v1/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);
}
          

@api3/airnode-protocol-v1/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);
}
          

@api3/airnode-protocol-v1/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
        );
}
          

@api3/airnode-protocol-v1/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);
}
          

@api3/airnode-protocol-v1/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;
    }
}
          

@api3/airnode-protocol-v1/contracts/whitelist/Whitelist.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contract to be inherited by contracts that need temporary and
/// permanent whitelists for services identified by hashes
/// @notice This contract implements two kinds of whitelisting:
///   (1) Temporary, ends when the expiration timestamp is in the past
///   (2) Indefinite, ends when the indefinite whitelist count is zero
/// Multiple senders can indefinitely whitelist/unwhitelist independently. The
/// user will be considered whitelisted as long as there is at least one active
/// indefinite whitelisting.
/// @dev The interface of this contract is not implemented. It should be
/// inherited and its functions should be exposed with a sort of an
/// authorization scheme.
contract Whitelist {
    struct WhitelistStatus {
        uint64 expirationTimestamp;
        uint192 indefiniteWhitelistCount;
    }

    mapping(bytes32 => mapping(address => WhitelistStatus))
        internal serviceIdToUserToWhitelistStatus;

    mapping(bytes32 => mapping(address => mapping(address => bool)))
        internal serviceIdToUserToSetterToIndefiniteWhitelistStatus;

    /// @notice Extends the expiration of the temporary whitelist of the user
    /// for the service
    /// @param serviceId Service ID
    /// @param user User address
    /// @param expirationTimestamp Timestamp at which the temporary whitelist
    /// will expire
    function _extendWhitelistExpiration(
        bytes32 serviceId,
        address user,
        uint64 expirationTimestamp
    ) internal {
        require(
            expirationTimestamp >
                serviceIdToUserToWhitelistStatus[serviceId][user]
                    .expirationTimestamp,
            "Does not extend expiration"
        );
        serviceIdToUserToWhitelistStatus[serviceId][user]
            .expirationTimestamp = expirationTimestamp;
    }

    /// @notice Sets the expiration of the temporary whitelist of the user for
    /// the service
    /// @dev Unlike `extendWhitelistExpiration()`, this can hasten expiration
    /// @param serviceId Service ID
    /// @param user User address
    /// @param expirationTimestamp Timestamp at which the temporary whitelist
    /// will expire
    function _setWhitelistExpiration(
        bytes32 serviceId,
        address user,
        uint64 expirationTimestamp
    ) internal {
        serviceIdToUserToWhitelistStatus[serviceId][user]
            .expirationTimestamp = expirationTimestamp;
    }

    /// @notice Sets the indefinite whitelist status of the user for the
    /// service
    /// @dev As long as at least there is at least one account that has set the
    /// indefinite whitelist status of the user for the service as true, the
    /// user will be considered whitelisted
    /// @param serviceId Service ID
    /// @param user User address
    /// @param status Indefinite whitelist status
    function _setIndefiniteWhitelistStatus(
        bytes32 serviceId,
        address user,
        bool status
    ) internal returns (uint192 indefiniteWhitelistCount) {
        indefiniteWhitelistCount = serviceIdToUserToWhitelistStatus[serviceId][
            user
        ].indefiniteWhitelistCount;
        if (
            status &&
            !serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][
                user
            ][msg.sender]
        ) {
            serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
                msg.sender
            ] = true;
            indefiniteWhitelistCount++;
            serviceIdToUserToWhitelistStatus[serviceId][user]
                .indefiniteWhitelistCount = indefiniteWhitelistCount;
        } else if (
            !status &&
            serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
                msg.sender
            ]
        ) {
            serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
                msg.sender
            ] = false;
            indefiniteWhitelistCount--;
            serviceIdToUserToWhitelistStatus[serviceId][user]
                .indefiniteWhitelistCount = indefiniteWhitelistCount;
        }
    }

    /// @notice Revokes the indefinite whitelist status granted to the user for
    /// the service by a specific account
    /// @param serviceId Service ID
    /// @param user User address
    /// @param setter Setter of the indefinite whitelist status
    function _revokeIndefiniteWhitelistStatus(
        bytes32 serviceId,
        address user,
        address setter
    ) internal returns (bool revoked, uint192 indefiniteWhitelistCount) {
        indefiniteWhitelistCount = serviceIdToUserToWhitelistStatus[serviceId][
            user
        ].indefiniteWhitelistCount;
        if (
            serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
                setter
            ]
        ) {
            serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
                setter
            ] = false;
            indefiniteWhitelistCount--;
            serviceIdToUserToWhitelistStatus[serviceId][user]
                .indefiniteWhitelistCount = indefiniteWhitelistCount;
            revoked = true;
        }
    }

    /// @notice Returns if the user is whitelised to use the service
    /// @param serviceId Service ID
    /// @param user User address
    /// @return isWhitelisted If the user is whitelisted
    function userIsWhitelisted(bytes32 serviceId, address user)
        internal
        view
        returns (bool isWhitelisted)
    {
        WhitelistStatus
            storage whitelistStatus = serviceIdToUserToWhitelistStatus[
                serviceId
            ][user];
        return
            whitelistStatus.indefiniteWhitelistCount > 0 ||
            whitelistStatus.expirationTimestamp > block.timestamp;
    }
}
          

@api3/airnode-protocol-v1/contracts/whitelist/WhitelistRoles.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/IWhitelistRoles.sol";

/// @title Contract to be inherited by Whitelist contracts that will use
/// generic AccessControlRegistry roles
contract WhitelistRoles is IWhitelistRoles {
    // There are four roles implemented in this contract:
    // Root
    // └── (1) Admin (can grant and revoke the roles below)
    //     ├── (2) Whitelist expiration extender
    //     ├── (3) Whitelist expiration setter
    //     └── (4) Indefinite whitelister
    // Their IDs are derived from the descriptions below. Refer to
    // AccessControlRegistry for more information.
    // To clarify, the root role of the manager is the admin of (1), while (1)
    // is the admin of (2), (3) and (4). So (1) is more of a "contract admin",
    // while the `adminRole` used in AccessControl and AccessControlRegistry
    // refers to a more general adminship relationship between roles.

    /// @notice Whitelist expiration extender role description
    string
        public constant
        override WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION =
        "Whitelist expiration extender";

    /// @notice Whitelist expiration setter role description
    string
        public constant
        override WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION =
        "Whitelist expiration setter";

    /// @notice Indefinite whitelister role description

    string public constant override INDEFINITE_WHITELISTER_ROLE_DESCRIPTION =
        "Indefinite whitelister";

    bytes32
        internal constant WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION_HASH =
        keccak256(
            abi.encodePacked(WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION)
        );

    bytes32
        internal constant WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION_HASH =
        keccak256(
            abi.encodePacked(WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION)
        );

    bytes32 internal constant INDEFINITE_WHITELISTER_ROLE_DESCRIPTION_HASH =
        keccak256(abi.encodePacked(INDEFINITE_WHITELISTER_ROLE_DESCRIPTION));
}
          

@api3/airnode-protocol-v1/contracts/whitelist/WhitelistRolesWithManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./WhitelistRoles.sol";
import "../access-control-registry/AccessControlRegistryAdminnedWithManager.sol";
import "./interfaces/IWhitelistRolesWithManager.sol";
import "../access-control-registry/interfaces/IAccessControlRegistry.sol";

/// @title Contract to be inherited by Whitelist contracts that will use
/// roles where there is a single manager
contract WhitelistRolesWithManager is
    WhitelistRoles,
    AccessControlRegistryAdminnedWithManager,
    IWhitelistRolesWithManager
{
    // Since there will be a single manager, we can derive the roles beforehand

    /// @notice Whitelist expiration extender role
    bytes32 public immutable override whitelistExpirationExtenderRole;

    /// @notice Whitelist expiration setter role
    bytes32 public immutable override whitelistExpirationSetterRole;

    /// @notice Indefinite whitelister role
    bytes32 public immutable override indefiniteWhitelisterRole;

    /// @param _accessControlRegistry AccessControlRegistry contract address
    /// @param _adminRoleDescription Admin role description
    /// @param _manager Manager address
    constructor(
        address _accessControlRegistry,
        string memory _adminRoleDescription,
        address _manager
    )
        AccessControlRegistryAdminnedWithManager(
            _accessControlRegistry,
            _adminRoleDescription,
            _manager
        )
    {
        whitelistExpirationExtenderRole = _deriveRole(
            adminRole,
            WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION_HASH
        );
        whitelistExpirationSetterRole = _deriveRole(
            adminRole,
            WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION_HASH
        );
        indefiniteWhitelisterRole = _deriveRole(
            adminRole,
            INDEFINITE_WHITELISTER_ROLE_DESCRIPTION_HASH
        );
    }

    /// @dev Returns if the account has the whitelist expiration extender role
    /// or is the manager
    /// @param account Account address
    /// @return If the account has the whitelist extender role or is the
    /// manager
    function hasWhitelistExpirationExtenderRoleOrIsManager(address account)
        internal
        view
        returns (bool)
    {
        return
            manager == account ||
            IAccessControlRegistry(accessControlRegistry).hasRole(
                whitelistExpirationExtenderRole,
                account
            );
    }

    /// @dev Returns if the account has the whitelist expriation setter role or
    /// is the manager
    /// @param account Account address
    /// @return If the account has the whitelist setter role or is the
    /// manager
    function hasWhitelistExpirationSetterRoleOrIsManager(address account)
        internal
        view
        returns (bool)
    {
        return
            manager == account ||
            IAccessControlRegistry(accessControlRegistry).hasRole(
                whitelistExpirationSetterRole,
                account
            );
    }

    /// @dev Returns if the account has the indefinite whitelister role or is the
    /// manager
    /// @param account Account address
    /// @return If the account has the indefinite whitelister role or is the
    /// manager
    function hasIndefiniteWhitelisterRoleOrIsManager(address account)
        internal
        view
        returns (bool)
    {
        return
            manager == account ||
            IAccessControlRegistry(accessControlRegistry).hasRole(
                indefiniteWhitelisterRole,
                account
            );
    }
}
          

@api3/airnode-protocol-v1/contracts/whitelist/WhitelistWithManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./Whitelist.sol";
import "./WhitelistRolesWithManager.sol";
import "./interfaces/IWhitelistWithManager.sol";

/// @title Contract to be inherited by Whitelist contracts that are controlled
/// by a manager
contract WhitelistWithManager is
    Whitelist,
    WhitelistRolesWithManager,
    IWhitelistWithManager
{
    /// @param _accessControlRegistry AccessControlRegistry contract address
    /// @param _adminRoleDescription Admin role description
    /// @param _manager Manager address
    constructor(
        address _accessControlRegistry,
        string memory _adminRoleDescription,
        address _manager
    )
        WhitelistRolesWithManager(
            _accessControlRegistry,
            _adminRoleDescription,
            _manager
        )
    {}

    /// @notice Extends the expiration of the temporary whitelist of `user` to
    /// be able to use the service with `serviceId` if the sender has the
    /// whitelist expiration extender role
    /// @param serviceId Service ID
    /// @param user User address
    /// @param expirationTimestamp Timestamp at which the temporary whitelist
    /// will expire
    function extendWhitelistExpiration(
        bytes32 serviceId,
        address user,
        uint64 expirationTimestamp
    ) external override {
        require(
            hasWhitelistExpirationExtenderRoleOrIsManager(msg.sender),
            "Cannot extend expiration"
        );
        require(serviceId != bytes32(0), "Service ID zero");
        require(user != address(0), "User address zero");
        _extendWhitelistExpiration(serviceId, user, expirationTimestamp);
        emit ExtendedWhitelistExpiration(
            serviceId,
            user,
            msg.sender,
            expirationTimestamp
        );
    }

    /// @notice Sets the expiration of the temporary whitelist of `user` to be
    /// able to use the service with `serviceId` if the sender has the
    /// whitelist expiration setter role
    /// @param serviceId Service ID
    /// @param user User address
    /// @param expirationTimestamp Timestamp at which the temporary whitelist
    /// will expire
    function setWhitelistExpiration(
        bytes32 serviceId,
        address user,
        uint64 expirationTimestamp
    ) external override {
        require(
            hasWhitelistExpirationSetterRoleOrIsManager(msg.sender),
            "Cannot set expiration"
        );
        require(serviceId != bytes32(0), "Service ID zero");
        require(user != address(0), "User address zero");
        _setWhitelistExpiration(serviceId, user, expirationTimestamp);
        emit SetWhitelistExpiration(
            serviceId,
            user,
            msg.sender,
            expirationTimestamp
        );
    }

    /// @notice Sets the indefinite whitelist status of `user` to be able to
    /// use the service with `serviceId` if the sender has the indefinite
    /// whitelister role
    /// @param serviceId Service ID
    /// @param user User address
    /// @param status Indefinite whitelist status
    function setIndefiniteWhitelistStatus(
        bytes32 serviceId,
        address user,
        bool status
    ) external override {
        require(
            hasIndefiniteWhitelisterRoleOrIsManager(msg.sender),
            "Cannot set indefinite status"
        );
        require(serviceId != bytes32(0), "Service ID zero");
        require(user != address(0), "User address zero");
        uint192 indefiniteWhitelistCount = _setIndefiniteWhitelistStatus(
            serviceId,
            user,
            status
        );
        emit SetIndefiniteWhitelistStatus(
            serviceId,
            user,
            msg.sender,
            status,
            indefiniteWhitelistCount
        );
    }

    /// @notice Revokes the indefinite whitelist status granted by a specific
    /// account that no longer has the indefinite whitelister role
    /// @param serviceId Service ID
    /// @param user User address
    /// @param setter Setter of the indefinite whitelist status
    function revokeIndefiniteWhitelistStatus(
        bytes32 serviceId,
        address user,
        address setter
    ) external override {
        require(
            !hasIndefiniteWhitelisterRoleOrIsManager(setter),
            "setter can set indefinite status"
        );
        (
            bool revoked,
            uint192 indefiniteWhitelistCount
        ) = _revokeIndefiniteWhitelistStatus(serviceId, user, setter);
        if (revoked) {
            emit RevokedIndefiniteWhitelistStatus(
                serviceId,
                user,
                setter,
                msg.sender,
                indefiniteWhitelistCount
            );
        }
    }
}
          

@api3/airnode-protocol-v1/contracts/whitelist/interfaces/IWhitelistRoles.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWhitelistRoles {
    // solhint-disable-next-line func-name-mixedcase
    function WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION()
        external
        view
        returns (string memory);

    // solhint-disable-next-line func-name-mixedcase
    function WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION()
        external
        view
        returns (string memory);

    // solhint-disable-next-line func-name-mixedcase
    function INDEFINITE_WHITELISTER_ROLE_DESCRIPTION()
        external
        view
        returns (string memory);
}
          

@api3/airnode-protocol-v1/contracts/whitelist/interfaces/IWhitelistRolesWithManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IWhitelistRoles.sol";
import "../../access-control-registry/interfaces/IAccessControlRegistryAdminnedWithManager.sol";

interface IWhitelistRolesWithManager is
    IWhitelistRoles,
    IAccessControlRegistryAdminnedWithManager
{
    function whitelistExpirationExtenderRole() external view returns (bytes32);

    function whitelistExpirationSetterRole() external view returns (bytes32);

    function indefiniteWhitelisterRole() external view returns (bytes32);
}
          

@api3/airnode-protocol-v1/contracts/whitelist/interfaces/IWhitelistWithManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IWhitelistRolesWithManager.sol";

interface IWhitelistWithManager is IWhitelistRolesWithManager {
    event ExtendedWhitelistExpiration(
        bytes32 indexed serviceId,
        address indexed user,
        address indexed sender,
        uint256 expiration
    );

    event SetWhitelistExpiration(
        bytes32 indexed serviceId,
        address indexed user,
        address indexed sender,
        uint256 expiration
    );

    event SetIndefiniteWhitelistStatus(
        bytes32 indexed serviceId,
        address indexed user,
        address indexed sender,
        bool status,
        uint192 indefiniteWhitelistCount
    );

    event RevokedIndefiniteWhitelistStatus(
        bytes32 indexed serviceId,
        address indexed user,
        address indexed setter,
        address sender,
        uint192 indefiniteWhitelistCount
    );

    function extendWhitelistExpiration(
        bytes32 serviceId,
        address user,
        uint64 expirationTimestamp
    ) external;

    function setWhitelistExpiration(
        bytes32 serviceId,
        address user,
        uint64 expirationTimestamp
    ) external;

    function setIndefiniteWhitelistStatus(
        bytes32 serviceId,
        address user,
        bool status
    ) external;

    function revokeIndefiniteWhitelistStatus(
        bytes32 serviceId,
        address user,
        address setter
    ) external;
}
          

@openzeppelin/contracts/access/IAccessControl.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
          

@openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
          

@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);
    }
}
          

@openzeppelin/contracts/utils/cryptography/ECDSA.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 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));
    }
}
          

Contract ABI

[{"type":"constructor","inputs":[{"type":"address","name":"_accessControlRegistry","internalType":"address"},{"type":"string","name":"_adminRoleDescription","internalType":"string"},{"type":"address","name":"_manager","internalType":"address"},{"type":"address","name":"_airnodeProtocol","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"DAPI_NAME_SETTER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"HUNDRED_PERCENT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"INDEFINITE_WHITELISTER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"accessControlRegistry","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addUnlimitedReader","inputs":[{"type":"address","name":"unlimitedReader","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"adminRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"adminRoleDescription","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"airnodeProtocol","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"conditionPspBeaconSetUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"conditionParameters","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"conditionPspBeaconUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"conditionParameters","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"dapiNameSetterRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"dapiNameToDataFeedId","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"indefiniteWhitelistStatus","internalType":"bool"}],"name":"dataFeedIdToReaderToSetterToIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"},{"type":"address","name":"reader","internalType":"address"},{"type":"address","name":"setter","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"expirationTimestamp","internalType":"uint64"},{"type":"uint192","name":"indefiniteWhitelistCount","internalType":"uint192"}],"name":"dataFeedIdToReaderToWhitelistStatus","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"},{"type":"address","name":"reader","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"beaconId","internalType":"bytes32"}],"name":"deriveBeaconId","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"beaconSetId","internalType":"bytes32"}],"name":"deriveBeaconSetId","inputs":[{"type":"bytes32[]","name":"beaconIds","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"extendWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"uint64","name":"expirationTimestamp","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fulfillPspBeaconSetUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fulfillPspBeaconUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fulfillRrpBeaconUpdate","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","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":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"indefiniteWhitelisterRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"manager","inputs":[]},{"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":"int224","name":"value","internalType":"int224"}],"name":"readDataFeedValueWithDapiName","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"}],"name":"readDataFeedValueWithId","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"},{"type":"uint32","name":"timestamp","internalType":"uint32"}],"name":"readDataFeedWithDapiName","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"},{"type":"uint32","name":"timestamp","internalType":"uint32"}],"name":"readDataFeedWithId","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"readerCanReadDataFeed","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"},{"type":"address","name":"reader","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"}],"name":"registerBeaconUpdateSubscription","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"bytes","name":"conditions","internalType":"bytes"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}],"name":"requestRrpBeaconUpdate","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"address","name":"sponsor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}],"name":"requestRrpBeaconUpdateRelayed","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"setter","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDapiName","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"},{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRrpBeaconUpdatePermissionStatus","inputs":[{"type":"address","name":"rrpBeaconUpdateRequester","internalType":"address"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"uint64","name":"expirationTimestamp","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"sponsorToRrpBeaconUpdateRequesterToPermissionStatus","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"subscriptionIdToBeaconId","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"unlimitedReaderStatus","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"beaconSetId","internalType":"bytes32"}],"name":"updateBeaconSetWithBeacons","inputs":[{"type":"bytes32[]","name":"beaconIds","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"updateBeaconSetWithBeaconsAndReturnCondition","inputs":[{"type":"bytes32[]","name":"beaconIds","internalType":"bytes32[]"},{"type":"uint256","name":"deviationThresholdInPercentage","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"beaconSetId","internalType":"bytes32"}],"name":"updateBeaconSetWithSignedData","inputs":[{"type":"address[]","name":"airnodes","internalType":"address[]"},{"type":"bytes32[]","name":"templateIds","internalType":"bytes32[]"},{"type":"uint256[]","name":"timestamps","internalType":"uint256[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"},{"type":"bytes[]","name":"signatures","internalType":"bytes[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateBeaconWithSignedData","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"whitelistExpirationExtenderRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"whitelistExpirationSetterRole","inputs":[]},{"type":"event","name":"AddedUnlimitedReader","inputs":[{"type":"address","name":"unlimitedReader","indexed":true}],"anonymous":false},{"type":"event","name":"ExtendedWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"expiration","indexed":false}],"anonymous":false},{"type":"event","name":"RegisteredBeaconUpdateSubscription","inputs":[{"type":"bytes32","name":"subscriptionId","indexed":true},{"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":"RequestedRrpBeaconUpdate","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"requester","indexed":true},{"type":"bytes32","name":"requestId","indexed":false},{"type":"address","name":"airnode","indexed":false},{"type":"bytes32","name":"templateId","indexed":false}],"anonymous":false},{"type":"event","name":"RequestedRrpBeaconUpdateRelayed","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"requester","indexed":true},{"type":"bytes32","name":"requestId","indexed":false},{"type":"address","name":"airnode","indexed":false},{"type":"address","name":"relayer","indexed":false},{"type":"bytes32","name":"templateId","indexed":false}],"anonymous":false},{"type":"event","name":"RevokedIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"setter","indexed":true},{"type":"address","name":"sender","indexed":false},{"type":"uint192","name":"indefiniteWhitelistCount","indexed":false}],"anonymous":false},{"type":"event","name":"SetDapiName","inputs":[{"type":"bytes32","name":"dapiName","indexed":true},{"type":"bytes32","name":"dataFeedId","indexed":false},{"type":"address","name":"sender","indexed":true}],"anonymous":false},{"type":"event","name":"SetIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"bool","name":"status","indexed":false},{"type":"uint192","name":"indefiniteWhitelistCount","indexed":false}],"anonymous":false},{"type":"event","name":"SetRrpBeaconUpdatePermissionStatus","inputs":[{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"rrpBeaconUpdateRequester","indexed":true},{"type":"bool","name":"status","indexed":false}],"anonymous":false},{"type":"event","name":"SetWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"expiration","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconSetWithBeacons","inputs":[{"type":"bytes32","name":"beaconSetId","indexed":true},{"type":"int224","name":"value","indexed":false},{"type":"uint32","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconSetWithSignedData","inputs":[{"type":"bytes32","name":"dapiId","indexed":true},{"type":"int224","name":"value","indexed":false},{"type":"uint32","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconWithPsp","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"bytes32","name":"subscriptionId","indexed":false},{"type":"int224","name":"value","indexed":false},{"type":"uint32","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconWithRrp","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"bytes32","name":"requestId","indexed":false},{"type":"int256","name":"value","indexed":false},{"type":"uint256","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconWithSignedData","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"int256","name":"value","indexed":false},{"type":"uint256","name":"timestamp","indexed":false}],"anonymous":false}]
              

Contract Creation Code

0x6101a06040523480156200001257600080fd5b5060405162005d7938038062005d7983398101604081905262000035916200054d565b808484848282828282828282816001600160a01b038116620000915760405162461bcd60e51b815260206004820152601060248201526f4143522061646472657373207a65726f60801b60448201526064015b60405180910390fd5b6001600160a01b03166080528051620000ed5760405162461bcd60e51b815260206004820152601c60248201527f41646d696e20726f6c65206465736372697074696f6e20656d70747900000000604482015260640162000088565b80516200010290600290602084019062000441565b508060405160200162000116919062000640565b60408051601f19818403018152919052805160209091012060a05250506001600160a01b0381166200018b5760405162461bcd60e51b815260206004820152601460248201527f4d616e616765722061646472657373207a65726f000000000000000000000000604482015260640162000088565b6001600160a01b03811660c052620001a3816200039b565b60e081815250505050506200021e60e0516040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e646572000000815250604051602001620001fc919062000640565b604051602081830303815290604052805190602001206200041560201b60201c565b61010081815250506200027560e0516040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e207365747465720000000000815250604051602001620001fc919062000640565b6101208181525050620002cc60e0516040518060400160405280601681526020017f496e646566696e6974652077686974656c697374657200000000000000000000815250604051602001620001fc919062000640565b6101405250505050506001600160a01b03821615159050620003315760405162461bcd60e51b815260206004820152601c60248201527f4169726e6f646550726f746f636f6c2061646472657373207a65726f00000000604482015260640162000088565b6001600160a01b03166101605260c0516200038c9062000351906200039b565b6040518060400160405280601081526020016f3220a824903730b6b29039b2ba3a32b960811b815250604051602001620001fc919062000640565b61018052506200069b92505050565b60006200040f620003e0836040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b60a051604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b92915050565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b8280546200044f906200065e565b90600052602060002090601f016020900481019282620004735760008555620004be565b82601f106200048e57805160ff1916838001178555620004be565b82800160010185558215620004be579182015b82811115620004be578251825591602001919060010190620004a1565b50620004cc929150620004d0565b5090565b5b80821115620004cc5760008155600101620004d1565b80516001600160a01b0381168114620004ff57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620005375781810151838201526020016200051d565b8381111562000547576000848401525b50505050565b600080600080608085870312156200056457600080fd5b6200056f85620004e7565b60208601519094506001600160401b03808211156200058d57600080fd5b818701915087601f830112620005a257600080fd5b815181811115620005b757620005b762000504565b604051601f8201601f19908116603f01168101908382118183101715620005e257620005e262000504565b816040528281528a6020848701011115620005fc57600080fd5b6200060f8360208301602088016200051a565b80975050505050506200062560408601620004e7565b91506200063560608601620004e7565b905092959194509250565b60008251620006548184602087016200051a565b9190910192915050565b600181811c908216806200067357607f821691505b602082108114156200069557634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e051610100516101205161014051610160516101805161560062000779600039600081816105b6015261207401526000818161076a015281816116cd015281816122f00152612bb001526000818161089201526132f101526000818161065301526131b10152600081816105f601526137c1015260006103540152600081816104920152818161203e015281816129f501528181613173015281816132b30152613783015260005050600081816103c9015281816120a0015281816131e70152818161332701526137f701526156006000f3fe608060405234801561001057600080fd5b506004361061034a5760003560e01c80638e85afe0116101bd578063bcb5daec116100f9578063e18de857116100a2578063f60e45d31161007c578063f60e45d3146108da578063f8b2cb4f1461091b578063fc3c1f8b14610936578063fce90be81461094957600080fd5b8063e18de8571461088d578063e406f3b2146108b4578063e705882e146108c757600080fd5b8063d55a42dd116100d3578063d55a42dd1461082b578063d7c0b87914610867578063dc96acc81461087a57600080fd5b8063bcb5daec1461078c578063c4f95dd51461079f578063d39c8968146107b257600080fd5b80639e14caa811610166578063a6b1b9a211610140578063a6b1b9a21461070f578063ac9650d814610722578063b605bbcd14610742578063b76f3c151461076557600080fd5b80639e14caa8146106c9578063a026658f146106dc578063a5fc076f146106fc57600080fd5b806391eed0851161019757806391eed085146106755780639421240514610688578063977cae011461069b57600080fd5b80638e85afe0146106185780638fca9ab91461063b5780638fe47dd81461064e57600080fd5b80634c8f1d8d1161028c5780636db798f9116102355780637512449b1161020f5780637512449b146105b1578063776bcbd2146105d8578063796b89b9146105eb5780637c706738146105f157600080fd5b80636db798f9146105575780636ed93dd01461059357806370c927ca1461059e57600080fd5b80635bc8a5c8116102665780635bc8a5c8146104f557806365101054146105085780636bd2bdd01461051b57600080fd5b80634c8f1d8d146104b45780634dcc19fe146104c957806358c55edd146104cf57600080fd5b8063206b48f4116102f95780633408e470116102d35780633408e4701461044f5780633af1dbfd1461045557806342cbb15c14610487578063481c6a751461048d57600080fd5b8063206b48f4146104165780632ce010e3146104295780633376e7c21461043c57600080fd5b80631a0a0b3e1161032a5780631a0a0b3e146103af5780631ce9ae07146103c45780631d36cf5c1461040357600080fd5b80629f2f3c1461034f578062aae33f1461038957806304dd3cee1461039c575b600080fd5b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6103766103973660046147a4565b610985565b6103766103aa3660046147f5565b610be1565b6103c26103bd366004614861565b610cca565b005b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610380565b6103c26104113660046148f5565b610e46565b6103c2610424366004614942565b610fb1565b6103c2610437366004614942565b6112ad565b6103c261044a366004614a07565b61132c565b46610376565b610468610463366004614a3c565b611472565b60408051601b9390930b835263ffffffff909116602083015201610380565b43610376565b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b6104bc611574565b6040516103809190614ab1565b48610376565b6104e26104dd366004614a3c565b611602565b604051601b9190910b8152602001610380565b6103c2610503366004614ac4565b6116c2565b610376610516366004614c69565b6117f6565b6104bc6040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e20736574746572000000000081525081565b6104bc6040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e64657200000081525081565b6103766305f5e10081565b6104e26105ac366004614a3c565b611d3b565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c26105e6366004614d3b565b611e35565b42610376565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b61062b610626366004614d77565b611efe565b6040519015158152602001610380565b610376610649366004614a3c565b611fa1565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c2610683366004614dbc565b611fe6565b610376610696366004614dde565b6121f6565b61062b6106a9366004614e11565b600460209081526000928352604080842090915290825290205460ff1681565b6103c26106d7366004614e44565b6123e0565b6103766106ea366004614a3c565b60056020526000908152604090205481565b61046861070a366004614a3c565b61249b565b6103c261071d3660046148f5565b612511565b610735610730366004614e7b565b612643565b6040516103809190614ef0565b61062b610750366004614f52565b60036020526000908152604090205460ff1681565b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b61062b61079a366004614f6d565b612738565b6103766107ad3660046147a4565b61277f565b6108036107c0366004614f6d565b6000918252602082815260408084206001600160a01b0393909316845291905290205467ffffffffffffffff811691600160401b9091046001600160c01b031690565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610380565b6104bc6040518060400160405280601681526020017f496e646566696e6974652077686974656c69737465720000000000000000000081525081565b61062b610875366004614f90565b6127af565b61062b610888366004614f90565b6128b4565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c26108c2366004614f52565b6129ea565b6103766108d536600461500a565b612aae565b61062b6108e8366004614d3b565b60009283526001602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b610376610929366004614f52565b6001600160a01b03163190565b610376610944366004615057565b612cb9565b6104bc6040518060400160405280601081526020017f64415049206e616d65207365747465720000000000000000000000000000000081525081565b8051600090600181116109df5760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064015b60405180910390fd5b60008167ffffffffffffffff8111156109fa576109fa6146ce565b604051908082528060200260200182016040528015610a23578160200160208202803683370190505b5090506000805b83811015610abb57600060066000888481518110610a4a57610a4a6150d0565b602090810291909101810151825281019190915260400160002080548551919250601b0b90859084908110610a8157610a816150d0565b60209081029190910101528054610aa590600160e01b900463ffffffff16846150fc565b9250508080610ab390615114565b915050610a2a565b506000610ac88483615145565b9050610ad38661277f565b60008181526006602052604090205490955063ffffffff600160e01b90910481169082161015610b455760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000610b5084612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008e81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925087917fb7712be6248d021e8c56ac9613c09491354a4d0f4ad0b7db1a664b35be4b2349910160405180910390a25050505050919050565b60006001600160a01b038316610c395760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f00000000000000000000000060448201526064016109d6565b81610c865760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f0000000000000000000000000000000060448201526064016109d6565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260540160405160208183030381529060405280519060200120905092915050565b84610cd481612fd5565b610d165760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b876001600160a01b0316610d9584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508c908c908c908c90602001615159565b60405160208183030381529060405280519060200120613001565b9061303c565b6001600160a01b031614610de05760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b6000610dec8989610be1565b90506000610dfc82898989613060565b60408051828152602081018b905291925083917f403078446dab7471f481ca4bffac706bd84a6dba118980d267676c096f2ba924910160405180910390a250505050505050505050565b610e4f33613165565b610e9b5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f74207365742065787069726174696f6e000000000000000000000060448201526064016109d6565b82610eda5760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b038216610f245760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000838152602081815260408083206001600160a01b03861684529091529020805467ffffffffffffffff191667ffffffffffffffff831617905560405167ffffffffffffffff8216815233906001600160a01b0384169085907fd19e89b7d547ccf349211588a9a1d29461e2ce984b1b1cdbe7150976528b86f1906020015b60405180910390a4505050565b84610fbb81612fd5565b610ffd5760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6040516bffffffffffffffffffffffff1960608b811b821660208401528a811b8216603484015289901b166048820152605c0160408051601f19818403018152918152815160209283012060008d81526008909352912054146110a25760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b876001600160a01b0316896001600160a01b0316141561118b57886001600160a01b031661113b84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c903390602001928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b6001600160a01b0316146111865760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611236565b886001600160a01b03166111eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c9033908d908d9060200161517a565b6001600160a01b0316146112365760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b60008a8152600560205260408120549061125282898989613060565b604080518e8152601b83900b602082015263ffffffff8b1681830152905191925083917f2b85d71813342fd0091d4573a5f78e244c2133755e470943c33a6dbf3cf9c15d9181900360600190a2505050505050505050505050565b6112bc610397848601866147a4565b84846040516112cc9291906151b4565b6040518091039020146113215760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b505050505050505050565b611335336132a5565b6113815760405162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742073657420696e646566696e697465207374617475730000000060448201526064016109d6565b826113c05760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b03821661140a5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000611417848484613356565b6040805184151581526001600160c01b038316602082015291925033916001600160a01b0386169187917f2fa93828cf3f001b9a9f0a7365db04ea068b9db7bcb7a38d289f0cf8aa9ce374910160405180910390a450505050565b60008060008360405160200161148a91815260200190565b6040516020818303038152906040528051906020012090506114ac8133612738565b6114ed5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b600081815260096020526040902054806115495760405162461bcd60e51b815260206004820152601160248201527f64415049206e616d65206e6f742073657400000000000000000000000000000060448201526064016109d6565b600090815260066020526040902054601b81900b95600160e01b90910463ffffffff16945092505050565b60028054611581906151c4565b80601f01602080910402602001604051908101604052809291908181526020018280546115ad906151c4565b80156115fa5780601f106115cf576101008083540402835291602001916115fa565b820191906000526020600020905b8154815290600101906020018083116115dd57829003601f168201915b505050505081565b600061160e8233612738565b61164f5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b60008281526006602052604090208054600160e01b900463ffffffff166116b85760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b92915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461173a5760405162461bcd60e51b815260206004820152601b60248201527f53656e646572206e6f74204169726e6f64652070726f746f636f6c000000000060448201526064016109d6565b8261174481612fd5565b6117865760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6000858152600760205260408120805490829055906117a782878787613060565b604080518981526020810183905290810188905290915082907f7c88b543c4cda65df046e87397c3152c27098419cb22a90a5e37db30ef621b269060600160405180910390a250505050505050565b84518451600091908114801561180c5750845181145b80156118185750835181145b80156118245750825181145b6118705760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d617463680000000000000060448201526064016109d6565b600181116118c05760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064016109d6565b60008167ffffffffffffffff8111156118db576118db6146ce565b604051908082528060200260200182016040528015611904578160200160208202803683370190505b50905060008267ffffffffffffffff811115611922576119226146ce565b60405190808252806020026020018201604052801561194b578160200160208202803683370190505b5090506000805b84811015611c105786818151811061196c5761196c6150d0565b602002602001015151600014611b555760008b8281518110611990576119906150d0565b6020026020010151905060008a83815181106119ae576119ae6150d0565b602002602001015190506119c181612fd5565b611a035760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b816001600160a01b0316611a748a8581518110611a2257611a226150d0565b6020026020010151610d8f8f8781518110611a3f57611a3f6150d0565b6020026020010151858f8981518110611a5a57611a5a6150d0565b6020026020010151604051602001610d74939291906151f9565b6001600160a01b031614611abf5760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611ae18a8481518110611ad457611ad46150d0565b602002602001015161350e565b601b0b858481518110611af657611af66150d0565b6020908102919091010152611b0b81856150fc565b9350611b30828d8581518110611b2357611b236150d0565b6020026020010151610be1565b868481518110611b4257611b426150d0565b6020026020010181815250505050611bfe565b6000611b868c8381518110611b6c57611b6c6150d0565b60200260200101518c8481518110611b2357611b236150d0565b6000818152600660205260409020805486519293509091601b9190910b90869085908110611bb657611bb66150d0565b60209081029190910101528054611bda90600160e01b900463ffffffff16856150fc565b935081868481518110611bef57611bef6150d0565b60200260200101818152505050505b80611c0881615114565b915050611952565b50611c1a8361277f565b94506000611c288583615145565b60008781526006602052604090205490915063ffffffff600160e01b90910481169082161015611c9a5760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000611ca584612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008f81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925088917fb34747dc40d9c985b4857c2955fec7a8f34d88bc06da72f43319795758800407910160405180910390a250505050505095945050505050565b60008082604051602001611d5191815260200190565b604051602081830303815290604052805190602001209050611d738133612738565b611db45760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b6000818152600960209081526040808320548352600690915290208054600160e01b900463ffffffff16611e2a5760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b9392505050565b611e3e816132a5565b15611e8b5760405162461bcd60e51b815260206004820181905260248201527f7365747465722063616e2073657420696e646566696e6974652073746174757360448201526064016109d6565b600080611e99858585613611565b915091508115611ef757604080513381526001600160c01b03831660208201526001600160a01b03808616929087169188917f29c394c1d92801cab93215bf9cd50ae38d23341be6540f27d80ee2bc8a541237910160405180910390a45b5050505050565b600080611f0a8461277f565b600081815260066020908152604091829020825180840190935254601b81900b8352600160e01b900463ffffffff1690820152909150611f4985610985565b506000828152600660205260409020815181548691611f6a91601b0b6136fc565b101580611f975750602082015163ffffffff16158015611f9757508054600160e01b900463ffffffff1615155b9695505050505050565b60006009600083604051602001611fba91815260200190565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b816120335760405162461bcd60e51b815260206004820152600e60248201527f64415049206e616d65207a65726f00000000000000000000000000000000000060448201526064016109d6565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806121225750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d148549060440160206040518083038186803b1580156120ea57600080fd5b505afa1580156120fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121229190615226565b61216e5760405162461bcd60e51b815260206004820152601b60248201527f53656e6465722063616e6e6f74207365742064415049206e616d65000000000060448201526064016109d6565b80600960008460405160200161218691815260200190565b60405160208183030381529060405280519060200120815260200190815260200160002081905550336001600160a01b0316827ff3a9aac9b6ac0f842cb5d9b3491cd5fc1b6a6778d97fd9529f587339865294f5836040516121ea91815260200190565b60405180910390a35050565b6000816001600160a01b03811633148061223357506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b61227f5760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b600061228b8686610be1565b6040517feebecf690000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301526024820188905260a06044830152600060a48301528681166064830152630b7914b960e31b60848301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063eebecf699060c401602060405180830381600087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236e9190615243565b60008181526007602090815260409182902084905581518381526001600160a01b038a811692820192909252918201889052919450339186169083907fc0e8c8735457dd46668d6c01832656e0803b41b1e61ba12354a24f11e114eab39060600160405180910390a450509392505050565b6001600160a01b0382166124365760405162461bcd60e51b815260206004820152601560248201527f55706461746520726571756573746572207a65726f000000000000000000000060448201526064016109d6565b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fba239f737a4075f5efb0d649e16cd7d2b1978690876b079dab642f077050b27991016121ea565b6000806124a88333612738565b6124e95760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b5050600090815260066020526040902054601b81900b91600160e01b90910463ffffffff1690565b61251a33613775565b6125665760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420657874656e642065787069726174696f6e000000000000000060448201526064016109d6565b826125a55760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b0382166125ef5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6125fa838383613826565b60405167ffffffffffffffff8216815233906001600160a01b0384169085907fa9e0c89b898eb7a904617915dc5b5510d539c899810042e9248569b54b9cc2ed90602001610fa4565b60608167ffffffffffffffff81111561265e5761265e6146ce565b60405190808252806020026020018201604052801561269157816020015b606081526020019060019003908161267c5790505b50905060005b8281101561273157612701308585848181106126b5576126b56150d0565b90506020028101906126c7919061525c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506138a392505050565b828281518110612713576127136150d0565b6020026020010181905250808061272990615114565b915050612697565b5092915050565b60006001600160a01b0382161580612755575061275583836138c8565b8061277857506001600160a01b03821660009081526003602052604090205460ff165b9392505050565b60008160405160200161279291906152a3565b604051602081830303815290604052805190602001209050919050565b600033156127ff5760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600061280d858701876147a4565b9050858560405161281f9291906151b4565b60405180910390208160405160200161283891906152a3565b604051602081830303815290604052805190602001201461289b5760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6128a981610626868661391b565b979650505050505050565b600033156129045760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600086815260056020526040902054806129605760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b6000818152600660205260409020612978858561391b565b8154604080516020601f8b018190048102820181019092528981526129c592601b0b916129c091908c908c908190840183828082843760009201919091525061350e92505050565b6136fc565b1015806129de57508054600160e01b900463ffffffff16155b98975050505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612a625760405162461bcd60e51b815260206004820152601260248201527f53656e646572206e6f74206d616e61676572000000000000000000000000000060448201526064016109d6565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f6d71fde11d6c56eaf0154d464b51df0b26dcdac118703056ea3da805fc9e1cd79190a250565b6000816001600160a01b038116331480612aeb57506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b612b375760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b6000612b438787610be1565b6040517fa7e0c85e0000000000000000000000000000000000000000000000000000000081526001600160a01b0389811660048301526024820189905260c06044830152600060c483015287811660648301528681166084830152630b7914b960e31b60a48301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a7e0c85e9060e401602060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2e9190615243565b600081815260076020526040908190208390555190935033906001600160a01b0386169083907faeef85c84fba3ef952ca2738d951d30f5093595cb5eb9830764f5b49a39442a390612ca79088908d908c908e909384526001600160a01b03928316602085015291166040830152606082015260800190565b60405180910390a45050949350505050565b60006001600160a01b038316612d115760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b6001600160a01b038216612d675760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b604051612d8d90469088908890889088908890309063081ad23d60e21b906020016152e7565b60408051601f1981840301815282825280516020918201206bffffffffffffffffffffffff1960608b811b82168487015288811b8216603487015287901b1660488501528251808503603c018152605c909401835283519382019390932060008481526008909252919020559050612e058686610be1565b60008281526005602052604090819020919091555181907f82b139c5e690a3a4a9c2f68133d2ab1f7724bf8a69ad3fe1cd9f5a4923cf41b290612e5c9089908990899089908990309063081ad23d60e21b90615376565b60405180910390a295945050505050565b805160009060098111612f2d57612e8383613979565b612e8e6002826153fd565b60011415612ec25782612ea2600283615145565b81518110612eb257612eb26150d0565b6020026020010151915050919050565b600283612ecf8284615145565b81518110612edf57612edf6150d0565b6020026020010151846001600285612ef79190615145565b612f019190615411565b81518110612f1157612f116150d0565b6020026020010151612f239190615428565b6127789190615467565b612f386002826153fd565b60011415612f555782612ea281612f50600285615145565b613e90565b600080612f78856001612f69600287615145565b612f739190615411565b613eae565b915091506002858281518110612f9057612f906150d0565b6020026020010151868481518110612faa57612faa6150d0565b6020026020010151612fbc9190615428565b612fc69190615467565b95945050505050565b50919050565b600042612fe483610e106150fc565b118015612ffb5750612ff8426103846150fc565b82105b92915050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01612792565b600080600061304b8585613f2b565b9150915061305881613f98565b509392505050565b60006130a183838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061350e92505050565b600086815260066020526040902054601b9190910b9150600160e01b900463ffffffff1684116131135760405162461bcd60e51b815260206004820152601d60248201527f46756c66696c6c6d656e74206f6c646572207468616e20426561636f6e00000060448201526064016109d6565b604080518082018252601b83900b815263ffffffff95861660208083019182526000988952600690529190962095519051909416600160e01b026001600160e01b039094169390931790935550919050565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044015b60206040518083038186803b15801561322a57600080fd5b505afa15801561323e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffb9190615226565b6000928352602083815260408085206001600160a01b039490941685529290529120805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401613212565b6000838152602081815260408083206001600160a01b0386168452909152902054600160401b90046001600160c01b03168180156133bf575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff16155b1561344c5760008481526001602081815260408084206001600160a01b03881685528252808420338552909152909120805460ff191690911790558061340481615495565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b0384160217905591506127789050565b81158015613484575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff165b156127785760008481526001602090815260408083206001600160a01b038716845282528083203384529091529020805460ff19169055806134c5816154bc565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b038416021790559150509392505050565b600081516020146135615760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6000828060200190518101906135779190615243565b90507fffffffff8000000000000000000000000000000000000000000000000000000081128015906135c557507b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff8113155b612ffb5760405162461bcd60e51b815260206004820152601760248201527f56616c7565207479706563617374696e67206572726f7200000000000000000060448201526064016109d6565b6000838152602081815260408083206001600160a01b0386811680865291845282852054888652600185528386209286529184528285209086168552909252822054600160401b9091046001600160c01b03169060ff16156136f45760008581526001602090815260408083206001600160a01b03808916855290835281842090871684529091529020805460ff19169055806136ad816154bc565b6000878152602081815260408083206001600160a01b038a1684529091529020805467ffffffffffffffff16600160401b6001600160c01b03841602179055600193509150505b935093915050565b60008083601b0b83601b0b61371191906154df565b9050600080821361372a576137258261551e565b61372c565b815b905060008086601b0b1361374c5785601b0b6137479061551e565b613751565b85601b0b5b90508061375c575060015b8061376b6305f5e1008461553b565b611f979190615145565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401613212565b6000838152602081815260408083206001600160a01b038616845290915290205467ffffffffffffffff908116908216116132625760405162461bcd60e51b815260206004820152601a60248201527f446f6573206e6f7420657874656e642065787069726174696f6e00000000000060448201526064016109d6565b606061277883836040518060600160405280602781526020016155a460279139614156565b6000828152602081815260408083206001600160a01b038516845290915281208054600160401b90046001600160c01b0316151580613913575080544267ffffffffffffffff909116115b949350505050565b60006020821461396d5760405162461bcd60e51b815260206004820152601a60248201527f496e636f727265637420706172616d65746572206c656e67746800000000000060448201526064016109d6565b61277882840184614a3c565b805160098111156139cc5760405162461bcd60e51b815260206004820152601660248201527f417272617920746f6f206c6f6e6720746f20736f72740000000000000000000060448201526064016109d6565b6006811015613ada576004811015613a28578060031415613a12576139f48260006001614237565b613a018260016002614237565b613a0e8260006001614237565b5050565b8060021415613a0e57613a0e8260006001614237565b8060051415613aa657613a3e8260016002614237565b613a4b8260036004614237565b613a588260016003614237565b613a658260006002614237565b613a728260026004614237565b613a7f8260006003614237565b613a8c8260006001614237565b613a998260026003614237565b613a0e8260016002614237565b613ab38260006001614237565b613ac08260026003614237565b613acd8260016003614237565b613a998260006002614237565b6008811015613c58578060071415613bbc57613af98260016002614237565b613b068260036004614237565b613b138260056006614237565b613b208260006002614237565b613b2d8260046006614237565b613b3a8260036005614237565b613b478260026006614237565b613b548260016005614237565b613b618260006004614237565b613b6e8260026005614237565b613b7b8260006003614237565b613b888260026004614237565b613b958260016003614237565b613ba28260006001614237565b613baf8260026003614237565b613a0e8260046005614237565b613bc98260006001614237565b613bd68260026003614237565b613be38260046005614237565b613bf08260016003614237565b613bfd8260036005614237565b613c0a8260016003614237565b613c178260026004614237565b613c248260006002614237565b613c318260026004614237565b613c3e8260036004614237565b613c4b8260016002614237565b613a0e8260026003614237565b8060091415613d8c57613c6e8260016008614237565b613c7b8260026007614237565b613c888260036006614237565b613c958260046005614237565b613ca28260016004614237565b613caf8260056008614237565b613cbc8260006002614237565b613cc98260066007614237565b613cd68260026006614237565b613ce38260076008614237565b613cf08260006003614237565b613cfd8260046005614237565b613d0a8260006001614237565b613d178260036005614237565b613d248260066007614237565b613d318260026004614237565b613d3e8260016003614237565b613d4b8260056007614237565b613d588260046006614237565b613d658260016002614237565b613d728260036004614237565b613d7f8260056006614237565b613ba28260076008614237565b613d998260006007614237565b613da68260016006614237565b613db38260026005614237565b613dc08260036004614237565b613dcd8260006003614237565b613dda8260046007614237565b613de78260016002614237565b613df48260056006614237565b613e018260006001614237565b613e0e8260026003614237565b613e1b8260046005614237565b613e288260066007614237565b613e358260036005614237565b613e428260026004614237565b613e4f8260016002614237565b613e5c8260036004614237565b613e698260056006614237565b613e768260026003614237565b613e838260046005614237565b613a0e8260036004614237565b600061305883600060018651613ea69190615411565b8560006142e5565b8151600090819060018111613f055760405162461bcd60e51b815260206004820152601d60248201527f417272617920746f6f2073686f727420746f2073656c656374206b2b3100000060448201526064016109d6565b613f1e856000613f16600185615411565b8760016142e5565b92509250505b9250929050565b600080825160411415613f625760208301516040840151606085015160001a613f56878285856143e0565b94509450505050613f24565b825160401415613f8c5760208301516040840151613f818683836144cd565b935093505050613f24565b50600090506002613f24565b6000816004811115613fac57613fac61555a565b1415613fb55750565b6001816004811115613fc957613fc961555a565b14156140175760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016109d6565b600281600481111561402b5761402b61555a565b14156140795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016109d6565b600381600481111561408d5761408d61555a565b14156140e65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016109d6565b60048160048111156140fa576140fa61555a565b14156141535760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016109d6565b50565b6060833b6141cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016109d6565b600080856001600160a01b0316856040516141e79190615570565b600060405180830381855af49150503d8060008114614222576040519150601f19603f3d011682016040523d82523d6000602084013e614227565b606091505b5091509150611f978282866144fc565b828181518110614249576142496150d0565b6020026020010151838381518110614263576142636150d0565b602002602001015113156142e057828181518110614283576142836150d0565b602002602001015183838151811061429d5761429d6150d0565b60200260200101518484815181106142b7576142b76150d0565b602002602001018584815181106142d0576142d06150d0565b6020908102919091010191909152525b505050565b600080848614156142fb575082905060006143d6565b6000614308888888614535565b9050808510156143325761432a8888614322600185615411565b8860006142e5565b509250614356565b808511156143525761432a886143498360016150fc565b888860006142e5565b8092505b83156143d4576143678360016150fc565b915060006143768360016150fc565b90505b88518110156143d257888381518110614394576143946150d0565b60200260200101518982815181106143ae576143ae6150d0565b602002602001015112156143c0578092505b806143ca81615114565b915050614379565b505b505b9550959350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561441757506000905060036144c4565b8460ff16601b1415801561442f57508460ff16601c14155b1561444057506000905060046144c4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614494573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166144bd576000600192509250506144c4565b9150600090505b94509492505050565b6000806001600160ff1b03831660ff84901c601b016144ee878288856143e0565b935093505050935093915050565b6060831561450b575081612778565b82511561451b5782518084602001fd5b8160405162461bcd60e51b81526004016109d69190614ab1565b600081831415614546575081612778565b600084848151811061455a5761455a6150d0565b60209081029190910101519050836145738460016150fc565b92505b8061458081615114565b9150508551811080156145ab5750818682815181106145a1576145a16150d0565b6020026020010151125b614576575b826145ba8161558c565b935050818684815181106145d0576145d06150d0565b6020026020010151136145b05782811061465b578583815181106145f6576145f66150d0565b6020026020010151868681518110614610576146106150d0565b602002602001015187878151811061462a5761462a6150d0565b60200260200101888681518110614643576146436150d0565b60200260200101828152508281525050505050612778565b85838151811061466d5761466d6150d0565b6020026020010151868281518110614687576146876150d0565b60200260200101518783815181106146a1576146a16150d0565b602002602001018886815181106146ba576146ba6150d0565b602090810291909101019190915252614576565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561470d5761470d6146ce565b604052919050565b600067ffffffffffffffff82111561472f5761472f6146ce565b5060051b60200190565b600082601f83011261474a57600080fd5b8135602061475f61475a83614715565b6146e4565b82815260059290921b8401810191818101908684111561477e57600080fd5b8286015b848110156147995780358352918301918301614782565b509695505050505050565b6000602082840312156147b657600080fd5b813567ffffffffffffffff8111156147cd57600080fd5b61391384828501614739565b80356001600160a01b03811681146147f057600080fd5b919050565b6000806040838503121561480857600080fd5b614811836147d9565b946020939093013593505050565b60008083601f84011261483157600080fd5b50813567ffffffffffffffff81111561484957600080fd5b602083019150836020828501011115613f2457600080fd5b600080600080600080600060a0888a03121561487c57600080fd5b614885886147d9565b96506020880135955060408801359450606088013567ffffffffffffffff808211156148b057600080fd5b6148bc8b838c0161481f565b909650945060808a01359150808211156148d557600080fd5b506148e28a828b0161481f565b989b979a50959850939692959293505050565b60008060006060848603121561490a57600080fd5b8335925061491a602085016147d9565b9150604084013567ffffffffffffffff8116811461493757600080fd5b809150509250925092565b600080600080600080600080600060e08a8c03121561496057600080fd5b8935985061497060208b016147d9565b975061497e60408b016147d9565b965061498c60608b016147d9565b955060808a0135945060a08a013567ffffffffffffffff808211156149b057600080fd5b6149bc8d838e0161481f565b909650945060c08c01359150808211156149d557600080fd5b506149e28c828d0161481f565b915080935050809150509295985092959850929598565b801515811461415357600080fd5b600080600060608486031215614a1c57600080fd5b83359250614a2c602085016147d9565b91506040840135614937816149f9565b600060208284031215614a4e57600080fd5b5035919050565b60005b83811015614a70578181015183820152602001614a58565b83811115614a7f576000848401525b50505050565b60008151808452614a9d816020860160208601614a55565b601f01601f19169290920160200192915050565b6020815260006127786020830184614a85565b60008060008060608587031215614ada57600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614aff57600080fd5b614b0b8782880161481f565b95989497509550505050565b600082601f830112614b2857600080fd5b81356020614b3861475a83614715565b82815260059290921b84018101918181019086841115614b5757600080fd5b8286015b8481101561479957614b6c816147d9565b8352918301918301614b5b565b600082601f830112614b8a57600080fd5b813567ffffffffffffffff811115614ba457614ba46146ce565b614bb7601f8201601f19166020016146e4565b818152846020838601011115614bcc57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614bfa57600080fd5b81356020614c0a61475a83614715565b82815260059290921b84018101918181019086841115614c2957600080fd5b8286015b8481101561479957803567ffffffffffffffff811115614c4d5760008081fd5b614c5b8986838b0101614b79565b845250918301918301614c2d565b600080600080600060a08688031215614c8157600080fd5b853567ffffffffffffffff80821115614c9957600080fd5b614ca589838a01614b17565b96506020880135915080821115614cbb57600080fd5b614cc789838a01614739565b95506040880135915080821115614cdd57600080fd5b614ce989838a01614739565b94506060880135915080821115614cff57600080fd5b614d0b89838a01614be9565b93506080880135915080821115614d2157600080fd5b50614d2e88828901614be9565b9150509295509295909350565b600080600060608486031215614d5057600080fd5b83359250614d60602085016147d9565b9150614d6e604085016147d9565b90509250925092565b60008060408385031215614d8a57600080fd5b823567ffffffffffffffff811115614da157600080fd5b614dad85828601614739565b95602094909401359450505050565b60008060408385031215614dcf57600080fd5b50508035926020909101359150565b600080600060608486031215614df357600080fd5b614dfc846147d9565b925060208401359150614d6e604085016147d9565b60008060408385031215614e2457600080fd5b614e2d836147d9565b9150614e3b602084016147d9565b90509250929050565b60008060408385031215614e5757600080fd5b614e60836147d9565b91506020830135614e70816149f9565b809150509250929050565b60008060208385031215614e8e57600080fd5b823567ffffffffffffffff80821115614ea657600080fd5b818501915085601f830112614eba57600080fd5b813581811115614ec957600080fd5b8660208260051b8501011115614ede57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614f4557603f19888603018452614f33858351614a85565b94509285019290850190600101614f17565b5092979650505050505050565b600060208284031215614f6457600080fd5b612778826147d9565b60008060408385031215614f8057600080fd5b82359150614e3b602084016147d9565b600080600080600060608688031215614fa857600080fd5b85359450602086013567ffffffffffffffff80821115614fc757600080fd5b614fd389838a0161481f565b90965094506040880135915080821115614fec57600080fd5b50614ff98882890161481f565b969995985093965092949392505050565b6000806000806080858703121561502057600080fd5b615029856147d9565b93506020850135925061503e604086016147d9565b915061504c606086016147d9565b905092959194509250565b600080600080600060a0868803121561506f57600080fd5b615078866147d9565b945060208601359350604086013567ffffffffffffffff81111561509b57600080fd5b6150a788828901614b79565b9350506150b6606087016147d9565b91506150c4608087016147d9565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000821982111561510f5761510f6150e6565b500190565b6000600019821415615128576151286150e6565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826151545761515461512f565b500490565b84815283602082015281836040830137600091016040019081529392505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b8183823760009101908152919050565b600181811c908216806151d857607f821691505b60208210811415612fcf57634e487b7160e01b600052602260045260246000fd5b83815282602082015260008251615217816040850160208701614a55565b91909101604001949350505050565b60006020828403121561523857600080fd5b8151612778816149f9565b60006020828403121561525557600080fd5b5051919050565b6000808335601e1984360301811261527357600080fd5b83018035915067ffffffffffffffff82111561528e57600080fd5b602001915036819003821315613f2457600080fd5b6020808252825182820181905260009190848201906040850190845b818110156152db578351835292840192918401916001016152bf565b50909695505050505050565b60006101208a83526001600160a01b03808b16602085015289604085015281606085015260008285015261014091508160808501526153288285018a614a85565b97811660a085015295861660c084015250509190921660e08201527fffffffff0000000000000000000000000000000000000000000000000000000090911661010090910152949350505050565b60006101006001600160a01b03808b16845289602085015281604085015260008285015261012091508160608501526153b18285018a614a85565b978116608085015295861660a084015250509190921660c08201527fffffffff0000000000000000000000000000000000000000000000000000000090911660e0909101529392505050565b60008261540c5761540c61512f565b500690565b600082821015615423576154236150e6565b500390565b6000808212826001600160ff1b0303841381151615615449576154496150e6565b82600160ff1b038412811615615461576154616150e6565b50500190565b6000826154765761547661512f565b600160ff1b821460001984141615615490576154906150e6565b500590565b60006001600160c01b03808316818114156154b2576154b26150e6565b6001019392505050565b60006001600160c01b038216806154d5576154d56150e6565b6000190192915050565b600080831283600160ff1b018312811516156154fd576154fd6150e6565b836001600160ff1b03018313811615615518576155186150e6565b50500390565b6000600160ff1b821415615534576155346150e6565b5060000390565b6000816000190483118215151615615555576155556150e6565b500290565b634e487b7160e01b600052602160045260246000fd5b60008251615582818460208701614a55565b9190910192915050565b60008161559b5761559b6150e6565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220aaf5f98df755a777bf2ab38bf5b0d2b2f9740cb9c1a1c935964c5d11caec024864736f6c6343000809003300000000000000000000000092e5125adf385d86bedb950793526106143b6df100000000000000000000000000000000000000000000000000000000000000800000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a4600000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b0000000000000000000000000000000000000000000000000000000000000010446170695365727665722061646d696e00000000000000000000000000000000

Deployed ByteCode

0x608060405234801561001057600080fd5b506004361061034a5760003560e01c80638e85afe0116101bd578063bcb5daec116100f9578063e18de857116100a2578063f60e45d31161007c578063f60e45d3146108da578063f8b2cb4f1461091b578063fc3c1f8b14610936578063fce90be81461094957600080fd5b8063e18de8571461088d578063e406f3b2146108b4578063e705882e146108c757600080fd5b8063d55a42dd116100d3578063d55a42dd1461082b578063d7c0b87914610867578063dc96acc81461087a57600080fd5b8063bcb5daec1461078c578063c4f95dd51461079f578063d39c8968146107b257600080fd5b80639e14caa811610166578063a6b1b9a211610140578063a6b1b9a21461070f578063ac9650d814610722578063b605bbcd14610742578063b76f3c151461076557600080fd5b80639e14caa8146106c9578063a026658f146106dc578063a5fc076f146106fc57600080fd5b806391eed0851161019757806391eed085146106755780639421240514610688578063977cae011461069b57600080fd5b80638e85afe0146106185780638fca9ab91461063b5780638fe47dd81461064e57600080fd5b80634c8f1d8d1161028c5780636db798f9116102355780637512449b1161020f5780637512449b146105b1578063776bcbd2146105d8578063796b89b9146105eb5780637c706738146105f157600080fd5b80636db798f9146105575780636ed93dd01461059357806370c927ca1461059e57600080fd5b80635bc8a5c8116102665780635bc8a5c8146104f557806365101054146105085780636bd2bdd01461051b57600080fd5b80634c8f1d8d146104b45780634dcc19fe146104c957806358c55edd146104cf57600080fd5b8063206b48f4116102f95780633408e470116102d35780633408e4701461044f5780633af1dbfd1461045557806342cbb15c14610487578063481c6a751461048d57600080fd5b8063206b48f4146104165780632ce010e3146104295780633376e7c21461043c57600080fd5b80631a0a0b3e1161032a5780631a0a0b3e146103af5780631ce9ae07146103c45780631d36cf5c1461040357600080fd5b80629f2f3c1461034f578062aae33f1461038957806304dd3cee1461039c575b600080fd5b6103767f104c9d4f4e43e7ba83944d6212af5b9af9a93525e592ed31e398331520de830181565b6040519081526020015b60405180910390f35b6103766103973660046147a4565b610985565b6103766103aa3660046147f5565b610be1565b6103c26103bd366004614861565b610cca565b005b6103eb7f00000000000000000000000092e5125adf385d86bedb950793526106143b6df181565b6040516001600160a01b039091168152602001610380565b6103c26104113660046148f5565b610e46565b6103c2610424366004614942565b610fb1565b6103c2610437366004614942565b6112ad565b6103c261044a366004614a07565b61132c565b46610376565b610468610463366004614a3c565b611472565b60408051601b9390930b835263ffffffff909116602083015201610380565b43610376565b6103eb7f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a4681565b6104bc611574565b6040516103809190614ab1565b48610376565b6104e26104dd366004614a3c565b611602565b604051601b9190910b8152602001610380565b6103c2610503366004614ac4565b6116c2565b610376610516366004614c69565b6117f6565b6104bc6040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e20736574746572000000000081525081565b6104bc6040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e64657200000081525081565b6103766305f5e10081565b6104e26105ac366004614a3c565b611d3b565b6103767f492677bd30a08301f65b537cfc8252cb14ea0a0615e504f9101eeffd5f9ab89181565b6103c26105e6366004614d3b565b611e35565b42610376565b6103767f076e9207298350b8044dc8d4d473ffd3008f3aa7c89c9237bf72570348c51fcc81565b61062b610626366004614d77565b611efe565b6040519015158152602001610380565b610376610649366004614a3c565b611fa1565b6103767f01f3b33ad3d1e072f204c5a2fc7623570a125883679903f9c72c2b4b9469aac681565b6103c2610683366004614dbc565b611fe6565b610376610696366004614dde565b6121f6565b61062b6106a9366004614e11565b600460209081526000928352604080842090915290825290205460ff1681565b6103c26106d7366004614e44565b6123e0565b6103766106ea366004614a3c565b60056020526000908152604090205481565b61046861070a366004614a3c565b61249b565b6103c261071d3660046148f5565b612511565b610735610730366004614e7b565b612643565b6040516103809190614ef0565b61062b610750366004614f52565b60036020526000908152604090205460ff1681565b6103eb7f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b81565b61062b61079a366004614f6d565b612738565b6103766107ad3660046147a4565b61277f565b6108036107c0366004614f6d565b6000918252602082815260408084206001600160a01b0393909316845291905290205467ffffffffffffffff811691600160401b9091046001600160c01b031690565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610380565b6104bc6040518060400160405280601681526020017f496e646566696e6974652077686974656c69737465720000000000000000000081525081565b61062b610875366004614f90565b6127af565b61062b610888366004614f90565b6128b4565b6103767f9693655d166f407001527e484736ea4c5dca85db0d0a32bb1dadd4b5310d58a281565b6103c26108c2366004614f52565b6129ea565b6103766108d536600461500a565b612aae565b61062b6108e8366004614d3b565b60009283526001602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b610376610929366004614f52565b6001600160a01b03163190565b610376610944366004615057565b612cb9565b6104bc6040518060400160405280601081526020017f64415049206e616d65207365747465720000000000000000000000000000000081525081565b8051600090600181116109df5760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064015b60405180910390fd5b60008167ffffffffffffffff8111156109fa576109fa6146ce565b604051908082528060200260200182016040528015610a23578160200160208202803683370190505b5090506000805b83811015610abb57600060066000888481518110610a4a57610a4a6150d0565b602090810291909101810151825281019190915260400160002080548551919250601b0b90859084908110610a8157610a816150d0565b60209081029190910101528054610aa590600160e01b900463ffffffff16846150fc565b9250508080610ab390615114565b915050610a2a565b506000610ac88483615145565b9050610ad38661277f565b60008181526006602052604090205490955063ffffffff600160e01b90910481169082161015610b455760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000610b5084612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008e81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925087917fb7712be6248d021e8c56ac9613c09491354a4d0f4ad0b7db1a664b35be4b2349910160405180910390a25050505050919050565b60006001600160a01b038316610c395760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f00000000000000000000000060448201526064016109d6565b81610c865760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f0000000000000000000000000000000060448201526064016109d6565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260540160405160208183030381529060405280519060200120905092915050565b84610cd481612fd5565b610d165760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b876001600160a01b0316610d9584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508c908c908c908c90602001615159565b60405160208183030381529060405280519060200120613001565b9061303c565b6001600160a01b031614610de05760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b6000610dec8989610be1565b90506000610dfc82898989613060565b60408051828152602081018b905291925083917f403078446dab7471f481ca4bffac706bd84a6dba118980d267676c096f2ba924910160405180910390a250505050505050505050565b610e4f33613165565b610e9b5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f74207365742065787069726174696f6e000000000000000000000060448201526064016109d6565b82610eda5760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b038216610f245760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000838152602081815260408083206001600160a01b03861684529091529020805467ffffffffffffffff191667ffffffffffffffff831617905560405167ffffffffffffffff8216815233906001600160a01b0384169085907fd19e89b7d547ccf349211588a9a1d29461e2ce984b1b1cdbe7150976528b86f1906020015b60405180910390a4505050565b84610fbb81612fd5565b610ffd5760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6040516bffffffffffffffffffffffff1960608b811b821660208401528a811b8216603484015289901b166048820152605c0160408051601f19818403018152918152815160209283012060008d81526008909352912054146110a25760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b876001600160a01b0316896001600160a01b0316141561118b57886001600160a01b031661113b84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c903390602001928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b6001600160a01b0316146111865760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611236565b886001600160a01b03166111eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c9033908d908d9060200161517a565b6001600160a01b0316146112365760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b60008a8152600560205260408120549061125282898989613060565b604080518e8152601b83900b602082015263ffffffff8b1681830152905191925083917f2b85d71813342fd0091d4573a5f78e244c2133755e470943c33a6dbf3cf9c15d9181900360600190a2505050505050505050505050565b6112bc610397848601866147a4565b84846040516112cc9291906151b4565b6040518091039020146113215760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b505050505050505050565b611335336132a5565b6113815760405162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742073657420696e646566696e697465207374617475730000000060448201526064016109d6565b826113c05760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b03821661140a5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000611417848484613356565b6040805184151581526001600160c01b038316602082015291925033916001600160a01b0386169187917f2fa93828cf3f001b9a9f0a7365db04ea068b9db7bcb7a38d289f0cf8aa9ce374910160405180910390a450505050565b60008060008360405160200161148a91815260200190565b6040516020818303038152906040528051906020012090506114ac8133612738565b6114ed5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b600081815260096020526040902054806115495760405162461bcd60e51b815260206004820152601160248201527f64415049206e616d65206e6f742073657400000000000000000000000000000060448201526064016109d6565b600090815260066020526040902054601b81900b95600160e01b90910463ffffffff16945092505050565b60028054611581906151c4565b80601f01602080910402602001604051908101604052809291908181526020018280546115ad906151c4565b80156115fa5780601f106115cf576101008083540402835291602001916115fa565b820191906000526020600020905b8154815290600101906020018083116115dd57829003601f168201915b505050505081565b600061160e8233612738565b61164f5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b60008281526006602052604090208054600160e01b900463ffffffff166116b85760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b92915050565b336001600160a01b037f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b161461173a5760405162461bcd60e51b815260206004820152601b60248201527f53656e646572206e6f74204169726e6f64652070726f746f636f6c000000000060448201526064016109d6565b8261174481612fd5565b6117865760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6000858152600760205260408120805490829055906117a782878787613060565b604080518981526020810183905290810188905290915082907f7c88b543c4cda65df046e87397c3152c27098419cb22a90a5e37db30ef621b269060600160405180910390a250505050505050565b84518451600091908114801561180c5750845181145b80156118185750835181145b80156118245750825181145b6118705760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d617463680000000000000060448201526064016109d6565b600181116118c05760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064016109d6565b60008167ffffffffffffffff8111156118db576118db6146ce565b604051908082528060200260200182016040528015611904578160200160208202803683370190505b50905060008267ffffffffffffffff811115611922576119226146ce565b60405190808252806020026020018201604052801561194b578160200160208202803683370190505b5090506000805b84811015611c105786818151811061196c5761196c6150d0565b602002602001015151600014611b555760008b8281518110611990576119906150d0565b6020026020010151905060008a83815181106119ae576119ae6150d0565b602002602001015190506119c181612fd5565b611a035760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b816001600160a01b0316611a748a8581518110611a2257611a226150d0565b6020026020010151610d8f8f8781518110611a3f57611a3f6150d0565b6020026020010151858f8981518110611a5a57611a5a6150d0565b6020026020010151604051602001610d74939291906151f9565b6001600160a01b031614611abf5760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611ae18a8481518110611ad457611ad46150d0565b602002602001015161350e565b601b0b858481518110611af657611af66150d0565b6020908102919091010152611b0b81856150fc565b9350611b30828d8581518110611b2357611b236150d0565b6020026020010151610be1565b868481518110611b4257611b426150d0565b6020026020010181815250505050611bfe565b6000611b868c8381518110611b6c57611b6c6150d0565b60200260200101518c8481518110611b2357611b236150d0565b6000818152600660205260409020805486519293509091601b9190910b90869085908110611bb657611bb66150d0565b60209081029190910101528054611bda90600160e01b900463ffffffff16856150fc565b935081868481518110611bef57611bef6150d0565b60200260200101818152505050505b80611c0881615114565b915050611952565b50611c1a8361277f565b94506000611c288583615145565b60008781526006602052604090205490915063ffffffff600160e01b90910481169082161015611c9a5760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000611ca584612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008f81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925088917fb34747dc40d9c985b4857c2955fec7a8f34d88bc06da72f43319795758800407910160405180910390a250505050505095945050505050565b60008082604051602001611d5191815260200190565b604051602081830303815290604052805190602001209050611d738133612738565b611db45760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b6000818152600960209081526040808320548352600690915290208054600160e01b900463ffffffff16611e2a5760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b9392505050565b611e3e816132a5565b15611e8b5760405162461bcd60e51b815260206004820181905260248201527f7365747465722063616e2073657420696e646566696e6974652073746174757360448201526064016109d6565b600080611e99858585613611565b915091508115611ef757604080513381526001600160c01b03831660208201526001600160a01b03808616929087169188917f29c394c1d92801cab93215bf9cd50ae38d23341be6540f27d80ee2bc8a541237910160405180910390a45b5050505050565b600080611f0a8461277f565b600081815260066020908152604091829020825180840190935254601b81900b8352600160e01b900463ffffffff1690820152909150611f4985610985565b506000828152600660205260409020815181548691611f6a91601b0b6136fc565b101580611f975750602082015163ffffffff16158015611f9757508054600160e01b900463ffffffff1615155b9695505050505050565b60006009600083604051602001611fba91815260200190565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b816120335760405162461bcd60e51b815260206004820152600e60248201527f64415049206e616d65207a65726f00000000000000000000000000000000000060448201526064016109d6565b336001600160a01b037f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a461614806121225750604051632474521560e21b81527f492677bd30a08301f65b537cfc8252cb14ea0a0615e504f9101eeffd5f9ab89160048201523360248201527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df16001600160a01b0316906391d148549060440160206040518083038186803b1580156120ea57600080fd5b505afa1580156120fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121229190615226565b61216e5760405162461bcd60e51b815260206004820152601b60248201527f53656e6465722063616e6e6f74207365742064415049206e616d65000000000060448201526064016109d6565b80600960008460405160200161218691815260200190565b60405160208183030381529060405280519060200120815260200190815260200160002081905550336001600160a01b0316827ff3a9aac9b6ac0f842cb5d9b3491cd5fc1b6a6778d97fd9529f587339865294f5836040516121ea91815260200190565b60405180910390a35050565b6000816001600160a01b03811633148061223357506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b61227f5760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b600061228b8686610be1565b6040517feebecf690000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301526024820188905260a06044830152600060a48301528681166064830152630b7914b960e31b60848301529192507f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b9091169063eebecf699060c401602060405180830381600087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236e9190615243565b60008181526007602090815260409182902084905581518381526001600160a01b038a811692820192909252918201889052919450339186169083907fc0e8c8735457dd46668d6c01832656e0803b41b1e61ba12354a24f11e114eab39060600160405180910390a450509392505050565b6001600160a01b0382166124365760405162461bcd60e51b815260206004820152601560248201527f55706461746520726571756573746572207a65726f000000000000000000000060448201526064016109d6565b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fba239f737a4075f5efb0d649e16cd7d2b1978690876b079dab642f077050b27991016121ea565b6000806124a88333612738565b6124e95760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b5050600090815260066020526040902054601b81900b91600160e01b90910463ffffffff1690565b61251a33613775565b6125665760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420657874656e642065787069726174696f6e000000000000000060448201526064016109d6565b826125a55760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b0382166125ef5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6125fa838383613826565b60405167ffffffffffffffff8216815233906001600160a01b0384169085907fa9e0c89b898eb7a904617915dc5b5510d539c899810042e9248569b54b9cc2ed90602001610fa4565b60608167ffffffffffffffff81111561265e5761265e6146ce565b60405190808252806020026020018201604052801561269157816020015b606081526020019060019003908161267c5790505b50905060005b8281101561273157612701308585848181106126b5576126b56150d0565b90506020028101906126c7919061525c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506138a392505050565b828281518110612713576127136150d0565b6020026020010181905250808061272990615114565b915050612697565b5092915050565b60006001600160a01b0382161580612755575061275583836138c8565b8061277857506001600160a01b03821660009081526003602052604090205460ff165b9392505050565b60008160405160200161279291906152a3565b604051602081830303815290604052805190602001209050919050565b600033156127ff5760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600061280d858701876147a4565b9050858560405161281f9291906151b4565b60405180910390208160405160200161283891906152a3565b604051602081830303815290604052805190602001201461289b5760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6128a981610626868661391b565b979650505050505050565b600033156129045760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600086815260056020526040902054806129605760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b6000818152600660205260409020612978858561391b565b8154604080516020601f8b018190048102820181019092528981526129c592601b0b916129c091908c908c908190840183828082843760009201919091525061350e92505050565b6136fc565b1015806129de57508054600160e01b900463ffffffff16155b98975050505050505050565b336001600160a01b037f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a461614612a625760405162461bcd60e51b815260206004820152601260248201527f53656e646572206e6f74206d616e61676572000000000000000000000000000060448201526064016109d6565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f6d71fde11d6c56eaf0154d464b51df0b26dcdac118703056ea3da805fc9e1cd79190a250565b6000816001600160a01b038116331480612aeb57506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b612b375760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b6000612b438787610be1565b6040517fa7e0c85e0000000000000000000000000000000000000000000000000000000081526001600160a01b0389811660048301526024820189905260c06044830152600060c483015287811660648301528681166084830152630b7914b960e31b60a48301529192507f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b9091169063a7e0c85e9060e401602060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2e9190615243565b600081815260076020526040908190208390555190935033906001600160a01b0386169083907faeef85c84fba3ef952ca2738d951d30f5093595cb5eb9830764f5b49a39442a390612ca79088908d908c908e909384526001600160a01b03928316602085015291166040830152606082015260800190565b60405180910390a45050949350505050565b60006001600160a01b038316612d115760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b6001600160a01b038216612d675760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b604051612d8d90469088908890889088908890309063081ad23d60e21b906020016152e7565b60408051601f1981840301815282825280516020918201206bffffffffffffffffffffffff1960608b811b82168487015288811b8216603487015287901b1660488501528251808503603c018152605c909401835283519382019390932060008481526008909252919020559050612e058686610be1565b60008281526005602052604090819020919091555181907f82b139c5e690a3a4a9c2f68133d2ab1f7724bf8a69ad3fe1cd9f5a4923cf41b290612e5c9089908990899089908990309063081ad23d60e21b90615376565b60405180910390a295945050505050565b805160009060098111612f2d57612e8383613979565b612e8e6002826153fd565b60011415612ec25782612ea2600283615145565b81518110612eb257612eb26150d0565b6020026020010151915050919050565b600283612ecf8284615145565b81518110612edf57612edf6150d0565b6020026020010151846001600285612ef79190615145565b612f019190615411565b81518110612f1157612f116150d0565b6020026020010151612f239190615428565b6127789190615467565b612f386002826153fd565b60011415612f555782612ea281612f50600285615145565b613e90565b600080612f78856001612f69600287615145565b612f739190615411565b613eae565b915091506002858281518110612f9057612f906150d0565b6020026020010151868481518110612faa57612faa6150d0565b6020026020010151612fbc9190615428565b612fc69190615467565b95945050505050565b50919050565b600042612fe483610e106150fc565b118015612ffb5750612ff8426103846150fc565b82105b92915050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01612792565b600080600061304b8585613f2b565b9150915061305881613f98565b509392505050565b60006130a183838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061350e92505050565b600086815260066020526040902054601b9190910b9150600160e01b900463ffffffff1684116131135760405162461bcd60e51b815260206004820152601d60248201527f46756c66696c6c6d656e74206f6c646572207468616e20426561636f6e00000060448201526064016109d6565b604080518082018252601b83900b815263ffffffff95861660208083019182526000988952600690529190962095519051909416600160e01b026001600160e01b039094169390931790935550919050565b6000816001600160a01b03167f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a466001600160a01b03161480612ffb5750604051632474521560e21b81527f01f3b33ad3d1e072f204c5a2fc7623570a125883679903f9c72c2b4b9469aac660048201526001600160a01b0383811660248301527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df116906391d14854906044015b60206040518083038186803b15801561322a57600080fd5b505afa15801561323e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffb9190615226565b6000928352602083815260408085206001600160a01b039490941685529290529120805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b6000816001600160a01b03167f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a466001600160a01b03161480612ffb5750604051632474521560e21b81527f9693655d166f407001527e484736ea4c5dca85db0d0a32bb1dadd4b5310d58a260048201526001600160a01b0383811660248301527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df116906391d1485490604401613212565b6000838152602081815260408083206001600160a01b0386168452909152902054600160401b90046001600160c01b03168180156133bf575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff16155b1561344c5760008481526001602081815260408084206001600160a01b03881685528252808420338552909152909120805460ff191690911790558061340481615495565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b0384160217905591506127789050565b81158015613484575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff165b156127785760008481526001602090815260408083206001600160a01b038716845282528083203384529091529020805460ff19169055806134c5816154bc565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b038416021790559150509392505050565b600081516020146135615760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6000828060200190518101906135779190615243565b90507fffffffff8000000000000000000000000000000000000000000000000000000081128015906135c557507b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff8113155b612ffb5760405162461bcd60e51b815260206004820152601760248201527f56616c7565207479706563617374696e67206572726f7200000000000000000060448201526064016109d6565b6000838152602081815260408083206001600160a01b0386811680865291845282852054888652600185528386209286529184528285209086168552909252822054600160401b9091046001600160c01b03169060ff16156136f45760008581526001602090815260408083206001600160a01b03808916855290835281842090871684529091529020805460ff19169055806136ad816154bc565b6000878152602081815260408083206001600160a01b038a1684529091529020805467ffffffffffffffff16600160401b6001600160c01b03841602179055600193509150505b935093915050565b60008083601b0b83601b0b61371191906154df565b9050600080821361372a576137258261551e565b61372c565b815b905060008086601b0b1361374c5785601b0b6137479061551e565b613751565b85601b0b5b90508061375c575060015b8061376b6305f5e1008461553b565b611f979190615145565b6000816001600160a01b03167f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a466001600160a01b03161480612ffb5750604051632474521560e21b81527f076e9207298350b8044dc8d4d473ffd3008f3aa7c89c9237bf72570348c51fcc60048201526001600160a01b0383811660248301527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df116906391d1485490604401613212565b6000838152602081815260408083206001600160a01b038616845290915290205467ffffffffffffffff908116908216116132625760405162461bcd60e51b815260206004820152601a60248201527f446f6573206e6f7420657874656e642065787069726174696f6e00000000000060448201526064016109d6565b606061277883836040518060600160405280602781526020016155a460279139614156565b6000828152602081815260408083206001600160a01b038516845290915281208054600160401b90046001600160c01b0316151580613913575080544267ffffffffffffffff909116115b949350505050565b60006020821461396d5760405162461bcd60e51b815260206004820152601a60248201527f496e636f727265637420706172616d65746572206c656e67746800000000000060448201526064016109d6565b61277882840184614a3c565b805160098111156139cc5760405162461bcd60e51b815260206004820152601660248201527f417272617920746f6f206c6f6e6720746f20736f72740000000000000000000060448201526064016109d6565b6006811015613ada576004811015613a28578060031415613a12576139f48260006001614237565b613a018260016002614237565b613a0e8260006001614237565b5050565b8060021415613a0e57613a0e8260006001614237565b8060051415613aa657613a3e8260016002614237565b613a4b8260036004614237565b613a588260016003614237565b613a658260006002614237565b613a728260026004614237565b613a7f8260006003614237565b613a8c8260006001614237565b613a998260026003614237565b613a0e8260016002614237565b613ab38260006001614237565b613ac08260026003614237565b613acd8260016003614237565b613a998260006002614237565b6008811015613c58578060071415613bbc57613af98260016002614237565b613b068260036004614237565b613b138260056006614237565b613b208260006002614237565b613b2d8260046006614237565b613b3a8260036005614237565b613b478260026006614237565b613b548260016005614237565b613b618260006004614237565b613b6e8260026005614237565b613b7b8260006003614237565b613b888260026004614237565b613b958260016003614237565b613ba28260006001614237565b613baf8260026003614237565b613a0e8260046005614237565b613bc98260006001614237565b613bd68260026003614237565b613be38260046005614237565b613bf08260016003614237565b613bfd8260036005614237565b613c0a8260016003614237565b613c178260026004614237565b613c248260006002614237565b613c318260026004614237565b613c3e8260036004614237565b613c4b8260016002614237565b613a0e8260026003614237565b8060091415613d8c57613c6e8260016008614237565b613c7b8260026007614237565b613c888260036006614237565b613c958260046005614237565b613ca28260016004614237565b613caf8260056008614237565b613cbc8260006002614237565b613cc98260066007614237565b613cd68260026006614237565b613ce38260076008614237565b613cf08260006003614237565b613cfd8260046005614237565b613d0a8260006001614237565b613d178260036005614237565b613d248260066007614237565b613d318260026004614237565b613d3e8260016003614237565b613d4b8260056007614237565b613d588260046006614237565b613d658260016002614237565b613d728260036004614237565b613d7f8260056006614237565b613ba28260076008614237565b613d998260006007614237565b613da68260016006614237565b613db38260026005614237565b613dc08260036004614237565b613dcd8260006003614237565b613dda8260046007614237565b613de78260016002614237565b613df48260056006614237565b613e018260006001614237565b613e0e8260026003614237565b613e1b8260046005614237565b613e288260066007614237565b613e358260036005614237565b613e428260026004614237565b613e4f8260016002614237565b613e5c8260036004614237565b613e698260056006614237565b613e768260026003614237565b613e838260046005614237565b613a0e8260036004614237565b600061305883600060018651613ea69190615411565b8560006142e5565b8151600090819060018111613f055760405162461bcd60e51b815260206004820152601d60248201527f417272617920746f6f2073686f727420746f2073656c656374206b2b3100000060448201526064016109d6565b613f1e856000613f16600185615411565b8760016142e5565b92509250505b9250929050565b600080825160411415613f625760208301516040840151606085015160001a613f56878285856143e0565b94509450505050613f24565b825160401415613f8c5760208301516040840151613f818683836144cd565b935093505050613f24565b50600090506002613f24565b6000816004811115613fac57613fac61555a565b1415613fb55750565b6001816004811115613fc957613fc961555a565b14156140175760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016109d6565b600281600481111561402b5761402b61555a565b14156140795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016109d6565b600381600481111561408d5761408d61555a565b14156140e65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016109d6565b60048160048111156140fa576140fa61555a565b14156141535760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016109d6565b50565b6060833b6141cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016109d6565b600080856001600160a01b0316856040516141e79190615570565b600060405180830381855af49150503d8060008114614222576040519150601f19603f3d011682016040523d82523d6000602084013e614227565b606091505b5091509150611f978282866144fc565b828181518110614249576142496150d0565b6020026020010151838381518110614263576142636150d0565b602002602001015113156142e057828181518110614283576142836150d0565b602002602001015183838151811061429d5761429d6150d0565b60200260200101518484815181106142b7576142b76150d0565b602002602001018584815181106142d0576142d06150d0565b6020908102919091010191909152525b505050565b600080848614156142fb575082905060006143d6565b6000614308888888614535565b9050808510156143325761432a8888614322600185615411565b8860006142e5565b509250614356565b808511156143525761432a886143498360016150fc565b888860006142e5565b8092505b83156143d4576143678360016150fc565b915060006143768360016150fc565b90505b88518110156143d257888381518110614394576143946150d0565b60200260200101518982815181106143ae576143ae6150d0565b602002602001015112156143c0578092505b806143ca81615114565b915050614379565b505b505b9550959350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561441757506000905060036144c4565b8460ff16601b1415801561442f57508460ff16601c14155b1561444057506000905060046144c4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614494573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166144bd576000600192509250506144c4565b9150600090505b94509492505050565b6000806001600160ff1b03831660ff84901c601b016144ee878288856143e0565b935093505050935093915050565b6060831561450b575081612778565b82511561451b5782518084602001fd5b8160405162461bcd60e51b81526004016109d69190614ab1565b600081831415614546575081612778565b600084848151811061455a5761455a6150d0565b60209081029190910101519050836145738460016150fc565b92505b8061458081615114565b9150508551811080156145ab5750818682815181106145a1576145a16150d0565b6020026020010151125b614576575b826145ba8161558c565b935050818684815181106145d0576145d06150d0565b6020026020010151136145b05782811061465b578583815181106145f6576145f66150d0565b6020026020010151868681518110614610576146106150d0565b602002602001015187878151811061462a5761462a6150d0565b60200260200101888681518110614643576146436150d0565b60200260200101828152508281525050505050612778565b85838151811061466d5761466d6150d0565b6020026020010151868281518110614687576146876150d0565b60200260200101518783815181106146a1576146a16150d0565b602002602001018886815181106146ba576146ba6150d0565b602090810291909101019190915252614576565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561470d5761470d6146ce565b604052919050565b600067ffffffffffffffff82111561472f5761472f6146ce565b5060051b60200190565b600082601f83011261474a57600080fd5b8135602061475f61475a83614715565b6146e4565b82815260059290921b8401810191818101908684111561477e57600080fd5b8286015b848110156147995780358352918301918301614782565b509695505050505050565b6000602082840312156147b657600080fd5b813567ffffffffffffffff8111156147cd57600080fd5b61391384828501614739565b80356001600160a01b03811681146147f057600080fd5b919050565b6000806040838503121561480857600080fd5b614811836147d9565b946020939093013593505050565b60008083601f84011261483157600080fd5b50813567ffffffffffffffff81111561484957600080fd5b602083019150836020828501011115613f2457600080fd5b600080600080600080600060a0888a03121561487c57600080fd5b614885886147d9565b96506020880135955060408801359450606088013567ffffffffffffffff808211156148b057600080fd5b6148bc8b838c0161481f565b909650945060808a01359150808211156148d557600080fd5b506148e28a828b0161481f565b989b979a50959850939692959293505050565b60008060006060848603121561490a57600080fd5b8335925061491a602085016147d9565b9150604084013567ffffffffffffffff8116811461493757600080fd5b809150509250925092565b600080600080600080600080600060e08a8c03121561496057600080fd5b8935985061497060208b016147d9565b975061497e60408b016147d9565b965061498c60608b016147d9565b955060808a0135945060a08a013567ffffffffffffffff808211156149b057600080fd5b6149bc8d838e0161481f565b909650945060c08c01359150808211156149d557600080fd5b506149e28c828d0161481f565b915080935050809150509295985092959850929598565b801515811461415357600080fd5b600080600060608486031215614a1c57600080fd5b83359250614a2c602085016147d9565b91506040840135614937816149f9565b600060208284031215614a4e57600080fd5b5035919050565b60005b83811015614a70578181015183820152602001614a58565b83811115614a7f576000848401525b50505050565b60008151808452614a9d816020860160208601614a55565b601f01601f19169290920160200192915050565b6020815260006127786020830184614a85565b60008060008060608587031215614ada57600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614aff57600080fd5b614b0b8782880161481f565b95989497509550505050565b600082601f830112614b2857600080fd5b81356020614b3861475a83614715565b82815260059290921b84018101918181019086841115614b5757600080fd5b8286015b8481101561479957614b6c816147d9565b8352918301918301614b5b565b600082601f830112614b8a57600080fd5b813567ffffffffffffffff811115614ba457614ba46146ce565b614bb7601f8201601f19166020016146e4565b818152846020838601011115614bcc57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614bfa57600080fd5b81356020614c0a61475a83614715565b82815260059290921b84018101918181019086841115614c2957600080fd5b8286015b8481101561479957803567ffffffffffffffff811115614c4d5760008081fd5b614c5b8986838b0101614b79565b845250918301918301614c2d565b600080600080600060a08688031215614c8157600080fd5b853567ffffffffffffffff80821115614c9957600080fd5b614ca589838a01614b17565b96506020880135915080821115614cbb57600080fd5b614cc789838a01614739565b95506040880135915080821115614cdd57600080fd5b614ce989838a01614739565b94506060880135915080821115614cff57600080fd5b614d0b89838a01614be9565b93506080880135915080821115614d2157600080fd5b50614d2e88828901614be9565b9150509295509295909350565b600080600060608486031215614d5057600080fd5b83359250614d60602085016147d9565b9150614d6e604085016147d9565b90509250925092565b60008060408385031215614d8a57600080fd5b823567ffffffffffffffff811115614da157600080fd5b614dad85828601614739565b95602094909401359450505050565b60008060408385031215614dcf57600080fd5b50508035926020909101359150565b600080600060608486031215614df357600080fd5b614dfc846147d9565b925060208401359150614d6e604085016147d9565b60008060408385031215614e2457600080fd5b614e2d836147d9565b9150614e3b602084016147d9565b90509250929050565b60008060408385031215614e5757600080fd5b614e60836147d9565b91506020830135614e70816149f9565b809150509250929050565b60008060208385031215614e8e57600080fd5b823567ffffffffffffffff80821115614ea657600080fd5b818501915085601f830112614eba57600080fd5b813581811115614ec957600080fd5b8660208260051b8501011115614ede57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614f4557603f19888603018452614f33858351614a85565b94509285019290850190600101614f17565b5092979650505050505050565b600060208284031215614f6457600080fd5b612778826147d9565b60008060408385031215614f8057600080fd5b82359150614e3b602084016147d9565b600080600080600060608688031215614fa857600080fd5b85359450602086013567ffffffffffffffff80821115614fc757600080fd5b614fd389838a0161481f565b90965094506040880135915080821115614fec57600080fd5b50614ff98882890161481f565b969995985093965092949392505050565b6000806000806080858703121561502057600080fd5b615029856147d9565b93506020850135925061503e604086016147d9565b915061504c606086016147d9565b905092959194509250565b600080600080600060a0868803121561506f57600080fd5b615078866147d9565b945060208601359350604086013567ffffffffffffffff81111561509b57600080fd5b6150a788828901614b79565b9350506150b6606087016147d9565b91506150c4608087016147d9565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000821982111561510f5761510f6150e6565b500190565b6000600019821415615128576151286150e6565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826151545761515461512f565b500490565b84815283602082015281836040830137600091016040019081529392505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b8183823760009101908152919050565b600181811c908216806151d857607f821691505b60208210811415612fcf57634e487b7160e01b600052602260045260246000fd5b83815282602082015260008251615217816040850160208701614a55565b91909101604001949350505050565b60006020828403121561523857600080fd5b8151612778816149f9565b60006020828403121561525557600080fd5b5051919050565b6000808335601e1984360301811261527357600080fd5b83018035915067ffffffffffffffff82111561528e57600080fd5b602001915036819003821315613f2457600080fd5b6020808252825182820181905260009190848201906040850190845b818110156152db578351835292840192918401916001016152bf565b50909695505050505050565b60006101208a83526001600160a01b03808b16602085015289604085015281606085015260008285015261014091508160808501526153288285018a614a85565b97811660a085015295861660c084015250509190921660e08201527fffffffff0000000000000000000000000000000000000000000000000000000090911661010090910152949350505050565b60006101006001600160a01b03808b16845289602085015281604085015260008285015261012091508160608501526153b18285018a614a85565b978116608085015295861660a084015250509190921660c08201527fffffffff0000000000000000000000000000000000000000000000000000000090911660e0909101529392505050565b60008261540c5761540c61512f565b500690565b600082821015615423576154236150e6565b500390565b6000808212826001600160ff1b0303841381151615615449576154496150e6565b82600160ff1b038412811615615461576154616150e6565b50500190565b6000826154765761547661512f565b600160ff1b821460001984141615615490576154906150e6565b500590565b60006001600160c01b03808316818114156154b2576154b26150e6565b6001019392505050565b60006001600160c01b038216806154d5576154d56150e6565b6000190192915050565b600080831283600160ff1b018312811516156154fd576154fd6150e6565b836001600160ff1b03018313811615615518576155186150e6565b50500390565b6000600160ff1b821415615534576155346150e6565b5060000390565b6000816000190483118215151615615555576155556150e6565b500290565b634e487b7160e01b600052602160045260246000fd5b60008251615582818460208701614a55565b9190910192915050565b60008161559b5761559b6150e6565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220aaf5f98df755a777bf2ab38bf5b0d2b2f9740cb9c1a1c935964c5d11caec024864736f6c63430008090033