Contract 0xdc91ea613247c0c9438a6f64cc0e08291198981a 2

Contract Overview

Balance:
0 AVAX
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x5c83e5442bd66869aa18fd68e2fe838a68d8e9fa31a517c7bb0bad92977ef351Update Beacon Wi...202945122023-03-27 5:59:20179 days 7 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011244930
0x861a8e8a23deb951950e4e6bfe3c13e74056437e33ff2f8866a67662f1deec4eUpdate Beacon Wi...202934292023-03-27 5:19:49179 days 8 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011244930
0xb59756969c6c80655d34621060e6cbca3c0287fe5ee529ad24a442ff0e130d70Update Beacon Wi...202871622023-03-27 1:22:49179 days 12 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011250930
0x3cc988164a5f1938a255842f01c509f50da11564281afeb18b09efcdaac1b331Update Beacon Wi...202771402023-03-26 18:56:48179 days 18 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011247330
0x9f52f899cb8cb67d9e2e0228fcee61d8ab0064b6df04d1b622b1d6b6dc424b5dUpdate Beacon Wi...202759592023-03-26 18:15:50179 days 19 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011254530
0x1463c182f9cba59b33fc3f3e4a5af4745fe60fed104a601574c30e44fca265b7Update Beacon Wi...202736502023-03-26 16:54:48179 days 21 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011248530
0x8a3ed4be2730b0ea8cf34e0d64b3ca34db8a47abf1eb86726e24a9f07caa722fUpdate Beacon Wi...202681392023-03-26 13:41:50180 days 13 mins ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011254530
0x135420ca20f6e9655deec0764dbd3681c41b9afec564f734ef4c4f11368a6e68Update Beacon Wi...202681392023-03-26 13:41:50180 days 13 mins ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011258130
0xf7e1babe39605b8e5c02f12e9008acbd53da627d25159a89d52432b934a6b175Update Beacon Wi...202667892023-03-26 12:53:47180 days 1 hr ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011244930
0x2beccc40c8a028b43855efd51fcfe6ef7fb6e892c09159868a5ed02554300949Update Beacon Wi...202613512023-03-26 9:35:48180 days 4 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011250930
0xf9b906bf2540852ef0e5c4f5939756e5ab9437228c52cb1aa1843c597744d269Update Beacon Wi...202607762023-03-26 9:13:48180 days 4 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011244930
0x37d32a42325141627254ad0953d0274fa4104720f0ee339a5b89983e7ee50ba3Update Beacon Wi...202599842023-03-26 8:44:49180 days 5 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011252130
0xd34cbbc7bd477cccdd1ae783f0cd6301193e24b52f2a79d37dcc46cecc9ffa22Update Beacon Wi...202533162023-03-26 4:32:18180 days 9 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011250930
0x850e3793c0173c216d5c6398c30caf385016c24ca6c6c76afec2c274a9b3413cUpdate Beacon Wi...202490872023-03-26 1:29:50180 days 12 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011248530
0x5fb916af604d5732dda18cc604dc774804977a32ac533c96a20f5812fb63193bUpdate Beacon Wi...202480132023-03-26 0:46:49180 days 13 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011254530
0x7fae7208f93666b660b9433476424042ca90d6548f7dfc5672e7b1ab5e620324Update Beacon Wi...202469102023-03-26 0:04:19180 days 13 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011250930
0xddf8cb6ab494b2f1c6125affb962f9902c96d8e3b2ba1aa2899a7bc10dca306bUpdate Beacon Wi...202443312023-03-25 22:19:49180 days 15 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011250930
0xe42897df14e584d54a4bcb2da12c9b800d7e81cd28a030c941be5b77471f4d87Update Beacon Wi...202396802023-03-25 19:15:49180 days 18 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011241330
0x291711a33765be193a836917159894e027738efdf198d7e41d0e0ec0574993f7Update Beacon Wi...202394612023-03-25 19:07:50180 days 18 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011248530
0x791aaa92a5b318673e3bd489f9641413ecb393560ccbd85747f791ee130e158eUpdate Beacon Wi...202393962023-03-25 19:05:18180 days 18 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011254530
0xdf6f70f9d2cce963f73800e5cb09c0918042944199162d611248ed366fc1fdfbUpdate Beacon Wi...202393122023-03-25 19:01:48180 days 18 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011248530
0x9dab5ebb880a945e6f044b0f67a66aabef1199a96895541eb097e80685dd8ec4Update Beacon Wi...202372052023-03-25 17:41:18180 days 20 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011244930
0xb3f8bd6a9ee9874f813a61af8b228548291c0338fcb18b5772913c07f7cc7925Update Beacon Wi...202343902023-03-25 15:56:49180 days 21 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011241330
0x70c444dd26de8c1e2e777b895563e8462622e09f6de5b412f0e31ee1b36b392aUpdate Beacon Wi...202342542023-03-25 15:51:49180 days 22 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011258130
0xd354f986c4bfa6c9af98e12916f0c6e139a878af25904885fe9019545d830712Update Beacon Wi...202332102023-03-25 15:13:20180 days 22 hrs ago0xe9d92dc456f35d7643719ce852308c0a201f8476 IN 0xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX0.0011250930
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x8738bc69c4a3055851bc0642abb251eb592400e6ac53450923cb52b216ece2ca259832152023-09-19 8:54:433 days 5 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xe32ddbd0e9fe9229c779d6a7d620fe58a905dc14a2e05c9dd9726af665aec944259086202023-09-17 2:54:195 days 11 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x497f2ff4ff68e891cf4b25f5642106bab8ed47bc7220fb62d5301c930825836c258656032023-09-15 16:55:416 days 20 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xba3ddb43396e781417567342edffef83e44d3f0822ba1fe49e36699c0c34314b258638692023-09-15 15:52:426 days 22 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x3ae63566b2fdbb4a7005c371186b3f0496a7cc77b7a3825d2e3f12d39a8e2e63258638492023-09-15 15:52:026 days 22 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x2f1916a24c025e5243234d92775ffc1d94f4a4691ed961749432881ee9e3d22a257869112023-09-13 8:57:099 days 4 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xea82a756e6e040dc8772dc48d8e7bf991ca2a074d956ed006c4c734f15bfaae5257640322023-09-12 14:16:529 days 23 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xd8b12e5325b5b34ebcd8901aec7a6fae974698d95867cd6895ee36fa407e9f2a257640192023-09-12 14:16:219 days 23 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xa19a2936028b11afc855a0e5bfd57c827f73868836640c56c79b77aa66365600255488872023-09-04 13:31:1918 days 24 mins ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x1ffbd9f40e064042c48e83001fec2c1eff24a106773ce75bf257709f9cc58ad5254401512023-09-01 0:18:5321 days 13 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xab7f22b87abb632b199c12f5db2b238e1fc2edfbe2688815788aad443602a12c254401442023-09-01 0:18:0421 days 13 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x7c8b577e88bf39477bbfc7feb2c00b6c24c820af1e33ec077e2067556078ab4a254401342023-09-01 0:17:1221 days 13 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x776c961ef0a0b56aa9d79e8f8c898f10a39f2889c93b3719bd7a7baff54379b0253770922023-08-29 11:05:5224 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x1c1282c340ab326c88bc483da7f2fb9dd563e4821ce015d0a908dc3cd310bd98253770672023-08-29 11:05:0124 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x94ff5cc90fafd5f15afa8140a25bdaa1cf49bbb4c1a10cc577e58550b0940b45253770372023-08-29 11:03:5824 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x6580a9f1b4d1bf25c558c27d34d0e32ecb58d1c0c84685fbf69a0d93b05a638f253770202023-08-29 11:03:2224 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xe8981fb37ee44dd36223b25a8c8f71e583da97d94938a8bb0e5c09775e015a7e253770012023-08-29 11:02:4124 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x94b34329f0537568b1c8f0a54054be2e7109c91948fa0afcf941e5851ed93800253769392023-08-29 11:00:2324 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x9950c3b2299d61f92eeeb0863037784fe3849be3acfbc56184a2277832695543253477502023-08-28 8:02:5525 days 5 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xdecae7c3fce2c0b756ae20534c1092d0523f9ab9028a112729d4ba87cadee111252773012023-08-25 11:41:5728 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x18405cb961806139e4494454c8cf1e70093811198eeb176bc15070129507bc3d252772452023-08-25 11:39:2628 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0x1eae3decba2e227d177104719a1ac4ae35c615877b5709ba536742b40097f161252772132023-08-25 11:38:0428 days 2 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xc0453e64235948b610b49840c7d3a44df98c8365ba6111e0a8f882fedc6bcc0a252737292023-08-25 9:04:3728 days 4 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xce676cf1e0ae396d6dfa74c847220044701af4a160bbe4c296fd3d8a8223a1e3252737212023-08-25 9:04:0228 days 4 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
0xf2fd7971cf2c5fb5ff75904c49a852ca09164343e17c00934d5cad755709728d252737182023-08-25 9:03:4628 days 4 hrs ago 0x92279914f8862502ee8308fb4286942c14aa9f990xdc91ea613247c0c9438a6f64cc0e08291198981a0 AVAX
[ Download CSV Export 
Index Block
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
DapiServer

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 32 : DapiServer.sol
// 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;
    }
}

File 2 of 32 : 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;
    }
}

File 3 of 32 : 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
            );
        }
    }
}

File 4 of 32 : 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;
    }
}

File 5 of 32 : 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;
            }
        }
    }
}

File 6 of 32 : 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);
}

File 7 of 32 : 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));
    }
}

File 8 of 32 : 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;
    }
}

File 9 of 32 : 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);
            }
        }
    }
}

File 10 of 32 : 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;
    }
}

File 11 of 32 : 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
            );
    }
}

File 12 of 32 : 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;
}

File 13 of 32 : 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));
}

File 14 of 32 : 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);
    }
}

File 15 of 32 : 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);
}

File 16 of 32 : 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);
}

File 17 of 32 : 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);
}

File 18 of 32 : 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
        );
    }
}

File 19 of 32 : 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);
}

File 20 of 32 : 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));
    }
}

File 21 of 32 : 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;
    }
}

File 22 of 32 : 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);
}

File 23 of 32 : IAccessControlRegistryUser.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

File 24 of 32 : 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;
}

File 25 of 32 : 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);
}

File 26 of 32 : IAirnodeRequester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

File 27 of 32 : 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
        );
}

File 28 of 32 : 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);
}

File 29 of 32 : 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);
}

File 30 of 32 : 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]);
        }
    }
}

File 31 of 32 : 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]);
        }
    }
}

File 32 of 32 : 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);
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract ABI

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

6101a06040523480156200001257600080fd5b5060405162005d7938038062005d7983398101604081905262000035916200054d565b808484848282828282828282816001600160a01b038116620000915760405162461bcd60e51b815260206004820152601060248201526f4143522061646472657373207a65726f60801b60448201526064015b60405180910390fd5b6001600160a01b03166080528051620000ed5760405162461bcd60e51b815260206004820152601c60248201527f41646d696e20726f6c65206465736372697074696f6e20656d70747900000000604482015260640162000088565b80516200010290600290602084019062000441565b508060405160200162000116919062000640565b60408051601f19818403018152919052805160209091012060a05250506001600160a01b0381166200018b5760405162461bcd60e51b815260206004820152601460248201527f4d616e616765722061646472657373207a65726f000000000000000000000000604482015260640162000088565b6001600160a01b03811660c052620001a3816200039b565b60e081815250505050506200021e60e0516040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e646572000000815250604051602001620001fc919062000640565b604051602081830303815290604052805190602001206200041560201b60201c565b61010081815250506200027560e0516040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e207365747465720000000000815250604051602001620001fc919062000640565b6101208181525050620002cc60e0516040518060400160405280601681526020017f496e646566696e6974652077686974656c697374657200000000000000000000815250604051602001620001fc919062000640565b6101405250505050506001600160a01b03821615159050620003315760405162461bcd60e51b815260206004820152601c60248201527f4169726e6f646550726f746f636f6c2061646472657373207a65726f00000000604482015260640162000088565b6001600160a01b03166101605260c0516200038c9062000351906200039b565b6040518060400160405280601081526020016f3220a824903730b6b29039b2ba3a32b960811b815250604051602001620001fc919062000640565b61018052506200069b92505050565b60006200040f620003e0836040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b60a051604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b92915050565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b8280546200044f906200065e565b90600052602060002090601f016020900481019282620004735760008555620004be565b82601f106200048e57805160ff1916838001178555620004be565b82800160010185558215620004be579182015b82811115620004be578251825591602001919060010190620004a1565b50620004cc929150620004d0565b5090565b5b80821115620004cc5760008155600101620004d1565b80516001600160a01b0381168114620004ff57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620005375781810151838201526020016200051d565b8381111562000547576000848401525b50505050565b600080600080608085870312156200056457600080fd5b6200056f85620004e7565b60208601519094506001600160401b03808211156200058d57600080fd5b818701915087601f830112620005a257600080fd5b815181811115620005b757620005b762000504565b604051601f8201601f19908116603f01168101908382118183101715620005e257620005e262000504565b816040528281528a6020848701011115620005fc57600080fd5b6200060f8360208301602088016200051a565b80975050505050506200062560408601620004e7565b91506200063560608601620004e7565b905092959194509250565b60008251620006548184602087016200051a565b9190910192915050565b600181811c908216806200067357607f821691505b602082108114156200069557634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e051610100516101205161014051610160516101805161560062000779600039600081816105b6015261207401526000818161076a015281816116cd015281816122f00152612bb001526000818161089201526132f101526000818161065301526131b10152600081816105f601526137c1015260006103540152600081816104920152818161203e015281816129f501528181613173015281816132b30152613783015260005050600081816103c9015281816120a0015281816131e70152818161332701526137f701526156006000f3fe608060405234801561001057600080fd5b506004361061034a5760003560e01c80638e85afe0116101bd578063bcb5daec116100f9578063e18de857116100a2578063f60e45d31161007c578063f60e45d3146108da578063f8b2cb4f1461091b578063fc3c1f8b14610936578063fce90be81461094957600080fd5b8063e18de8571461088d578063e406f3b2146108b4578063e705882e146108c757600080fd5b8063d55a42dd116100d3578063d55a42dd1461082b578063d7c0b87914610867578063dc96acc81461087a57600080fd5b8063bcb5daec1461078c578063c4f95dd51461079f578063d39c8968146107b257600080fd5b80639e14caa811610166578063a6b1b9a211610140578063a6b1b9a21461070f578063ac9650d814610722578063b605bbcd14610742578063b76f3c151461076557600080fd5b80639e14caa8146106c9578063a026658f146106dc578063a5fc076f146106fc57600080fd5b806391eed0851161019757806391eed085146106755780639421240514610688578063977cae011461069b57600080fd5b80638e85afe0146106185780638fca9ab91461063b5780638fe47dd81461064e57600080fd5b80634c8f1d8d1161028c5780636db798f9116102355780637512449b1161020f5780637512449b146105b1578063776bcbd2146105d8578063796b89b9146105eb5780637c706738146105f157600080fd5b80636db798f9146105575780636ed93dd01461059357806370c927ca1461059e57600080fd5b80635bc8a5c8116102665780635bc8a5c8146104f557806365101054146105085780636bd2bdd01461051b57600080fd5b80634c8f1d8d146104b45780634dcc19fe146104c957806358c55edd146104cf57600080fd5b8063206b48f4116102f95780633408e470116102d35780633408e4701461044f5780633af1dbfd1461045557806342cbb15c14610487578063481c6a751461048d57600080fd5b8063206b48f4146104165780632ce010e3146104295780633376e7c21461043c57600080fd5b80631a0a0b3e1161032a5780631a0a0b3e146103af5780631ce9ae07146103c45780631d36cf5c1461040357600080fd5b80629f2f3c1461034f578062aae33f1461038957806304dd3cee1461039c575b600080fd5b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6103766103973660046147a4565b610985565b6103766103aa3660046147f5565b610be1565b6103c26103bd366004614861565b610cca565b005b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610380565b6103c26104113660046148f5565b610e46565b6103c2610424366004614942565b610fb1565b6103c2610437366004614942565b6112ad565b6103c261044a366004614a07565b61132c565b46610376565b610468610463366004614a3c565b611472565b60408051601b9390930b835263ffffffff909116602083015201610380565b43610376565b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b6104bc611574565b6040516103809190614ab1565b48610376565b6104e26104dd366004614a3c565b611602565b604051601b9190910b8152602001610380565b6103c2610503366004614ac4565b6116c2565b610376610516366004614c69565b6117f6565b6104bc6040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e20736574746572000000000081525081565b6104bc6040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e64657200000081525081565b6103766305f5e10081565b6104e26105ac366004614a3c565b611d3b565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c26105e6366004614d3b565b611e35565b42610376565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b61062b610626366004614d77565b611efe565b6040519015158152602001610380565b610376610649366004614a3c565b611fa1565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c2610683366004614dbc565b611fe6565b610376610696366004614dde565b6121f6565b61062b6106a9366004614e11565b600460209081526000928352604080842090915290825290205460ff1681565b6103c26106d7366004614e44565b6123e0565b6103766106ea366004614a3c565b60056020526000908152604090205481565b61046861070a366004614a3c565b61249b565b6103c261071d3660046148f5565b612511565b610735610730366004614e7b565b612643565b6040516103809190614ef0565b61062b610750366004614f52565b60036020526000908152604090205460ff1681565b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b61062b61079a366004614f6d565b612738565b6103766107ad3660046147a4565b61277f565b6108036107c0366004614f6d565b6000918252602082815260408084206001600160a01b0393909316845291905290205467ffffffffffffffff811691600160401b9091046001600160c01b031690565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610380565b6104bc6040518060400160405280601681526020017f496e646566696e6974652077686974656c69737465720000000000000000000081525081565b61062b610875366004614f90565b6127af565b61062b610888366004614f90565b6128b4565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c26108c2366004614f52565b6129ea565b6103766108d536600461500a565b612aae565b61062b6108e8366004614d3b565b60009283526001602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b610376610929366004614f52565b6001600160a01b03163190565b610376610944366004615057565b612cb9565b6104bc6040518060400160405280601081526020017f64415049206e616d65207365747465720000000000000000000000000000000081525081565b8051600090600181116109df5760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064015b60405180910390fd5b60008167ffffffffffffffff8111156109fa576109fa6146ce565b604051908082528060200260200182016040528015610a23578160200160208202803683370190505b5090506000805b83811015610abb57600060066000888481518110610a4a57610a4a6150d0565b602090810291909101810151825281019190915260400160002080548551919250601b0b90859084908110610a8157610a816150d0565b60209081029190910101528054610aa590600160e01b900463ffffffff16846150fc565b9250508080610ab390615114565b915050610a2a565b506000610ac88483615145565b9050610ad38661277f565b60008181526006602052604090205490955063ffffffff600160e01b90910481169082161015610b455760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000610b5084612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008e81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925087917fb7712be6248d021e8c56ac9613c09491354a4d0f4ad0b7db1a664b35be4b2349910160405180910390a25050505050919050565b60006001600160a01b038316610c395760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f00000000000000000000000060448201526064016109d6565b81610c865760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f0000000000000000000000000000000060448201526064016109d6565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260540160405160208183030381529060405280519060200120905092915050565b84610cd481612fd5565b610d165760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b876001600160a01b0316610d9584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508c908c908c908c90602001615159565b60405160208183030381529060405280519060200120613001565b9061303c565b6001600160a01b031614610de05760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b6000610dec8989610be1565b90506000610dfc82898989613060565b60408051828152602081018b905291925083917f403078446dab7471f481ca4bffac706bd84a6dba118980d267676c096f2ba924910160405180910390a250505050505050505050565b610e4f33613165565b610e9b5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f74207365742065787069726174696f6e000000000000000000000060448201526064016109d6565b82610eda5760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b038216610f245760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000838152602081815260408083206001600160a01b03861684529091529020805467ffffffffffffffff191667ffffffffffffffff831617905560405167ffffffffffffffff8216815233906001600160a01b0384169085907fd19e89b7d547ccf349211588a9a1d29461e2ce984b1b1cdbe7150976528b86f1906020015b60405180910390a4505050565b84610fbb81612fd5565b610ffd5760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6040516bffffffffffffffffffffffff1960608b811b821660208401528a811b8216603484015289901b166048820152605c0160408051601f19818403018152918152815160209283012060008d81526008909352912054146110a25760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b876001600160a01b0316896001600160a01b0316141561118b57886001600160a01b031661113b84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c903390602001928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b6001600160a01b0316146111865760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611236565b886001600160a01b03166111eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c9033908d908d9060200161517a565b6001600160a01b0316146112365760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b60008a8152600560205260408120549061125282898989613060565b604080518e8152601b83900b602082015263ffffffff8b1681830152905191925083917f2b85d71813342fd0091d4573a5f78e244c2133755e470943c33a6dbf3cf9c15d9181900360600190a2505050505050505050505050565b6112bc610397848601866147a4565b84846040516112cc9291906151b4565b6040518091039020146113215760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b505050505050505050565b611335336132a5565b6113815760405162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742073657420696e646566696e697465207374617475730000000060448201526064016109d6565b826113c05760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b03821661140a5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000611417848484613356565b6040805184151581526001600160c01b038316602082015291925033916001600160a01b0386169187917f2fa93828cf3f001b9a9f0a7365db04ea068b9db7bcb7a38d289f0cf8aa9ce374910160405180910390a450505050565b60008060008360405160200161148a91815260200190565b6040516020818303038152906040528051906020012090506114ac8133612738565b6114ed5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b600081815260096020526040902054806115495760405162461bcd60e51b815260206004820152601160248201527f64415049206e616d65206e6f742073657400000000000000000000000000000060448201526064016109d6565b600090815260066020526040902054601b81900b95600160e01b90910463ffffffff16945092505050565b60028054611581906151c4565b80601f01602080910402602001604051908101604052809291908181526020018280546115ad906151c4565b80156115fa5780601f106115cf576101008083540402835291602001916115fa565b820191906000526020600020905b8154815290600101906020018083116115dd57829003601f168201915b505050505081565b600061160e8233612738565b61164f5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b60008281526006602052604090208054600160e01b900463ffffffff166116b85760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b92915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461173a5760405162461bcd60e51b815260206004820152601b60248201527f53656e646572206e6f74204169726e6f64652070726f746f636f6c000000000060448201526064016109d6565b8261174481612fd5565b6117865760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6000858152600760205260408120805490829055906117a782878787613060565b604080518981526020810183905290810188905290915082907f7c88b543c4cda65df046e87397c3152c27098419cb22a90a5e37db30ef621b269060600160405180910390a250505050505050565b84518451600091908114801561180c5750845181145b80156118185750835181145b80156118245750825181145b6118705760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d617463680000000000000060448201526064016109d6565b600181116118c05760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064016109d6565b60008167ffffffffffffffff8111156118db576118db6146ce565b604051908082528060200260200182016040528015611904578160200160208202803683370190505b50905060008267ffffffffffffffff811115611922576119226146ce565b60405190808252806020026020018201604052801561194b578160200160208202803683370190505b5090506000805b84811015611c105786818151811061196c5761196c6150d0565b602002602001015151600014611b555760008b8281518110611990576119906150d0565b6020026020010151905060008a83815181106119ae576119ae6150d0565b602002602001015190506119c181612fd5565b611a035760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b816001600160a01b0316611a748a8581518110611a2257611a226150d0565b6020026020010151610d8f8f8781518110611a3f57611a3f6150d0565b6020026020010151858f8981518110611a5a57611a5a6150d0565b6020026020010151604051602001610d74939291906151f9565b6001600160a01b031614611abf5760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611ae18a8481518110611ad457611ad46150d0565b602002602001015161350e565b601b0b858481518110611af657611af66150d0565b6020908102919091010152611b0b81856150fc565b9350611b30828d8581518110611b2357611b236150d0565b6020026020010151610be1565b868481518110611b4257611b426150d0565b6020026020010181815250505050611bfe565b6000611b868c8381518110611b6c57611b6c6150d0565b60200260200101518c8481518110611b2357611b236150d0565b6000818152600660205260409020805486519293509091601b9190910b90869085908110611bb657611bb66150d0565b60209081029190910101528054611bda90600160e01b900463ffffffff16856150fc565b935081868481518110611bef57611bef6150d0565b60200260200101818152505050505b80611c0881615114565b915050611952565b50611c1a8361277f565b94506000611c288583615145565b60008781526006602052604090205490915063ffffffff600160e01b90910481169082161015611c9a5760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000611ca584612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008f81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925088917fb34747dc40d9c985b4857c2955fec7a8f34d88bc06da72f43319795758800407910160405180910390a250505050505095945050505050565b60008082604051602001611d5191815260200190565b604051602081830303815290604052805190602001209050611d738133612738565b611db45760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b6000818152600960209081526040808320548352600690915290208054600160e01b900463ffffffff16611e2a5760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b9392505050565b611e3e816132a5565b15611e8b5760405162461bcd60e51b815260206004820181905260248201527f7365747465722063616e2073657420696e646566696e6974652073746174757360448201526064016109d6565b600080611e99858585613611565b915091508115611ef757604080513381526001600160c01b03831660208201526001600160a01b03808616929087169188917f29c394c1d92801cab93215bf9cd50ae38d23341be6540f27d80ee2bc8a541237910160405180910390a45b5050505050565b600080611f0a8461277f565b600081815260066020908152604091829020825180840190935254601b81900b8352600160e01b900463ffffffff1690820152909150611f4985610985565b506000828152600660205260409020815181548691611f6a91601b0b6136fc565b101580611f975750602082015163ffffffff16158015611f9757508054600160e01b900463ffffffff1615155b9695505050505050565b60006009600083604051602001611fba91815260200190565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b816120335760405162461bcd60e51b815260206004820152600e60248201527f64415049206e616d65207a65726f00000000000000000000000000000000000060448201526064016109d6565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806121225750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d148549060440160206040518083038186803b1580156120ea57600080fd5b505afa1580156120fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121229190615226565b61216e5760405162461bcd60e51b815260206004820152601b60248201527f53656e6465722063616e6e6f74207365742064415049206e616d65000000000060448201526064016109d6565b80600960008460405160200161218691815260200190565b60405160208183030381529060405280519060200120815260200190815260200160002081905550336001600160a01b0316827ff3a9aac9b6ac0f842cb5d9b3491cd5fc1b6a6778d97fd9529f587339865294f5836040516121ea91815260200190565b60405180910390a35050565b6000816001600160a01b03811633148061223357506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b61227f5760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b600061228b8686610be1565b6040517feebecf690000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301526024820188905260a06044830152600060a48301528681166064830152630b7914b960e31b60848301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063eebecf699060c401602060405180830381600087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236e9190615243565b60008181526007602090815260409182902084905581518381526001600160a01b038a811692820192909252918201889052919450339186169083907fc0e8c8735457dd46668d6c01832656e0803b41b1e61ba12354a24f11e114eab39060600160405180910390a450509392505050565b6001600160a01b0382166124365760405162461bcd60e51b815260206004820152601560248201527f55706461746520726571756573746572207a65726f000000000000000000000060448201526064016109d6565b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fba239f737a4075f5efb0d649e16cd7d2b1978690876b079dab642f077050b27991016121ea565b6000806124a88333612738565b6124e95760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b5050600090815260066020526040902054601b81900b91600160e01b90910463ffffffff1690565b61251a33613775565b6125665760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420657874656e642065787069726174696f6e000000000000000060448201526064016109d6565b826125a55760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b0382166125ef5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6125fa838383613826565b60405167ffffffffffffffff8216815233906001600160a01b0384169085907fa9e0c89b898eb7a904617915dc5b5510d539c899810042e9248569b54b9cc2ed90602001610fa4565b60608167ffffffffffffffff81111561265e5761265e6146ce565b60405190808252806020026020018201604052801561269157816020015b606081526020019060019003908161267c5790505b50905060005b8281101561273157612701308585848181106126b5576126b56150d0565b90506020028101906126c7919061525c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506138a392505050565b828281518110612713576127136150d0565b6020026020010181905250808061272990615114565b915050612697565b5092915050565b60006001600160a01b0382161580612755575061275583836138c8565b8061277857506001600160a01b03821660009081526003602052604090205460ff165b9392505050565b60008160405160200161279291906152a3565b604051602081830303815290604052805190602001209050919050565b600033156127ff5760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600061280d858701876147a4565b9050858560405161281f9291906151b4565b60405180910390208160405160200161283891906152a3565b604051602081830303815290604052805190602001201461289b5760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6128a981610626868661391b565b979650505050505050565b600033156129045760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600086815260056020526040902054806129605760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b6000818152600660205260409020612978858561391b565b8154604080516020601f8b018190048102820181019092528981526129c592601b0b916129c091908c908c908190840183828082843760009201919091525061350e92505050565b6136fc565b1015806129de57508054600160e01b900463ffffffff16155b98975050505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612a625760405162461bcd60e51b815260206004820152601260248201527f53656e646572206e6f74206d616e61676572000000000000000000000000000060448201526064016109d6565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f6d71fde11d6c56eaf0154d464b51df0b26dcdac118703056ea3da805fc9e1cd79190a250565b6000816001600160a01b038116331480612aeb57506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b612b375760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b6000612b438787610be1565b6040517fa7e0c85e0000000000000000000000000000000000000000000000000000000081526001600160a01b0389811660048301526024820189905260c06044830152600060c483015287811660648301528681166084830152630b7914b960e31b60a48301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a7e0c85e9060e401602060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2e9190615243565b600081815260076020526040908190208390555190935033906001600160a01b0386169083907faeef85c84fba3ef952ca2738d951d30f5093595cb5eb9830764f5b49a39442a390612ca79088908d908c908e909384526001600160a01b03928316602085015291166040830152606082015260800190565b60405180910390a45050949350505050565b60006001600160a01b038316612d115760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b6001600160a01b038216612d675760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b604051612d8d90469088908890889088908890309063081ad23d60e21b906020016152e7565b60408051601f1981840301815282825280516020918201206bffffffffffffffffffffffff1960608b811b82168487015288811b8216603487015287901b1660488501528251808503603c018152605c909401835283519382019390932060008481526008909252919020559050612e058686610be1565b60008281526005602052604090819020919091555181907f82b139c5e690a3a4a9c2f68133d2ab1f7724bf8a69ad3fe1cd9f5a4923cf41b290612e5c9089908990899089908990309063081ad23d60e21b90615376565b60405180910390a295945050505050565b805160009060098111612f2d57612e8383613979565b612e8e6002826153fd565b60011415612ec25782612ea2600283615145565b81518110612eb257612eb26150d0565b6020026020010151915050919050565b600283612ecf8284615145565b81518110612edf57612edf6150d0565b6020026020010151846001600285612ef79190615145565b612f019190615411565b81518110612f1157612f116150d0565b6020026020010151612f239190615428565b6127789190615467565b612f386002826153fd565b60011415612f555782612ea281612f50600285615145565b613e90565b600080612f78856001612f69600287615145565b612f739190615411565b613eae565b915091506002858281518110612f9057612f906150d0565b6020026020010151868481518110612faa57612faa6150d0565b6020026020010151612fbc9190615428565b612fc69190615467565b95945050505050565b50919050565b600042612fe483610e106150fc565b118015612ffb5750612ff8426103846150fc565b82105b92915050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01612792565b600080600061304b8585613f2b565b9150915061305881613f98565b509392505050565b60006130a183838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061350e92505050565b600086815260066020526040902054601b9190910b9150600160e01b900463ffffffff1684116131135760405162461bcd60e51b815260206004820152601d60248201527f46756c66696c6c6d656e74206f6c646572207468616e20426561636f6e00000060448201526064016109d6565b604080518082018252601b83900b815263ffffffff95861660208083019182526000988952600690529190962095519051909416600160e01b026001600160e01b039094169390931790935550919050565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044015b60206040518083038186803b15801561322a57600080fd5b505afa15801561323e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffb9190615226565b6000928352602083815260408085206001600160a01b039490941685529290529120805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401613212565b6000838152602081815260408083206001600160a01b0386168452909152902054600160401b90046001600160c01b03168180156133bf575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff16155b1561344c5760008481526001602081815260408084206001600160a01b03881685528252808420338552909152909120805460ff191690911790558061340481615495565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b0384160217905591506127789050565b81158015613484575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff165b156127785760008481526001602090815260408083206001600160a01b038716845282528083203384529091529020805460ff19169055806134c5816154bc565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b038416021790559150509392505050565b600081516020146135615760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6000828060200190518101906135779190615243565b90507fffffffff8000000000000000000000000000000000000000000000000000000081128015906135c557507b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff8113155b612ffb5760405162461bcd60e51b815260206004820152601760248201527f56616c7565207479706563617374696e67206572726f7200000000000000000060448201526064016109d6565b6000838152602081815260408083206001600160a01b0386811680865291845282852054888652600185528386209286529184528285209086168552909252822054600160401b9091046001600160c01b03169060ff16156136f45760008581526001602090815260408083206001600160a01b03808916855290835281842090871684529091529020805460ff19169055806136ad816154bc565b6000878152602081815260408083206001600160a01b038a1684529091529020805467ffffffffffffffff16600160401b6001600160c01b03841602179055600193509150505b935093915050565b60008083601b0b83601b0b61371191906154df565b9050600080821361372a576137258261551e565b61372c565b815b905060008086601b0b1361374c5785601b0b6137479061551e565b613751565b85601b0b5b90508061375c575060015b8061376b6305f5e1008461553b565b611f979190615145565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401613212565b6000838152602081815260408083206001600160a01b038616845290915290205467ffffffffffffffff908116908216116132625760405162461bcd60e51b815260206004820152601a60248201527f446f6573206e6f7420657874656e642065787069726174696f6e00000000000060448201526064016109d6565b606061277883836040518060600160405280602781526020016155a460279139614156565b6000828152602081815260408083206001600160a01b038516845290915281208054600160401b90046001600160c01b0316151580613913575080544267ffffffffffffffff909116115b949350505050565b60006020821461396d5760405162461bcd60e51b815260206004820152601a60248201527f496e636f727265637420706172616d65746572206c656e67746800000000000060448201526064016109d6565b61277882840184614a3c565b805160098111156139cc5760405162461bcd60e51b815260206004820152601660248201527f417272617920746f6f206c6f6e6720746f20736f72740000000000000000000060448201526064016109d6565b6006811015613ada576004811015613a28578060031415613a12576139f48260006001614237565b613a018260016002614237565b613a0e8260006001614237565b5050565b8060021415613a0e57613a0e8260006001614237565b8060051415613aa657613a3e8260016002614237565b613a4b8260036004614237565b613a588260016003614237565b613a658260006002614237565b613a728260026004614237565b613a7f8260006003614237565b613a8c8260006001614237565b613a998260026003614237565b613a0e8260016002614237565b613ab38260006001614237565b613ac08260026003614237565b613acd8260016003614237565b613a998260006002614237565b6008811015613c58578060071415613bbc57613af98260016002614237565b613b068260036004614237565b613b138260056006614237565b613b208260006002614237565b613b2d8260046006614237565b613b3a8260036005614237565b613b478260026006614237565b613b548260016005614237565b613b618260006004614237565b613b6e8260026005614237565b613b7b8260006003614237565b613b888260026004614237565b613b958260016003614237565b613ba28260006001614237565b613baf8260026003614237565b613a0e8260046005614237565b613bc98260006001614237565b613bd68260026003614237565b613be38260046005614237565b613bf08260016003614237565b613bfd8260036005614237565b613c0a8260016003614237565b613c178260026004614237565b613c248260006002614237565b613c318260026004614237565b613c3e8260036004614237565b613c4b8260016002614237565b613a0e8260026003614237565b8060091415613d8c57613c6e8260016008614237565b613c7b8260026007614237565b613c888260036006614237565b613c958260046005614237565b613ca28260016004614237565b613caf8260056008614237565b613cbc8260006002614237565b613cc98260066007614237565b613cd68260026006614237565b613ce38260076008614237565b613cf08260006003614237565b613cfd8260046005614237565b613d0a8260006001614237565b613d178260036005614237565b613d248260066007614237565b613d318260026004614237565b613d3e8260016003614237565b613d4b8260056007614237565b613d588260046006614237565b613d658260016002614237565b613d728260036004614237565b613d7f8260056006614237565b613ba28260076008614237565b613d998260006007614237565b613da68260016006614237565b613db38260026005614237565b613dc08260036004614237565b613dcd8260006003614237565b613dda8260046007614237565b613de78260016002614237565b613df48260056006614237565b613e018260006001614237565b613e0e8260026003614237565b613e1b8260046005614237565b613e288260066007614237565b613e358260036005614237565b613e428260026004614237565b613e4f8260016002614237565b613e5c8260036004614237565b613e698260056006614237565b613e768260026003614237565b613e838260046005614237565b613a0e8260036004614237565b600061305883600060018651613ea69190615411565b8560006142e5565b8151600090819060018111613f055760405162461bcd60e51b815260206004820152601d60248201527f417272617920746f6f2073686f727420746f2073656c656374206b2b3100000060448201526064016109d6565b613f1e856000613f16600185615411565b8760016142e5565b92509250505b9250929050565b600080825160411415613f625760208301516040840151606085015160001a613f56878285856143e0565b94509450505050613f24565b825160401415613f8c5760208301516040840151613f818683836144cd565b935093505050613f24565b50600090506002613f24565b6000816004811115613fac57613fac61555a565b1415613fb55750565b6001816004811115613fc957613fc961555a565b14156140175760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016109d6565b600281600481111561402b5761402b61555a565b14156140795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016109d6565b600381600481111561408d5761408d61555a565b14156140e65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016109d6565b60048160048111156140fa576140fa61555a565b14156141535760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016109d6565b50565b6060833b6141cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016109d6565b600080856001600160a01b0316856040516141e79190615570565b600060405180830381855af49150503d8060008114614222576040519150601f19603f3d011682016040523d82523d6000602084013e614227565b606091505b5091509150611f978282866144fc565b828181518110614249576142496150d0565b6020026020010151838381518110614263576142636150d0565b602002602001015113156142e057828181518110614283576142836150d0565b602002602001015183838151811061429d5761429d6150d0565b60200260200101518484815181106142b7576142b76150d0565b602002602001018584815181106142d0576142d06150d0565b6020908102919091010191909152525b505050565b600080848614156142fb575082905060006143d6565b6000614308888888614535565b9050808510156143325761432a8888614322600185615411565b8860006142e5565b509250614356565b808511156143525761432a886143498360016150fc565b888860006142e5565b8092505b83156143d4576143678360016150fc565b915060006143768360016150fc565b90505b88518110156143d257888381518110614394576143946150d0565b60200260200101518982815181106143ae576143ae6150d0565b602002602001015112156143c0578092505b806143ca81615114565b915050614379565b505b505b9550959350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561441757506000905060036144c4565b8460ff16601b1415801561442f57508460ff16601c14155b1561444057506000905060046144c4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614494573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166144bd576000600192509250506144c4565b9150600090505b94509492505050565b6000806001600160ff1b03831660ff84901c601b016144ee878288856143e0565b935093505050935093915050565b6060831561450b575081612778565b82511561451b5782518084602001fd5b8160405162461bcd60e51b81526004016109d69190614ab1565b600081831415614546575081612778565b600084848151811061455a5761455a6150d0565b60209081029190910101519050836145738460016150fc565b92505b8061458081615114565b9150508551811080156145ab5750818682815181106145a1576145a16150d0565b6020026020010151125b614576575b826145ba8161558c565b935050818684815181106145d0576145d06150d0565b6020026020010151136145b05782811061465b578583815181106145f6576145f66150d0565b6020026020010151868681518110614610576146106150d0565b602002602001015187878151811061462a5761462a6150d0565b60200260200101888681518110614643576146436150d0565b60200260200101828152508281525050505050612778565b85838151811061466d5761466d6150d0565b6020026020010151868281518110614687576146876150d0565b60200260200101518783815181106146a1576146a16150d0565b602002602001018886815181106146ba576146ba6150d0565b602090810291909101019190915252614576565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561470d5761470d6146ce565b604052919050565b600067ffffffffffffffff82111561472f5761472f6146ce565b5060051b60200190565b600082601f83011261474a57600080fd5b8135602061475f61475a83614715565b6146e4565b82815260059290921b8401810191818101908684111561477e57600080fd5b8286015b848110156147995780358352918301918301614782565b509695505050505050565b6000602082840312156147b657600080fd5b813567ffffffffffffffff8111156147cd57600080fd5b61391384828501614739565b80356001600160a01b03811681146147f057600080fd5b919050565b6000806040838503121561480857600080fd5b614811836147d9565b946020939093013593505050565b60008083601f84011261483157600080fd5b50813567ffffffffffffffff81111561484957600080fd5b602083019150836020828501011115613f2457600080fd5b600080600080600080600060a0888a03121561487c57600080fd5b614885886147d9565b96506020880135955060408801359450606088013567ffffffffffffffff808211156148b057600080fd5b6148bc8b838c0161481f565b909650945060808a01359150808211156148d557600080fd5b506148e28a828b0161481f565b989b979a50959850939692959293505050565b60008060006060848603121561490a57600080fd5b8335925061491a602085016147d9565b9150604084013567ffffffffffffffff8116811461493757600080fd5b809150509250925092565b600080600080600080600080600060e08a8c03121561496057600080fd5b8935985061497060208b016147d9565b975061497e60408b016147d9565b965061498c60608b016147d9565b955060808a0135945060a08a013567ffffffffffffffff808211156149b057600080fd5b6149bc8d838e0161481f565b909650945060c08c01359150808211156149d557600080fd5b506149e28c828d0161481f565b915080935050809150509295985092959850929598565b801515811461415357600080fd5b600080600060608486031215614a1c57600080fd5b83359250614a2c602085016147d9565b91506040840135614937816149f9565b600060208284031215614a4e57600080fd5b5035919050565b60005b83811015614a70578181015183820152602001614a58565b83811115614a7f576000848401525b50505050565b60008151808452614a9d816020860160208601614a55565b601f01601f19169290920160200192915050565b6020815260006127786020830184614a85565b60008060008060608587031215614ada57600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614aff57600080fd5b614b0b8782880161481f565b95989497509550505050565b600082601f830112614b2857600080fd5b81356020614b3861475a83614715565b82815260059290921b84018101918181019086841115614b5757600080fd5b8286015b8481101561479957614b6c816147d9565b8352918301918301614b5b565b600082601f830112614b8a57600080fd5b813567ffffffffffffffff811115614ba457614ba46146ce565b614bb7601f8201601f19166020016146e4565b818152846020838601011115614bcc57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614bfa57600080fd5b81356020614c0a61475a83614715565b82815260059290921b84018101918181019086841115614c2957600080fd5b8286015b8481101561479957803567ffffffffffffffff811115614c4d5760008081fd5b614c5b8986838b0101614b79565b845250918301918301614c2d565b600080600080600060a08688031215614c8157600080fd5b853567ffffffffffffffff80821115614c9957600080fd5b614ca589838a01614b17565b96506020880135915080821115614cbb57600080fd5b614cc789838a01614739565b95506040880135915080821115614cdd57600080fd5b614ce989838a01614739565b94506060880135915080821115614cff57600080fd5b614d0b89838a01614be9565b93506080880135915080821115614d2157600080fd5b50614d2e88828901614be9565b9150509295509295909350565b600080600060608486031215614d5057600080fd5b83359250614d60602085016147d9565b9150614d6e604085016147d9565b90509250925092565b60008060408385031215614d8a57600080fd5b823567ffffffffffffffff811115614da157600080fd5b614dad85828601614739565b95602094909401359450505050565b60008060408385031215614dcf57600080fd5b50508035926020909101359150565b600080600060608486031215614df357600080fd5b614dfc846147d9565b925060208401359150614d6e604085016147d9565b60008060408385031215614e2457600080fd5b614e2d836147d9565b9150614e3b602084016147d9565b90509250929050565b60008060408385031215614e5757600080fd5b614e60836147d9565b91506020830135614e70816149f9565b809150509250929050565b60008060208385031215614e8e57600080fd5b823567ffffffffffffffff80821115614ea657600080fd5b818501915085601f830112614eba57600080fd5b813581811115614ec957600080fd5b8660208260051b8501011115614ede57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614f4557603f19888603018452614f33858351614a85565b94509285019290850190600101614f17565b5092979650505050505050565b600060208284031215614f6457600080fd5b612778826147d9565b60008060408385031215614f8057600080fd5b82359150614e3b602084016147d9565b600080600080600060608688031215614fa857600080fd5b85359450602086013567ffffffffffffffff80821115614fc757600080fd5b614fd389838a0161481f565b90965094506040880135915080821115614fec57600080fd5b50614ff98882890161481f565b969995985093965092949392505050565b6000806000806080858703121561502057600080fd5b615029856147d9565b93506020850135925061503e604086016147d9565b915061504c606086016147d9565b905092959194509250565b600080600080600060a0868803121561506f57600080fd5b615078866147d9565b945060208601359350604086013567ffffffffffffffff81111561509b57600080fd5b6150a788828901614b79565b9350506150b6606087016147d9565b91506150c4608087016147d9565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000821982111561510f5761510f6150e6565b500190565b6000600019821415615128576151286150e6565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826151545761515461512f565b500490565b84815283602082015281836040830137600091016040019081529392505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b8183823760009101908152919050565b600181811c908216806151d857607f821691505b60208210811415612fcf57634e487b7160e01b600052602260045260246000fd5b83815282602082015260008251615217816040850160208701614a55565b91909101604001949350505050565b60006020828403121561523857600080fd5b8151612778816149f9565b60006020828403121561525557600080fd5b5051919050565b6000808335601e1984360301811261527357600080fd5b83018035915067ffffffffffffffff82111561528e57600080fd5b602001915036819003821315613f2457600080fd5b6020808252825182820181905260009190848201906040850190845b818110156152db578351835292840192918401916001016152bf565b50909695505050505050565b60006101208a83526001600160a01b03808b16602085015289604085015281606085015260008285015261014091508160808501526153288285018a614a85565b97811660a085015295861660c084015250509190921660e08201527fffffffff0000000000000000000000000000000000000000000000000000000090911661010090910152949350505050565b60006101006001600160a01b03808b16845289602085015281604085015260008285015261012091508160608501526153b18285018a614a85565b978116608085015295861660a084015250509190921660c08201527fffffffff0000000000000000000000000000000000000000000000000000000090911660e0909101529392505050565b60008261540c5761540c61512f565b500690565b600082821015615423576154236150e6565b500390565b6000808212826001600160ff1b0303841381151615615449576154496150e6565b82600160ff1b038412811615615461576154616150e6565b50500190565b6000826154765761547661512f565b600160ff1b821460001984141615615490576154906150e6565b500590565b60006001600160c01b03808316818114156154b2576154b26150e6565b6001019392505050565b60006001600160c01b038216806154d5576154d56150e6565b6000190192915050565b600080831283600160ff1b018312811516156154fd576154fd6150e6565b836001600160ff1b03018313811615615518576155186150e6565b50500390565b6000600160ff1b821415615534576155346150e6565b5060000390565b6000816000190483118215151615615555576155556150e6565b500290565b634e487b7160e01b600052602160045260246000fd5b60008251615582818460208701614a55565b9190910192915050565b60008161559b5761559b6150e6565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220aaf5f98df755a777bf2ab38bf5b0d2b2f9740cb9c1a1c935964c5d11caec024864736f6c634300080900330000000000000000000000005997c09be60196b7de9de73c88dd7776f28754010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000c3e76d8829259f2a34541746de2f8a0509dc1987000000000000000000000000ccc843194f02ef93b8ee9a9f7b5f9273604acd120000000000000000000000000000000000000000000000000000000000000010446170695365727665722061646d696e00000000000000000000000000000000

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000005997c09be60196b7de9de73c88dd7776f28754010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000c3e76d8829259f2a34541746de2f8a0509dc1987000000000000000000000000ccc843194f02ef93b8ee9a9f7b5f9273604acd120000000000000000000000000000000000000000000000000000000000000010446170695365727665722061646d696e00000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _accessControlRegistry (address): 0x5997c09be60196b7de9de73c88dd7776f2875401
Arg [1] : _adminRoleDescription (string): DapiServer admin
Arg [2] : _manager (address): 0xc3e76d8829259f2a34541746de2f8a0509dc1987
Arg [3] : _airnodeProtocol (address): 0xccc843194f02ef93b8ee9a9f7b5f9273604acd12

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000005997c09be60196b7de9de73c88dd7776f2875401
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 000000000000000000000000c3e76d8829259f2a34541746de2f8a0509dc1987
Arg [3] : 000000000000000000000000ccc843194f02ef93b8ee9a9f7b5f9273604acd12
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [5] : 446170695365727665722061646d696e00000000000000000000000000000000


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading