Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- DapiServer
- Optimization enabled
- true
- Compiler version
- v0.8.9+commit.e5eed63a
- Optimization runs
- 1000
- EVM Version
- default
- Verified at
- 2022-08-28T16:59:44.548042Z
Constructor Arguments
0x00000000000000000000000092e5125adf385d86bedb950793526106143b6df100000000000000000000000000000000000000000000000000000000000000800000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a4600000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b0000000000000000000000000000000000000000000000000000000000000010446170695365727665722061646d696e00000000000000000000000000000000
Arg [0] (address) : 0x92e5125adf385d86bedb950793526106143b6df1
Arg [1] (string) : DapiServer admin
Arg [2] (address) : 0x7fc103cc17eb1764c14f8a1ac4745445cea62a46
Arg [3] (address) : 0x73ff8cdeea2770ba2b8ba1fd000090a7dd598f0b
@api3/airnode-protocol-v1/contracts/dapis/DapiServer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "../utils/ExtendedMulticall.sol";
import "../whitelist/WhitelistWithManager.sol";
import "../protocol/AirnodeRequester.sol";
import "./Median.sol";
import "./interfaces/IDapiServer.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @title Contract that serves Beacons, Beacon sets and dAPIs based on the
/// Airnode protocol
/// @notice A Beacon is a live data feed addressed by an ID, which is derived
/// from an Airnode address and a template ID. This is suitable where the more
/// recent data point is always more favorable, e.g., in the context of an
/// asset price data feed. Beacons can also be seen as one-Airnode data feeds
/// that can be used individually or combined to build Beacon sets. dAPIs are
/// an abstraction layer over Beacons and Beacon sets.
/// @dev DapiServer is a PSP requester contract. Unlike RRP, which is
/// implemented as a central contract, PSP implementation is built into the
/// requester for optimization. Accordingly, the checks that are not required
/// are omitted. Some examples:
/// - While executing a PSP Beacon update, the condition is not verified
/// because Beacon updates where the condition returns `false` (i.e., the
/// on-chain value is already close to the actual value) are not harmful, and
/// are even desirable.
/// - PSP Beacon set update subscription IDs are not verified, as the
/// Airnode/relayer cannot be made to "misreport a Beacon set update" by
/// spoofing a subscription ID.
/// - While executing a PSP Beacon set update, even the signature is not
/// checked because this is a purely keeper job that does not require off-chain
/// data. Similar to Beacon updates, any Beacon set update is welcome.
contract DapiServer is
ExtendedMulticall,
WhitelistWithManager,
AirnodeRequester,
Median,
IDapiServer
{
using ECDSA for bytes32;
// Airnodes serve their fulfillment data along with timestamps. This
// contract casts the reported data to `int224` and the timestamp to
// `uint32`, which works until year 2106.
struct DataFeed {
int224 value;
uint32 timestamp;
}
/// @notice dAPI name setter role description
string public constant override DAPI_NAME_SETTER_ROLE_DESCRIPTION =
"dAPI name setter";
/// @notice Number that represents 100%
/// @dev 10^8 (and not a larger number) is chosen to avoid overflows in
/// `calculateUpdateInPercentage()`. Since the reported data needs to fit
/// into 224 bits, its multiplication by 10^8 is guaranteed not to
/// overflow.
uint256 public constant override HUNDRED_PERCENT = 1e8;
/// @notice dAPI name setter role
bytes32 public immutable override dapiNameSetterRole;
/// @notice If an account is an unlimited reader
mapping(address => bool) public unlimitedReaderStatus;
/// @notice If a sponsor has permitted an account to request RRP-based
/// updates at this contract
mapping(address => mapping(address => bool))
public
override sponsorToRrpBeaconUpdateRequesterToPermissionStatus;
/// @notice ID of the Beacon that the subscription is registered to update
mapping(bytes32 => bytes32) public override subscriptionIdToBeaconId;
mapping(bytes32 => DataFeed) private dataFeeds;
mapping(bytes32 => bytes32) private requestIdToBeaconId;
mapping(bytes32 => bytes32) private subscriptionIdToHash;
mapping(bytes32 => bytes32) private dapiNameHashToDataFeedId;
/// @dev Reverts if the sender is not permitted to request an RRP-based
/// update with the sponsor and is not the sponsor
/// @param sponsor Sponsor address
modifier onlyPermittedUpdateRequester(address sponsor) {
require(
sponsor == msg.sender ||
sponsorToRrpBeaconUpdateRequesterToPermissionStatus[sponsor][
msg.sender
],
"Sender not permitted"
);
_;
}
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
/// @param _airnodeProtocol AirnodeProtocol contract address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager,
address _airnodeProtocol
)
WhitelistWithManager(
_accessControlRegistry,
_adminRoleDescription,
_manager
)
AirnodeRequester(_airnodeProtocol)
{
dapiNameSetterRole = _deriveRole(
_deriveAdminRole(manager),
keccak256(abi.encodePacked(DAPI_NAME_SETTER_ROLE_DESCRIPTION))
);
}
/// ~~~RRP Beacon updates~~~
/// @notice Called by the sponsor to set the update request permission
/// status of an account
/// @param rrpBeaconUpdateRequester RRP-based Beacon update requester
/// address
/// @param status Permission status
function setRrpBeaconUpdatePermissionStatus(
address rrpBeaconUpdateRequester,
bool status
) external override {
require(
rrpBeaconUpdateRequester != address(0),
"Update requester zero"
);
sponsorToRrpBeaconUpdateRequesterToPermissionStatus[msg.sender][
rrpBeaconUpdateRequester
] = status;
emit SetRrpBeaconUpdatePermissionStatus(
msg.sender,
rrpBeaconUpdateRequester,
status
);
}
/// @notice Creates an RRP requests for the Beacon to be updated
/// @dev In addition to the sponsor sponsoring this contract (by calling
/// `setRrpSponsorshipStatus()`), the sponsor must also give update request
/// permission to the sender (by calling
/// `setRrpBeaconUpdatePermissionStatus()`) before this method is called.
/// The template must specify a single point of data of type `int256` to be
/// returned and for it to be small enough to be castable to `int224`
/// because this is what `fulfillRrpBeaconUpdate()` expects.
/// @param airnode Airnode address
/// @param templateId Template ID
/// @param sponsor Sponsor address
/// @return requestId Request ID
function requestRrpBeaconUpdate(
address airnode,
bytes32 templateId,
address sponsor
)
external
override
onlyPermittedUpdateRequester(sponsor)
returns (bytes32 requestId)
{
bytes32 beaconId = deriveBeaconId(airnode, templateId);
requestId = IAirnodeProtocol(airnodeProtocol).makeRequest(
airnode,
templateId,
"",
sponsor,
this.fulfillRrpBeaconUpdate.selector
);
requestIdToBeaconId[requestId] = beaconId;
emit RequestedRrpBeaconUpdate(
beaconId,
sponsor,
msg.sender,
requestId,
airnode,
templateId
);
}
/// @notice Creates an RRP requests for the Beacon to be updated by the relayer
/// @param airnode Airnode address
/// @param templateId Template ID
/// @param relayer Relayer address
/// @param sponsor Sponsor address
/// @return requestId Request ID
function requestRrpBeaconUpdateRelayed(
address airnode,
bytes32 templateId,
address relayer,
address sponsor
)
external
override
onlyPermittedUpdateRequester(sponsor)
returns (bytes32 requestId)
{
bytes32 beaconId = deriveBeaconId(airnode, templateId);
requestId = IAirnodeProtocol(airnodeProtocol).makeRequestRelayed(
airnode,
templateId,
"",
relayer,
sponsor,
this.fulfillRrpBeaconUpdate.selector
);
requestIdToBeaconId[requestId] = beaconId;
emit RequestedRrpBeaconUpdateRelayed(
beaconId,
sponsor,
msg.sender,
requestId,
airnode,
relayer,
templateId
);
}
/// @notice Called by the Airnode/relayer using the sponsor wallet through
/// AirnodeProtocol to fulfill the request
/// @param requestId Request ID
/// @param timestamp Timestamp used in the signature
/// @param data Fulfillment data (an `int256` encoded in contract ABI)
function fulfillRrpBeaconUpdate(
bytes32 requestId,
uint256 timestamp,
bytes calldata data
) external override onlyAirnodeProtocol onlyValidTimestamp(timestamp) {
bytes32 beaconId = requestIdToBeaconId[requestId];
delete requestIdToBeaconId[requestId];
int256 decodedData = processBeaconUpdate(beaconId, timestamp, data);
emit UpdatedBeaconWithRrp(beaconId, requestId, decodedData, timestamp);
}
/// ~~~PSP Beacon updates~~~
/// @notice Registers the Beacon update subscription
/// @dev Similar to how one needs to call `requestRrpBeaconUpdate()` for
/// this contract to recognize the incoming RRP fulfillment, this needs to
/// be called before the subscription fulfillments.
/// In addition to the subscription being registered, the sponsor must use
/// `setPspSponsorshipStatus()` to give permission for its sponsor wallet
/// to be used for the specific subscription.
/// @param airnode Airnode address
/// @param templateId Template ID
/// @param conditions Conditions under which the subscription is requested
/// to be fulfilled
/// @param relayer Relayer address
/// @param sponsor Sponsor address
/// @return subscriptionId Subscription ID
function registerBeaconUpdateSubscription(
address airnode,
bytes32 templateId,
bytes memory conditions,
address relayer,
address sponsor
) external override returns (bytes32 subscriptionId) {
require(relayer != address(0), "Relayer address zero");
require(sponsor != address(0), "Sponsor address zero");
subscriptionId = keccak256(
abi.encode(
block.chainid,
airnode,
templateId,
"",
conditions,
relayer,
sponsor,
address(this),
this.fulfillPspBeaconUpdate.selector
)
);
subscriptionIdToHash[subscriptionId] = keccak256(
abi.encodePacked(airnode, relayer, sponsor)
);
subscriptionIdToBeaconId[subscriptionId] = deriveBeaconId(
airnode,
templateId
);
emit RegisteredBeaconUpdateSubscription(
subscriptionId,
airnode,
templateId,
"",
conditions,
relayer,
sponsor,
address(this),
this.fulfillPspBeaconUpdate.selector
);
}
/// @notice Returns if the respective Beacon needs to be updated based on
/// the fulfillment data and the condition parameters
/// @dev Reverts if not called by a void signer with zero address because
/// this method can be used to indirectly read a Beacon.
/// `conditionParameters` are specified within the `conditions` field of a
/// Subscription.
/// @param subscriptionId Subscription ID
/// @param data Fulfillment data (an `int256` encoded in contract ABI)
/// @param conditionParameters Subscription condition parameters (a
/// `uint256` encoded in contract ABI)
/// @return If the Beacon update subscription should be fulfilled
function conditionPspBeaconUpdate(
bytes32 subscriptionId,
bytes calldata data,
bytes calldata conditionParameters
) external view override returns (bool) {
require(msg.sender == address(0), "Sender not zero address");
bytes32 beaconId = subscriptionIdToBeaconId[subscriptionId];
require(beaconId != bytes32(0), "Subscription not registered");
DataFeed storage beacon = dataFeeds[beaconId];
return
calculateUpdateInPercentage(
beacon.value,
decodeFulfillmentData(data)
) >=
decodeConditionParameters(conditionParameters) ||
beacon.timestamp == 0;
}
/// @notice Called by the Airnode/relayer using the sponsor wallet to
/// fulfill the Beacon update subscription
/// @dev There is no need to verify that `conditionPspBeaconUpdate()`
/// returns `true` because any Beacon update is a good Beacon update
/// @param subscriptionId Subscription ID
/// @param airnode Airnode address
/// @param relayer Relayer address
/// @param sponsor Sponsor address
/// @param timestamp Timestamp used in the signature
/// @param data Fulfillment data (a single `int256` encoded in contract
/// ABI)
/// @param signature Subscription ID, timestamp, sponsor wallet address
/// (and fulfillment data if the relayer is not the Airnode) signed by the
/// Airnode wallet
function fulfillPspBeaconUpdate(
bytes32 subscriptionId,
address airnode,
address relayer,
address sponsor,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external override onlyValidTimestamp(timestamp) {
require(
subscriptionIdToHash[subscriptionId] ==
keccak256(abi.encodePacked(airnode, relayer, sponsor)),
"Subscription not registered"
);
if (airnode == relayer) {
require(
(
keccak256(
abi.encodePacked(subscriptionId, timestamp, msg.sender)
).toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
} else {
require(
(
keccak256(
abi.encodePacked(
subscriptionId,
timestamp,
msg.sender,
data
)
).toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
}
bytes32 beaconId = subscriptionIdToBeaconId[subscriptionId];
// Beacon ID is guaranteed to not be zero because the subscription is
// registered
int256 decodedData = processBeaconUpdate(beaconId, timestamp, data);
emit UpdatedBeaconWithPsp(
beaconId,
subscriptionId,
int224(decodedData),
uint32(timestamp)
);
}
/// ~~~Signed data Beacon updates~~~
/// @notice Updates a Beacon using data signed by the respective Airnode,
/// without requiring a request or subscription
/// @param airnode Airnode address
/// @param templateId Template ID
/// @param timestamp Timestamp used in the signature
/// @param data Response data (an `int256` encoded in contract ABI)
/// @param signature Template ID, a timestamp and the response data signed
/// by the Airnode address
function updateBeaconWithSignedData(
address airnode,
bytes32 templateId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external override onlyValidTimestamp(timestamp) {
require(
(
keccak256(abi.encodePacked(templateId, timestamp, data))
.toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
bytes32 beaconId = deriveBeaconId(airnode, templateId);
int256 decodedData = processBeaconUpdate(beaconId, timestamp, data);
emit UpdatedBeaconWithSignedData(beaconId, decodedData, timestamp);
}
/// ~~~PSP Beacon set updates~~~
/// @notice Updates the Beacon set using the current values of its Beacons
/// @dev This function still works if some of the IDs in `beaconIds` belong
/// to Beacon sets rather than Beacons. However, this is not the intended
/// use.
/// @param beaconIds Beacon IDs
/// @return beaconSetId Beacon set ID
function updateBeaconSetWithBeacons(bytes32[] memory beaconIds)
public
override
returns (bytes32 beaconSetId)
{
uint256 beaconCount = beaconIds.length;
require(beaconCount > 1, "Specified less than two Beacons");
int256[] memory values = new int256[](beaconCount);
uint256 accumulatedTimestamp = 0;
for (uint256 ind = 0; ind < beaconCount; ind++) {
DataFeed storage dataFeed = dataFeeds[beaconIds[ind]];
values[ind] = dataFeed.value;
accumulatedTimestamp += dataFeed.timestamp;
}
uint32 updatedTimestamp = uint32(accumulatedTimestamp / beaconCount);
beaconSetId = deriveBeaconSetId(beaconIds);
require(
updatedTimestamp >= dataFeeds[beaconSetId].timestamp,
"Updated value outdated"
);
int224 updatedValue = int224(median(values));
dataFeeds[beaconSetId] = DataFeed({
value: updatedValue,
timestamp: updatedTimestamp
});
emit UpdatedBeaconSetWithBeacons(
beaconSetId,
updatedValue,
updatedTimestamp
);
}
/// @notice Updates the Beacon set using the current values of the Beacons
/// and returns if this update was justified according to the deviation
/// threshold
/// @dev This method does not allow the caller to indirectly read a Beacon
/// set, which is why it does not require the sender to be a void signer
/// with zero address. This allows the implementation of incentive
/// mechanisms that rewards keepers that trigger valid dAPI updates.
/// @param beaconIds Beacon IDs
/// @param deviationThresholdInPercentage Deviation threshold in percentage
/// where 100% is represented as `HUNDRED_PERCENT`
function updateBeaconSetWithBeaconsAndReturnCondition(
bytes32[] memory beaconIds,
uint256 deviationThresholdInPercentage
) public override returns (bool) {
bytes32 beaconSetId = deriveBeaconSetId(beaconIds);
DataFeed memory initialBeaconSet = dataFeeds[beaconSetId];
updateBeaconSetWithBeacons(beaconIds);
DataFeed storage updatedBeaconSet = dataFeeds[beaconSetId];
return
calculateUpdateInPercentage(
initialBeaconSet.value,
updatedBeaconSet.value
) >=
deviationThresholdInPercentage ||
(initialBeaconSet.timestamp == 0 && updatedBeaconSet.timestamp > 0);
}
/// @notice Returns if the respective Beacon set needs to be updated based
/// on the condition parameters
/// @dev The template ID used in the respective Subscription is expected to
/// be zero, which means the `parameters` field of the Subscription will be
/// forwarded to this function as `data`. This field should be the Beacon
/// ID array encoded in contract ABI.
/// @param subscriptionId Subscription ID
/// @param data Fulfillment data (array of Beacon IDs, i.e., `bytes32[]`
/// encoded in contract ABI)
/// @param conditionParameters Subscription condition parameters (a
/// `uint256` encoded in contract ABI)
/// @return If the Beacon set update subscription should be fulfilled
function conditionPspBeaconSetUpdate(
bytes32 subscriptionId, // solhint-disable-line no-unused-vars
bytes calldata data,
bytes calldata conditionParameters
) external override returns (bool) {
require(msg.sender == address(0), "Sender not zero address");
bytes32[] memory beaconIds = abi.decode(data, (bytes32[]));
require(
keccak256(abi.encode(beaconIds)) == keccak256(data),
"Data length not correct"
);
return
updateBeaconSetWithBeaconsAndReturnCondition(
beaconIds,
decodeConditionParameters(conditionParameters)
);
}
/// @notice Called by the Airnode/relayer using the sponsor wallet to
/// fulfill the Beacon set update subscription
/// @dev Similar to `conditionPspBeaconSetUpdate()`, if `templateId` of the
/// Subscription is zero, its `parameters` field will be forwarded to
/// `data` here, which is expect to be contract ABI-encoded array of Beacon
/// IDs.
/// It does not make sense for this subscription to be relayed, as there is
/// no external data being delivered. Nevertheless, this is allowed for the
/// lack of a reason to prevent it.
/// Even though the consistency of the arguments are not being checked, if
/// a standard implementation of Airnode is being used, these can be
/// expected to be correct. Either way, the assumption is that it does not
/// matter for the purposes of a Beacon set update subscription.
/// @param subscriptionId Subscription ID
/// @param airnode Airnode address
/// @param relayer Relayer address
/// @param sponsor Sponsor address
/// @param timestamp Timestamp used in the signature
/// @param data Fulfillment data (an `int256` encoded in contract ABI)
/// @param signature Subscription ID, timestamp, sponsor wallet address
/// (and fulfillment data if the relayer is not the Airnode) signed by the
/// Airnode wallet
function fulfillPspBeaconSetUpdate(
bytes32 subscriptionId, // solhint-disable-line no-unused-vars
address airnode, // solhint-disable-line no-unused-vars
address relayer, // solhint-disable-line no-unused-vars
address sponsor, // solhint-disable-line no-unused-vars
uint256 timestamp, // solhint-disable-line no-unused-vars
bytes calldata data,
bytes calldata signature // solhint-disable-line no-unused-vars
) external override {
require(
keccak256(data) ==
updateBeaconSetWithBeacons(abi.decode(data, (bytes32[]))),
"Data length not correct"
);
}
/// ~~~Signed data Beacon set updates~~~
/// @notice Updates a Beacon set using data signed by the respective
/// Airnodes without requiring a request or subscription. The Beacons for
/// which the signature is omitted will be read from the storage.
/// @param airnodes Airnode addresses
/// @param templateIds Template IDs
/// @param timestamps Timestamps used in the signatures
/// @param data Response data (an `int256` encoded in contract ABI per
/// Beacon)
/// @param signatures Template ID, a timestamp and the response data signed
/// by the respective Airnode address per Beacon
/// @return beaconSetId Beacon set ID
function updateBeaconSetWithSignedData(
address[] memory airnodes,
bytes32[] memory templateIds,
uint256[] memory timestamps,
bytes[] memory data,
bytes[] memory signatures
) external override returns (bytes32 beaconSetId) {
uint256 beaconCount = airnodes.length;
require(
beaconCount == templateIds.length &&
beaconCount == timestamps.length &&
beaconCount == data.length &&
beaconCount == signatures.length,
"Parameter length mismatch"
);
require(beaconCount > 1, "Specified less than two Beacons");
bytes32[] memory beaconIds = new bytes32[](beaconCount);
int256[] memory values = new int256[](beaconCount);
uint256 accumulatedTimestamp = 0;
for (uint256 ind = 0; ind < beaconCount; ind++) {
if (signatures[ind].length != 0) {
address airnode = airnodes[ind];
uint256 timestamp = timestamps[ind];
require(timestampIsValid(timestamp), "Timestamp not valid");
require(
(
keccak256(
abi.encodePacked(
templateIds[ind],
timestamp,
data[ind]
)
).toEthSignedMessageHash()
).recover(signatures[ind]) == airnode,
"Signature mismatch"
);
values[ind] = decodeFulfillmentData(data[ind]);
// Timestamp validity is already checked, which means it will
// be small enough to be typecast into `uint32`
accumulatedTimestamp += timestamp;
beaconIds[ind] = deriveBeaconId(airnode, templateIds[ind]);
} else {
bytes32 beaconId = deriveBeaconId(
airnodes[ind],
templateIds[ind]
);
DataFeed storage dataFeed = dataFeeds[beaconId];
values[ind] = dataFeed.value;
accumulatedTimestamp += dataFeed.timestamp;
beaconIds[ind] = beaconId;
}
}
beaconSetId = deriveBeaconSetId(beaconIds);
uint32 updatedTimestamp = uint32(accumulatedTimestamp / beaconCount);
require(
updatedTimestamp >= dataFeeds[beaconSetId].timestamp,
"Updated value outdated"
);
int224 updatedValue = int224(median(values));
dataFeeds[beaconSetId] = DataFeed({
value: updatedValue,
timestamp: updatedTimestamp
});
emit UpdatedBeaconSetWithSignedData(
beaconSetId,
updatedValue,
updatedTimestamp
);
}
/// @notice Called by the manager to add the unlimited reader indefinitely
/// @dev Since the unlimited reader status cannot be revoked, only
/// contracts that are adequately restricted should be given this status
/// @param unlimitedReader Unlimited reader address
function addUnlimitedReader(address unlimitedReader) external override {
require(msg.sender == manager, "Sender not manager");
unlimitedReaderStatus[unlimitedReader] = true;
emit AddedUnlimitedReader(unlimitedReader);
}
/// @notice Sets the data feed ID the dAPI name points to
/// @dev While a data feed ID refers to a specific Beacon or Beacon set,
/// dAPI names provide a more abstract interface for convenience. This
/// means a dAPI name that was pointing to a Beacon can be pointed to a
/// Beacon set, then another Beacon set, etc.
/// @param dapiName Human-readable dAPI name
/// @param dataFeedId Data feed ID the dAPI name will point to
function setDapiName(bytes32 dapiName, bytes32 dataFeedId)
external
override
{
require(dapiName != bytes32(0), "dAPI name zero");
require(
msg.sender == manager ||
IAccessControlRegistry(accessControlRegistry).hasRole(
dapiNameSetterRole,
msg.sender
),
"Sender cannot set dAPI name"
);
dapiNameHashToDataFeedId[
keccak256(abi.encodePacked(dapiName))
] = dataFeedId;
emit SetDapiName(dapiName, dataFeedId, msg.sender);
}
/// @notice Returns the data feed ID the dAPI name is set to
/// @param dapiName dAPI name
/// @return Data feed ID
function dapiNameToDataFeedId(bytes32 dapiName)
external
view
override
returns (bytes32)
{
return dapiNameHashToDataFeedId[keccak256(abi.encodePacked(dapiName))];
}
/// @notice Reads the data feed with ID
/// @param dataFeedId Data feed ID
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function readDataFeedWithId(bytes32 dataFeedId)
external
view
override
returns (int224 value, uint32 timestamp)
{
require(
readerCanReadDataFeed(dataFeedId, msg.sender),
"Sender cannot read"
);
DataFeed storage dataFeed = dataFeeds[dataFeedId];
return (dataFeed.value, dataFeed.timestamp);
}
/// @notice Reads the data feed value with ID
/// @param dataFeedId Data feed ID
/// @return value Data feed value
function readDataFeedValueWithId(bytes32 dataFeedId)
external
view
override
returns (int224 value)
{
require(
readerCanReadDataFeed(dataFeedId, msg.sender),
"Sender cannot read"
);
DataFeed storage dataFeed = dataFeeds[dataFeedId];
require(dataFeed.timestamp != 0, "Data feed does not exist");
return dataFeed.value;
}
/// @notice Reads the data feed with dAPI name
/// @dev The read data feed may belong to a Beacon or dAPI. The reader
/// must be whitelisted for the hash of the dAPI name.
/// @param dapiName dAPI name
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function readDataFeedWithDapiName(bytes32 dapiName)
external
view
override
returns (int224 value, uint32 timestamp)
{
bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
require(
readerCanReadDataFeed(dapiNameHash, msg.sender),
"Sender cannot read"
);
bytes32 dataFeedId = dapiNameHashToDataFeedId[dapiNameHash];
require(dataFeedId != bytes32(0), "dAPI name not set");
DataFeed storage dataFeed = dataFeeds[dataFeedId];
return (dataFeed.value, dataFeed.timestamp);
}
/// @notice Reads the data feed value with dAPI name
/// @param dapiName dAPI name
/// @return value Data feed value
function readDataFeedValueWithDapiName(bytes32 dapiName)
external
view
override
returns (int224 value)
{
bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
require(
readerCanReadDataFeed(dapiNameHash, msg.sender),
"Sender cannot read"
);
DataFeed storage dataFeed = dataFeeds[
dapiNameHashToDataFeedId[dapiNameHash]
];
require(dataFeed.timestamp != 0, "Data feed does not exist");
return dataFeed.value;
}
/// @notice Returns if a reader can read the data feed
/// @param dataFeedId Data feed ID (or dAPI name hash)
/// @param reader Reader address
/// @return If the reader can read the data feed
function readerCanReadDataFeed(bytes32 dataFeedId, address reader)
public
view
override
returns (bool)
{
return
reader == address(0) ||
userIsWhitelisted(dataFeedId, reader) ||
unlimitedReaderStatus[reader];
}
/// @notice Returns the detailed whitelist status of the reader for the
/// data feed
/// @param dataFeedId Data feed ID (or dAPI name hash)
/// @param reader Reader address
/// @return expirationTimestamp Timestamp at which the whitelisting of the
/// reader will expire
/// @return indefiniteWhitelistCount Number of times `reader` was
/// whitelisted indefinitely for `dataFeedId`
function dataFeedIdToReaderToWhitelistStatus(
bytes32 dataFeedId,
address reader
)
external
view
override
returns (uint64 expirationTimestamp, uint192 indefiniteWhitelistCount)
{
WhitelistStatus
storage whitelistStatus = serviceIdToUserToWhitelistStatus[
dataFeedId
][reader];
expirationTimestamp = whitelistStatus.expirationTimestamp;
indefiniteWhitelistCount = whitelistStatus.indefiniteWhitelistCount;
}
/// @notice Returns if an account has indefinitely whitelisted the reader
/// for the data feed
/// @param dataFeedId Data feed ID (or dAPI name hash)
/// @param reader Reader address
/// @param setter Address of the account that has potentially whitelisted
/// the reader for the data feed indefinitely
/// @return indefiniteWhitelistStatus If `setter` has indefinitely
/// whitelisted reader for the data feed
function dataFeedIdToReaderToSetterToIndefiniteWhitelistStatus(
bytes32 dataFeedId,
address reader,
address setter
) external view override returns (bool indefiniteWhitelistStatus) {
indefiniteWhitelistStatus = serviceIdToUserToSetterToIndefiniteWhitelistStatus[
dataFeedId
][reader][setter];
}
/// @notice Derives the Beacon ID from the Airnode address and template ID
/// @param airnode Airnode address
/// @param templateId Template ID
/// @return beaconId Beacon ID
function deriveBeaconId(address airnode, bytes32 templateId)
public
pure
override
returns (bytes32 beaconId)
{
require(airnode != address(0), "Airnode address zero");
require(templateId != bytes32(0), "Template ID zero");
beaconId = keccak256(abi.encodePacked(airnode, templateId));
}
/// @notice Derives the Beacon set ID from the Beacon IDs
/// @dev Notice that `abi.encode()` is used over `abi.encodePacked()`
/// @param beaconIds Beacon IDs
/// @return beaconSetId Beacon set ID
function deriveBeaconSetId(bytes32[] memory beaconIds)
public
pure
override
returns (bytes32 beaconSetId)
{
beaconSetId = keccak256(abi.encode(beaconIds));
}
/// @notice Called privately to process the Beacon update
/// @param beaconId Beacon ID
/// @param timestamp Timestamp used in the signature
/// @param data Fulfillment data (an `int256` encoded in contract ABI)
/// @return updatedBeaconValue Updated Beacon value
function processBeaconUpdate(
bytes32 beaconId,
uint256 timestamp,
bytes calldata data
) private returns (int256 updatedBeaconValue) {
updatedBeaconValue = decodeFulfillmentData(data);
require(
timestamp > dataFeeds[beaconId].timestamp,
"Fulfillment older than Beacon"
);
// Timestamp validity is already checked by `onlyValidTimestamp`, which
// means it will be small enough to be typecast into `uint32`
dataFeeds[beaconId] = DataFeed({
value: int224(updatedBeaconValue),
timestamp: uint32(timestamp)
});
}
/// @notice Called privately to decode the fulfillment data
/// @param data Fulfillment data (an `int256` encoded in contract ABI)
/// @return decodedData Decoded fulfillment data
function decodeFulfillmentData(bytes memory data)
private
pure
returns (int224)
{
require(data.length == 32, "Data length not correct");
int256 decodedData = abi.decode(data, (int256));
require(
decodedData >= type(int224).min && decodedData <= type(int224).max,
"Value typecasting error"
);
return int224(decodedData);
}
/// @notice Called privately to decode the condition parameters
/// @param conditionParameters Condition parameters (a `uint256` encoded in
/// contract ABI)
/// @return deviationThresholdInPercentage Deviation threshold in
/// percentage where 100% is represented as `HUNDRED_PERCENT`
function decodeConditionParameters(bytes calldata conditionParameters)
private
pure
returns (uint256 deviationThresholdInPercentage)
{
require(conditionParameters.length == 32, "Incorrect parameter length");
deviationThresholdInPercentage = abi.decode(
conditionParameters,
(uint256)
);
}
/// @notice Called privately to calculate the update magnitude in
/// percentages where 100% is represented as `HUNDRED_PERCENT`
/// @dev The percentage changes will be more pronounced when the first
/// value is almost zero, which may trigger updates more frequently than
/// wanted. To avoid this, Beacons should be defined in a way that the
/// expected values are not small numbers floating around zero, i.e.,
/// offset and scale.
/// @param initialValue Initial value
/// @param updatedValue Updated value
/// @return updateInPercentage Update in percentage
function calculateUpdateInPercentage(
int224 initialValue,
int224 updatedValue
) private pure returns (uint256 updateInPercentage) {
int256 delta = int256(updatedValue) - int256(initialValue);
uint256 absoluteDelta = delta > 0 ? uint256(delta) : uint256(-delta);
uint256 absoluteInitialValue = initialValue > 0
? uint256(int256(initialValue))
: uint256(-int256(initialValue));
// Avoid division by 0
if (absoluteInitialValue == 0) {
absoluteInitialValue = 1;
}
updateInPercentage =
(absoluteDelta * HUNDRED_PERCENT) /
absoluteInitialValue;
}
}
@api3/airnode-protocol-v1/contracts/protocol/interfaces/IAirnodeRequester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IAirnodeRequester {
function airnodeProtocol() external view returns (address);
}
@openzeppelin/contracts/utils/Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Multicall.sol)
pragma solidity ^0.8.0;
import "./Address.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
abstract contract Multicall {
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
return results;
}
}
@api3/airnode-protocol-v1/contracts/access-control-registry/AccessControlRegistryAdminned.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Multicall.sol";
import "./RoleDeriver.sol";
import "./AccessControlRegistryUser.sol";
import "./interfaces/IAccessControlRegistryAdminned.sol";
/// @title Contract to be inherited by contracts whose adminship functionality
/// will be implemented using AccessControlRegistry
contract AccessControlRegistryAdminned is
Multicall,
RoleDeriver,
AccessControlRegistryUser,
IAccessControlRegistryAdminned
{
/// @notice Admin role description
string public override adminRoleDescription;
bytes32 internal immutable adminRoleDescriptionHash;
/// @dev Contracts deployed with the same admin role descriptions will have
/// the same roles, meaning that granting an account a role will authorize
/// it in multiple contracts. Unless you want your deployed contract to
/// share the role configuration of another contract, use a unique admin
/// role description.
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription
) AccessControlRegistryUser(_accessControlRegistry) {
require(
bytes(_adminRoleDescription).length > 0,
"Admin role description empty"
);
adminRoleDescription = _adminRoleDescription;
adminRoleDescriptionHash = keccak256(
abi.encodePacked(_adminRoleDescription)
);
}
/// @notice Derives the admin role for the specific manager address
/// @param manager Manager address
/// @return adminRole Admin role
function _deriveAdminRole(address manager)
internal
view
returns (bytes32 adminRole)
{
adminRole = _deriveRole(
_deriveRootRole(manager),
adminRoleDescriptionHash
);
}
}
@api3/airnode-protocol-v1/contracts/access-control-registry/AccessControlRegistryAdminnedWithManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AccessControlRegistryAdminned.sol";
import "./interfaces/IAccessControlRegistryAdminnedWithManager.sol";
/// @title Contract to be inherited by contracts with manager whose adminship
/// functionality will be implemented using AccessControlRegistry
/// @notice The manager address here is expected to belong to an
/// AccessControlRegistry user that is a multisig/DAO
contract AccessControlRegistryAdminnedWithManager is
AccessControlRegistryAdminned,
IAccessControlRegistryAdminnedWithManager
{
/// @notice Address of the manager that manages the related
/// AccessControlRegistry roles
/// @dev The mutability of the manager role can be implemented by
/// designating an OwnableCallForwarder contract as the manager. The
/// ownership of this contract can then be transferred, effectively
/// transferring managership.
address public immutable override manager;
/// @notice Admin role
/// @dev Since `manager` is immutable, so is `adminRole`
bytes32 public immutable override adminRole;
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager
)
AccessControlRegistryAdminned(
_accessControlRegistry,
_adminRoleDescription
)
{
require(_manager != address(0), "Manager address zero");
manager = _manager;
adminRole = _deriveAdminRole(_manager);
}
}
@api3/airnode-protocol-v1/contracts/access-control-registry/AccessControlRegistryUser.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IAccessControlRegistry.sol";
import "./interfaces/IAccessControlRegistryUser.sol";
/// @title Contract to be inherited by contracts that will interact with
/// AccessControlRegistry
contract AccessControlRegistryUser is IAccessControlRegistryUser {
/// @notice AccessControlRegistry contract address
address public immutable override accessControlRegistry;
/// @param _accessControlRegistry AccessControlRegistry contract address
constructor(address _accessControlRegistry) {
require(_accessControlRegistry != address(0), "ACR address zero");
accessControlRegistry = _accessControlRegistry;
}
}
@api3/airnode-protocol-v1/contracts/access-control-registry/RoleDeriver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contract to be inherited by contracts that will derive
/// AccessControlRegistry roles
/// @notice If a contract interfaces with AccessControlRegistry and needs to
/// derive roles, it should inherit this contract instead of re-implementing
/// the logic
contract RoleDeriver {
/// @notice Derives the root role of the manager
/// @param manager Manager address
/// @return rootRole Root role
function _deriveRootRole(address manager)
internal
pure
returns (bytes32 rootRole)
{
rootRole = keccak256(abi.encodePacked(manager));
}
/// @notice Derives the role using its admin role and description
/// @dev This implies that roles adminned by the same role cannot have the
/// same description
/// @param adminRole Admin role
/// @param description Human-readable description of the role
/// @return role Role
function _deriveRole(bytes32 adminRole, string memory description)
internal
pure
returns (bytes32 role)
{
role = _deriveRole(adminRole, keccak256(abi.encodePacked(description)));
}
/// @notice Derives the role using its admin role and description hash
/// @dev This implies that roles adminned by the same role cannot have the
/// same description
/// @param adminRole Admin role
/// @param descriptionHash Hash of the human-readable description of the
/// role
/// @return role Role
function _deriveRole(bytes32 adminRole, bytes32 descriptionHash)
internal
pure
returns (bytes32 role)
{
role = keccak256(abi.encodePacked(adminRole, descriptionHash));
}
}
@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/IAccessControl.sol";
interface IAccessControlRegistry is IAccessControl {
event InitializedManager(bytes32 indexed rootRole, address indexed manager);
event InitializedRole(
bytes32 indexed role,
bytes32 indexed adminRole,
string description,
address sender
);
function initializeManager(address manager) external;
function initializeRoleAndGrantToSender(
bytes32 adminRole,
string calldata description
) external returns (bytes32 role);
function deriveRootRole(address manager)
external
pure
returns (bytes32 rootRole);
function deriveRole(bytes32 adminRole, string calldata description)
external
pure
returns (bytes32 role);
}
@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistryAdminned.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IAccessControlRegistryUser.sol";
interface IAccessControlRegistryAdminned is IAccessControlRegistryUser {
function adminRoleDescription() external view returns (string memory);
}
@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistryAdminnedWithManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IAccessControlRegistryAdminned.sol";
interface IAccessControlRegistryAdminnedWithManager is
IAccessControlRegistryAdminned
{
function manager() external view returns (address);
function adminRole() external view returns (bytes32);
}
@api3/airnode-protocol-v1/contracts/access-control-registry/interfaces/IAccessControlRegistryUser.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IAccessControlRegistryUser {
function accessControlRegistry() external view returns (address);
}
@api3/airnode-protocol-v1/contracts/dapis/Median.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Sort.sol";
import "./QuickSelect.sol";
/// @title Contract to be inherited by contracts that will calculate the median
/// of an array
/// @notice The operation will be in-place, i.e., the array provided as the
/// argument will be modified.
contract Median is Sort, Quickselect {
/// @notice Returns the median of the array
/// @dev Uses an unrolled sorting implementation for shorter arrays and
/// quickselect for longer arrays for gas cost efficiency
/// @param array Array whose median is to be calculated
/// @return Median of the array
function median(int256[] memory array) internal pure returns (int256) {
uint256 arrayLength = array.length;
if (arrayLength <= MAX_SORT_LENGTH) {
sort(array);
if (arrayLength % 2 == 1) {
return array[arrayLength / 2];
} else {
return
(array[arrayLength / 2 - 1] + array[arrayLength / 2]) / 2;
}
} else {
if (arrayLength % 2 == 1) {
return array[quickselectK(array, arrayLength / 2)];
} else {
(uint256 mid1, uint256 mid2) = quickselectKPlusOne(
array,
arrayLength / 2 - 1
);
return (array[mid1] + array[mid2]) / 2;
}
}
}
}
@api3/airnode-protocol-v1/contracts/dapis/QuickSelect.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contract to be inherited by contracts that will calculate the index
/// of the k-th and optionally (k+1)-th largest elements in the array
/// @notice Uses quickselect, which operates in-place, i.e., the array provided
/// as the argument will be modified.
contract Quickselect {
/// @notice Returns the index of the k-th largest element in the array
/// @param array Array in which k-th largest element will be searched
/// @param k K
/// @return indK Index of the k-th largest element
function quickselectK(int256[] memory array, uint256 k)
internal
pure
returns (uint256 indK)
{
(indK, ) = quickselect(array, 0, array.length - 1, k, false);
}
/// @notice Returns the index of the k-th and (k+1)-th largest elements in
/// the array
/// @param array Array in which k-th and (k+1)-th largest elements will be
/// searched
/// @param k K
/// @return indK Index of the k-th largest element
/// @return indKPlusOne Index of the (k+1)-th largest element
function quickselectKPlusOne(int256[] memory array, uint256 k)
internal
pure
returns (uint256 indK, uint256 indKPlusOne)
{
uint256 arrayLength = array.length;
require(arrayLength > 1, "Array too short to select k+1");
return quickselect(array, 0, arrayLength - 1, k, true);
}
/// @notice Returns the index of the k-th largest element in the specified
/// section of the (potentially unsorted) array
/// @param array Array in which K will be searched for
/// @param lo Starting index of the section of the array that K will be
/// searched in
/// @param hi Last index of the section of the array that K will be
/// searched in
/// @param k K
/// @param selectKPlusOne If the index of the (k+1)-th largest element is
/// to be returned
/// @return indK Index of the k-th largest element
/// @return indKPlusOne Index of the (k+1)-th largest element (only set if
/// `selectKPlusOne` is `true`)
function quickselect(
int256[] memory array,
uint256 lo,
uint256 hi,
uint256 k,
bool selectKPlusOne
) private pure returns (uint256 indK, uint256 indKPlusOne) {
if (lo == hi) {
return (k, 0);
}
uint256 indPivot = partition(array, lo, hi);
if (k < indPivot) {
(indK, ) = quickselect(array, lo, indPivot - 1, k, false);
} else if (k > indPivot) {
(indK, ) = quickselect(array, indPivot + 1, hi, k, false);
} else {
indK = indPivot;
}
// Since Quickselect ends in the array being partitioned around the
// k-th largest element, we can continue searching towards right for
// the (k+1)-th largest element, which is useful in calculating the
// median of an array with even length
if (selectKPlusOne) {
indKPlusOne = indK + 1;
for (uint256 i = indKPlusOne + 1; i < array.length; i++) {
if (array[i] < array[indKPlusOne]) {
indKPlusOne = i;
}
}
}
}
/// @notice Partitions the array into two around a pivot
/// @param array Array that will be partitioned
/// @param lo Starting index of the section of the array that will be
/// partitioned
/// @param hi Last index of the section of the array that will be
/// partitioned
/// @return pivotInd Pivot index
function partition(
int256[] memory array,
uint256 lo,
uint256 hi
) private pure returns (uint256 pivotInd) {
if (lo == hi) {
return lo;
}
int256 pivot = array[lo];
uint256 i = lo;
pivotInd = hi + 1;
while (true) {
do {
i++;
} while (i < array.length && array[i] < pivot);
do {
pivotInd--;
} while (array[pivotInd] > pivot);
if (i >= pivotInd) {
(array[lo], array[pivotInd]) = (array[pivotInd], array[lo]);
return pivotInd;
}
(array[i], array[pivotInd]) = (array[pivotInd], array[i]);
}
}
}
@api3/airnode-protocol-v1/contracts/dapis/Sort.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contract to be inherited by contracts that will sort an array using
/// an unrolled implementation
/// @notice The operation will be in-place, i.e., the array provided as the
/// argument will be modified.
contract Sort {
uint256 internal constant MAX_SORT_LENGTH = 9;
/// @notice Sorts the array
/// @param array Array to be sorted
function sort(int256[] memory array) internal pure {
uint256 arrayLength = array.length;
require(arrayLength <= MAX_SORT_LENGTH, "Array too long to sort");
// Do a binary search
if (arrayLength < 6) {
// Possible lengths: 1, 2, 3, 4, 5
if (arrayLength < 4) {
// Possible lengths: 1, 2, 3
if (arrayLength == 3) {
// Length: 3
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 0, 1);
} else if (arrayLength == 2) {
// Length: 2
swapIfFirstIsLarger(array, 0, 1);
}
// Do nothing for Length: 1
} else {
// Possible lengths: 4, 5
if (arrayLength == 5) {
// Length: 5
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 3, 4);
swapIfFirstIsLarger(array, 1, 3);
swapIfFirstIsLarger(array, 0, 2);
swapIfFirstIsLarger(array, 2, 4);
swapIfFirstIsLarger(array, 0, 3);
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 1, 2);
} else {
// Length: 4
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 1, 3);
swapIfFirstIsLarger(array, 0, 2);
swapIfFirstIsLarger(array, 1, 2);
}
}
} else {
// Possible lengths: 6, 7, 8, 9
if (arrayLength < 8) {
// Possible lengths: 6, 7
if (arrayLength == 7) {
// Length: 7
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 3, 4);
swapIfFirstIsLarger(array, 5, 6);
swapIfFirstIsLarger(array, 0, 2);
swapIfFirstIsLarger(array, 4, 6);
swapIfFirstIsLarger(array, 3, 5);
swapIfFirstIsLarger(array, 2, 6);
swapIfFirstIsLarger(array, 1, 5);
swapIfFirstIsLarger(array, 0, 4);
swapIfFirstIsLarger(array, 2, 5);
swapIfFirstIsLarger(array, 0, 3);
swapIfFirstIsLarger(array, 2, 4);
swapIfFirstIsLarger(array, 1, 3);
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 4, 5);
} else {
// Length: 6
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 4, 5);
swapIfFirstIsLarger(array, 1, 3);
swapIfFirstIsLarger(array, 3, 5);
swapIfFirstIsLarger(array, 1, 3);
swapIfFirstIsLarger(array, 2, 4);
swapIfFirstIsLarger(array, 0, 2);
swapIfFirstIsLarger(array, 2, 4);
swapIfFirstIsLarger(array, 3, 4);
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 2, 3);
}
} else {
// Possible lengths: 8, 9
if (arrayLength == 9) {
// Length: 9
swapIfFirstIsLarger(array, 1, 8);
swapIfFirstIsLarger(array, 2, 7);
swapIfFirstIsLarger(array, 3, 6);
swapIfFirstIsLarger(array, 4, 5);
swapIfFirstIsLarger(array, 1, 4);
swapIfFirstIsLarger(array, 5, 8);
swapIfFirstIsLarger(array, 0, 2);
swapIfFirstIsLarger(array, 6, 7);
swapIfFirstIsLarger(array, 2, 6);
swapIfFirstIsLarger(array, 7, 8);
swapIfFirstIsLarger(array, 0, 3);
swapIfFirstIsLarger(array, 4, 5);
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 3, 5);
swapIfFirstIsLarger(array, 6, 7);
swapIfFirstIsLarger(array, 2, 4);
swapIfFirstIsLarger(array, 1, 3);
swapIfFirstIsLarger(array, 5, 7);
swapIfFirstIsLarger(array, 4, 6);
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 3, 4);
swapIfFirstIsLarger(array, 5, 6);
swapIfFirstIsLarger(array, 7, 8);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 4, 5);
} else {
// Length: 8
swapIfFirstIsLarger(array, 0, 7);
swapIfFirstIsLarger(array, 1, 6);
swapIfFirstIsLarger(array, 2, 5);
swapIfFirstIsLarger(array, 3, 4);
swapIfFirstIsLarger(array, 0, 3);
swapIfFirstIsLarger(array, 4, 7);
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 5, 6);
swapIfFirstIsLarger(array, 0, 1);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 4, 5);
swapIfFirstIsLarger(array, 6, 7);
swapIfFirstIsLarger(array, 3, 5);
swapIfFirstIsLarger(array, 2, 4);
swapIfFirstIsLarger(array, 1, 2);
swapIfFirstIsLarger(array, 3, 4);
swapIfFirstIsLarger(array, 5, 6);
swapIfFirstIsLarger(array, 2, 3);
swapIfFirstIsLarger(array, 4, 5);
swapIfFirstIsLarger(array, 3, 4);
}
}
}
}
/// @notice Swaps two elements of an array if the first element is greater
/// than the second
/// @param array Array whose elements are to be swapped
/// @param ind1 Index of the first element
/// @param ind2 Index of the second element
function swapIfFirstIsLarger(
int256[] memory array,
uint256 ind1,
uint256 ind2
) private pure {
if (array[ind1] > array[ind2]) {
(array[ind1], array[ind2]) = (array[ind2], array[ind1]);
}
}
}
@api3/airnode-protocol-v1/contracts/dapis/interfaces/IDapiServer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../protocol/interfaces/IAirnodeRequester.sol";
interface IDapiServer is IAirnodeRequester {
event SetRrpBeaconUpdatePermissionStatus(
address indexed sponsor,
address indexed rrpBeaconUpdateRequester,
bool status
);
event RequestedRrpBeaconUpdate(
bytes32 indexed beaconId,
address indexed sponsor,
address indexed requester,
bytes32 requestId,
address airnode,
bytes32 templateId
);
event RequestedRrpBeaconUpdateRelayed(
bytes32 indexed beaconId,
address indexed sponsor,
address indexed requester,
bytes32 requestId,
address airnode,
address relayer,
bytes32 templateId
);
event UpdatedBeaconWithRrp(
bytes32 indexed beaconId,
bytes32 requestId,
int256 value,
uint256 timestamp
);
event RegisteredBeaconUpdateSubscription(
bytes32 indexed subscriptionId,
address airnode,
bytes32 templateId,
bytes parameters,
bytes conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
);
event UpdatedBeaconWithPsp(
bytes32 indexed beaconId,
bytes32 subscriptionId,
int224 value,
uint32 timestamp
);
event UpdatedBeaconWithSignedData(
bytes32 indexed beaconId,
int256 value,
uint256 timestamp
);
event UpdatedBeaconSetWithBeacons(
bytes32 indexed beaconSetId,
int224 value,
uint32 timestamp
);
event UpdatedBeaconSetWithSignedData(
bytes32 indexed dapiId,
int224 value,
uint32 timestamp
);
event AddedUnlimitedReader(address indexed unlimitedReader);
event SetDapiName(
bytes32 indexed dapiName,
bytes32 dataFeedId,
address indexed sender
);
function setRrpBeaconUpdatePermissionStatus(
address rrpBeaconUpdateRequester,
bool status
) external;
function requestRrpBeaconUpdate(
address airnode,
bytes32 templateId,
address sponsor
) external returns (bytes32 requestId);
function requestRrpBeaconUpdateRelayed(
address airnode,
bytes32 templateId,
address relayer,
address sponsor
) external returns (bytes32 requestId);
function fulfillRrpBeaconUpdate(
bytes32 requestId,
uint256 timestamp,
bytes calldata data
) external;
function registerBeaconUpdateSubscription(
address airnode,
bytes32 templateId,
bytes memory conditions,
address relayer,
address sponsor
) external returns (bytes32 subscriptionId);
function conditionPspBeaconUpdate(
bytes32 subscriptionId,
bytes calldata data,
bytes calldata conditionParameters
) external view returns (bool);
function fulfillPspBeaconUpdate(
bytes32 subscriptionId,
address airnode,
address relayer,
address sponsor,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external;
function updateBeaconWithSignedData(
address airnode,
bytes32 beaconId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external;
function updateBeaconSetWithBeacons(bytes32[] memory beaconIds)
external
returns (bytes32 beaconSetId);
function updateBeaconSetWithBeaconsAndReturnCondition(
bytes32[] memory beaconIds,
uint256 updateThresholdInPercentage
) external returns (bool);
function conditionPspBeaconSetUpdate(
bytes32 subscriptionId,
bytes calldata data,
bytes calldata conditionParameters
) external returns (bool);
function fulfillPspBeaconSetUpdate(
bytes32 subscriptionId,
address airnode,
address relayer,
address sponsor,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external;
function updateBeaconSetWithSignedData(
address[] memory airnodes,
bytes32[] memory templateIds,
uint256[] memory timestamps,
bytes[] memory data,
bytes[] memory signatures
) external returns (bytes32 beaconSetId);
function addUnlimitedReader(address unlimitedReader) external;
function setDapiName(bytes32 dapiName, bytes32 dataFeedId) external;
function dapiNameToDataFeedId(bytes32 dapiName)
external
view
returns (bytes32);
function readDataFeedWithId(bytes32 dataFeedId)
external
view
returns (int224 value, uint32 timestamp);
function readDataFeedValueWithId(bytes32 dataFeedId)
external
view
returns (int224 value);
function readDataFeedWithDapiName(bytes32 dapiName)
external
view
returns (int224 value, uint32 timestamp);
function readDataFeedValueWithDapiName(bytes32 dapiName)
external
view
returns (int224 value);
function readerCanReadDataFeed(bytes32 dataFeedId, address reader)
external
view
returns (bool);
function dataFeedIdToReaderToWhitelistStatus(
bytes32 dataFeedId,
address reader
)
external
view
returns (uint64 expirationTimestamp, uint192 indefiniteWhitelistCount);
function dataFeedIdToReaderToSetterToIndefiniteWhitelistStatus(
bytes32 dataFeedId,
address reader,
address setter
) external view returns (bool indefiniteWhitelistStatus);
function deriveBeaconId(address airnode, bytes32 templateId)
external
pure
returns (bytes32 beaconId);
function deriveBeaconSetId(bytes32[] memory beaconIds)
external
pure
returns (bytes32 beaconSetId);
// solhint-disable-next-line func-name-mixedcase
function DAPI_NAME_SETTER_ROLE_DESCRIPTION()
external
view
returns (string memory);
// solhint-disable-next-line func-name-mixedcase
function HUNDRED_PERCENT() external view returns (uint256);
function dapiNameSetterRole() external view returns (bytes32);
function sponsorToRrpBeaconUpdateRequesterToPermissionStatus(
address sponsor,
address updateRequester
) external view returns (bool);
function subscriptionIdToBeaconId(bytes32 subscriptionId)
external
view
returns (bytes32);
}
@api3/airnode-protocol-v1/contracts/protocol/AirnodeRequester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IAirnodeProtocol.sol";
import "./interfaces/IAirnodeRequester.sol";
/// @title Contract to be inherited by contracts that will make Airnode
/// requests and receive fulfillments
contract AirnodeRequester is IAirnodeRequester {
/// @notice AirnodeProtocol contract address
address public immutable override airnodeProtocol;
/// @dev Reverts if the sender is not the AirnodeProtocol contract. Use
/// this modifier with methods that are meant to receive RRP fulfillments.
modifier onlyAirnodeProtocol() {
require(
msg.sender == address(airnodeProtocol),
"Sender not Airnode protocol"
);
_;
}
/// @dev Reverts if the timestamp is not valid. Use this modifier with
/// methods that are meant to receive RRP and PSP fulfillments.
/// @param timestamp Timestamp used in the signature
modifier onlyValidTimestamp(uint256 timestamp) {
require(timestampIsValid(timestamp), "Timestamp not valid");
_;
}
/// @param _airnodeProtocol AirnodeProtocol contract address
constructor(address _airnodeProtocol) {
require(_airnodeProtocol != address(0), "AirnodeProtocol address zero");
airnodeProtocol = _airnodeProtocol;
}
/// @notice Returns if the timestamp used in the signature is valid
/// @dev Returns `false` if the timestamp is not at most 1 hour old to
/// prevent replays. Returns `false` if the timestamp is not from the past,
/// with some leeway to accomodate for some benign time drift. These values
/// are appropriate in most cases, but you can adjust them if you are aware
/// of the implications.
/// @param timestamp Timestamp used in the signature
function timestampIsValid(uint256 timestamp) internal view returns (bool) {
return
timestamp + 1 hours > block.timestamp &&
timestamp < block.timestamp + 15 minutes;
}
}
@api3/airnode-protocol-v1/contracts/protocol/interfaces/IAirnodeProtocol.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IStorageUtils.sol";
import "./ISponsorshipUtils.sol";
import "./IWithdrawalUtils.sol";
interface IAirnodeProtocol is
IStorageUtils,
ISponsorshipUtils,
IWithdrawalUtils
{
event MadeRequest(
address indexed airnode,
bytes32 indexed requestId,
address requester,
uint256 requesterRequestCount,
bytes32 templateId,
bytes parameters,
address sponsor,
bytes4 fulfillFunctionId
);
event FulfilledRequest(
address indexed airnode,
bytes32 indexed requestId,
uint256 timestamp,
bytes data
);
event FailedRequest(
address indexed airnode,
bytes32 indexed requestId,
uint256 timestamp,
string errorMessage
);
event MadeRequestRelayed(
address indexed relayer,
bytes32 indexed requestId,
address indexed airnode,
address requester,
uint256 requesterRequestCount,
bytes32 templateId,
bytes parameters,
address sponsor,
bytes4 fulfillFunctionId
);
event FulfilledRequestRelayed(
address indexed relayer,
bytes32 indexed requestId,
address indexed airnode,
uint256 timestamp,
bytes data
);
event FailedRequestRelayed(
address indexed relayer,
bytes32 indexed requestId,
address indexed airnode,
uint256 timestamp,
string errorMessage
);
function makeRequest(
address airnode,
bytes32 templateId,
bytes calldata parameters,
address sponsor,
bytes4 fulfillFunctionId
) external returns (bytes32 requestId);
function fulfillRequest(
bytes32 requestId,
address airnode,
address requester,
bytes4 fulfillFunctionId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external returns (bool callSuccess, bytes memory callData);
function failRequest(
bytes32 requestId,
address airnode,
address requester,
bytes4 fulfillFunctionId,
uint256 timestamp,
string calldata errorMessage,
bytes calldata signature
) external;
function makeRequestRelayed(
address airnode,
bytes32 templateId,
bytes calldata parameters,
address relayer,
address sponsor,
bytes4 fulfillFunctionId
) external returns (bytes32 requestId);
function fulfillRequestRelayed(
bytes32 requestId,
address airnode,
address requester,
address relayer,
bytes4 fulfillFunctionId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external returns (bool callSuccess, bytes memory callData);
function failRequestRelayed(
bytes32 requestId,
address airnode,
address requester,
address relayer,
bytes4 fulfillFunctionId,
uint256 timestamp,
string calldata errorMessage,
bytes calldata signature
) external;
function requestIsAwaitingFulfillment(bytes32 requestId)
external
view
returns (bool);
function requesterToRequestCount(address requester)
external
view
returns (uint256);
}
@api3/airnode-protocol-v1/contracts/protocol/interfaces/ISponsorshipUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISponsorshipUtils {
event SetRrpSponsorshipStatus(
address indexed sponsor,
address indexed requester,
bool status
);
event SetPspSponsorshipStatus(
address indexed sponsor,
bytes32 indexed subscriptionId,
bool status
);
function setRrpSponsorshipStatus(address requester, bool status) external;
function setPspSponsorshipStatus(bytes32 subscriptionId, bool status)
external;
function sponsorToRequesterToRrpSponsorshipStatus(
address sponsor,
address requester
) external view returns (bool status);
function sponsorToSubscriptionIdToPspSponsorshipStatus(
address sponsor,
bytes32 subscriptionId
) external view returns (bool status);
}
@api3/airnode-protocol-v1/contracts/protocol/interfaces/IStorageUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IStorageUtils {
event StoredTemplate(
bytes32 indexed templateId,
bytes32 endpointId,
bytes parameters
);
event StoredSubscription(
bytes32 indexed subscriptionId,
uint256 chainId,
address airnode,
bytes32 templateId,
bytes parameters,
bytes conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
);
function storeTemplate(bytes32 endpointId, bytes calldata parameters)
external
returns (bytes32 templateId);
function storeSubscription(
uint256 chainId,
address airnode,
bytes32 templateId,
bytes calldata parameters,
bytes calldata conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
) external returns (bytes32 subscriptionId);
// solhint-disable-next-line func-name-mixedcase
function MAXIMUM_PARAMETER_LENGTH() external view returns (uint256);
function templates(bytes32 templateId)
external
view
returns (bytes32 endpointId, bytes memory parameters);
function subscriptions(bytes32 subscriptionId)
external
view
returns (
uint256 chainId,
address airnode,
bytes32 templateId,
bytes memory parameters,
bytes memory conditions,
address relayer,
address sponsor,
address requester,
bytes4 fulfillFunctionId
);
}
@api3/airnode-protocol-v1/contracts/protocol/interfaces/IWithdrawalUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IWithdrawalUtils {
event RequestedWithdrawal(
address indexed airnodeOrRelayer,
address indexed sponsor,
bytes32 indexed withdrawalRequestId,
uint256 protocolId
);
event FulfilledWithdrawal(
address indexed airnodeOrRelayer,
address indexed sponsor,
bytes32 indexed withdrawalRequestId,
uint256 protocolId,
address sponsorWallet,
uint256 amount
);
event ClaimedBalance(address indexed sponsor, uint256 amount);
function requestWithdrawal(address airnodeOrRelayer, uint256 protocolId)
external;
function fulfillWithdrawal(
bytes32 withdrawalRequestId,
address airnodeOrRelayer,
uint256 protocolId,
address sponsor,
uint256 timestamp,
bytes calldata signature
) external payable;
function claimBalance() external;
function withdrawalRequestIsAwaitingFulfillment(bytes32 withdrawalRequestId)
external
view
returns (bool);
function sponsorToBalance(address sponsor) external view returns (uint256);
function sponsorToWithdrawalRequestCount(address sponsor)
external
view
returns (uint256);
}
@api3/airnode-protocol-v1/contracts/utils/ExtendedMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Multicall.sol";
/// @notice Contract that extends the functionality of Multicall to cover the
/// retrieval of some globally available variables
contract ExtendedMulticall is Multicall {
/// @notice Returns the chain ID
/// @return Chain ID
function getChainId() external view returns (uint256) {
return block.chainid;
}
/// @notice Returns the account balance
/// @param account Account address
/// @return Account balance
function getBalance(address account) external view returns (uint256) {
return account.balance;
}
/// @notice Returns the current block number
/// @return Current block number
function getBlockNumber() external view returns (uint256) {
return block.number;
}
/// @notice Returns the current block timestamp
/// @return Current block timestamp
function getBlockTimestamp() external view returns (uint256) {
return block.timestamp;
}
/// @notice Returns the current block basefee
/// @return Current block basefee
function getBlockBasefee() external view returns (uint256) {
return block.basefee;
}
}
@api3/airnode-protocol-v1/contracts/whitelist/Whitelist.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contract to be inherited by contracts that need temporary and
/// permanent whitelists for services identified by hashes
/// @notice This contract implements two kinds of whitelisting:
/// (1) Temporary, ends when the expiration timestamp is in the past
/// (2) Indefinite, ends when the indefinite whitelist count is zero
/// Multiple senders can indefinitely whitelist/unwhitelist independently. The
/// user will be considered whitelisted as long as there is at least one active
/// indefinite whitelisting.
/// @dev The interface of this contract is not implemented. It should be
/// inherited and its functions should be exposed with a sort of an
/// authorization scheme.
contract Whitelist {
struct WhitelistStatus {
uint64 expirationTimestamp;
uint192 indefiniteWhitelistCount;
}
mapping(bytes32 => mapping(address => WhitelistStatus))
internal serviceIdToUserToWhitelistStatus;
mapping(bytes32 => mapping(address => mapping(address => bool)))
internal serviceIdToUserToSetterToIndefiniteWhitelistStatus;
/// @notice Extends the expiration of the temporary whitelist of the user
/// for the service
/// @param serviceId Service ID
/// @param user User address
/// @param expirationTimestamp Timestamp at which the temporary whitelist
/// will expire
function _extendWhitelistExpiration(
bytes32 serviceId,
address user,
uint64 expirationTimestamp
) internal {
require(
expirationTimestamp >
serviceIdToUserToWhitelistStatus[serviceId][user]
.expirationTimestamp,
"Does not extend expiration"
);
serviceIdToUserToWhitelistStatus[serviceId][user]
.expirationTimestamp = expirationTimestamp;
}
/// @notice Sets the expiration of the temporary whitelist of the user for
/// the service
/// @dev Unlike `extendWhitelistExpiration()`, this can hasten expiration
/// @param serviceId Service ID
/// @param user User address
/// @param expirationTimestamp Timestamp at which the temporary whitelist
/// will expire
function _setWhitelistExpiration(
bytes32 serviceId,
address user,
uint64 expirationTimestamp
) internal {
serviceIdToUserToWhitelistStatus[serviceId][user]
.expirationTimestamp = expirationTimestamp;
}
/// @notice Sets the indefinite whitelist status of the user for the
/// service
/// @dev As long as at least there is at least one account that has set the
/// indefinite whitelist status of the user for the service as true, the
/// user will be considered whitelisted
/// @param serviceId Service ID
/// @param user User address
/// @param status Indefinite whitelist status
function _setIndefiniteWhitelistStatus(
bytes32 serviceId,
address user,
bool status
) internal returns (uint192 indefiniteWhitelistCount) {
indefiniteWhitelistCount = serviceIdToUserToWhitelistStatus[serviceId][
user
].indefiniteWhitelistCount;
if (
status &&
!serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][
user
][msg.sender]
) {
serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
msg.sender
] = true;
indefiniteWhitelistCount++;
serviceIdToUserToWhitelistStatus[serviceId][user]
.indefiniteWhitelistCount = indefiniteWhitelistCount;
} else if (
!status &&
serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
msg.sender
]
) {
serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
msg.sender
] = false;
indefiniteWhitelistCount--;
serviceIdToUserToWhitelistStatus[serviceId][user]
.indefiniteWhitelistCount = indefiniteWhitelistCount;
}
}
/// @notice Revokes the indefinite whitelist status granted to the user for
/// the service by a specific account
/// @param serviceId Service ID
/// @param user User address
/// @param setter Setter of the indefinite whitelist status
function _revokeIndefiniteWhitelistStatus(
bytes32 serviceId,
address user,
address setter
) internal returns (bool revoked, uint192 indefiniteWhitelistCount) {
indefiniteWhitelistCount = serviceIdToUserToWhitelistStatus[serviceId][
user
].indefiniteWhitelistCount;
if (
serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
setter
]
) {
serviceIdToUserToSetterToIndefiniteWhitelistStatus[serviceId][user][
setter
] = false;
indefiniteWhitelistCount--;
serviceIdToUserToWhitelistStatus[serviceId][user]
.indefiniteWhitelistCount = indefiniteWhitelistCount;
revoked = true;
}
}
/// @notice Returns if the user is whitelised to use the service
/// @param serviceId Service ID
/// @param user User address
/// @return isWhitelisted If the user is whitelisted
function userIsWhitelisted(bytes32 serviceId, address user)
internal
view
returns (bool isWhitelisted)
{
WhitelistStatus
storage whitelistStatus = serviceIdToUserToWhitelistStatus[
serviceId
][user];
return
whitelistStatus.indefiniteWhitelistCount > 0 ||
whitelistStatus.expirationTimestamp > block.timestamp;
}
}
@api3/airnode-protocol-v1/contracts/whitelist/WhitelistRoles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IWhitelistRoles.sol";
/// @title Contract to be inherited by Whitelist contracts that will use
/// generic AccessControlRegistry roles
contract WhitelistRoles is IWhitelistRoles {
// There are four roles implemented in this contract:
// Root
// └── (1) Admin (can grant and revoke the roles below)
// ├── (2) Whitelist expiration extender
// ├── (3) Whitelist expiration setter
// └── (4) Indefinite whitelister
// Their IDs are derived from the descriptions below. Refer to
// AccessControlRegistry for more information.
// To clarify, the root role of the manager is the admin of (1), while (1)
// is the admin of (2), (3) and (4). So (1) is more of a "contract admin",
// while the `adminRole` used in AccessControl and AccessControlRegistry
// refers to a more general adminship relationship between roles.
/// @notice Whitelist expiration extender role description
string
public constant
override WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION =
"Whitelist expiration extender";
/// @notice Whitelist expiration setter role description
string
public constant
override WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION =
"Whitelist expiration setter";
/// @notice Indefinite whitelister role description
string public constant override INDEFINITE_WHITELISTER_ROLE_DESCRIPTION =
"Indefinite whitelister";
bytes32
internal constant WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION_HASH =
keccak256(
abi.encodePacked(WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION)
);
bytes32
internal constant WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION_HASH =
keccak256(
abi.encodePacked(WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION)
);
bytes32 internal constant INDEFINITE_WHITELISTER_ROLE_DESCRIPTION_HASH =
keccak256(abi.encodePacked(INDEFINITE_WHITELISTER_ROLE_DESCRIPTION));
}
@api3/airnode-protocol-v1/contracts/whitelist/WhitelistRolesWithManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./WhitelistRoles.sol";
import "../access-control-registry/AccessControlRegistryAdminnedWithManager.sol";
import "./interfaces/IWhitelistRolesWithManager.sol";
import "../access-control-registry/interfaces/IAccessControlRegistry.sol";
/// @title Contract to be inherited by Whitelist contracts that will use
/// roles where there is a single manager
contract WhitelistRolesWithManager is
WhitelistRoles,
AccessControlRegistryAdminnedWithManager,
IWhitelistRolesWithManager
{
// Since there will be a single manager, we can derive the roles beforehand
/// @notice Whitelist expiration extender role
bytes32 public immutable override whitelistExpirationExtenderRole;
/// @notice Whitelist expiration setter role
bytes32 public immutable override whitelistExpirationSetterRole;
/// @notice Indefinite whitelister role
bytes32 public immutable override indefiniteWhitelisterRole;
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager
)
AccessControlRegistryAdminnedWithManager(
_accessControlRegistry,
_adminRoleDescription,
_manager
)
{
whitelistExpirationExtenderRole = _deriveRole(
adminRole,
WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION_HASH
);
whitelistExpirationSetterRole = _deriveRole(
adminRole,
WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION_HASH
);
indefiniteWhitelisterRole = _deriveRole(
adminRole,
INDEFINITE_WHITELISTER_ROLE_DESCRIPTION_HASH
);
}
/// @dev Returns if the account has the whitelist expiration extender role
/// or is the manager
/// @param account Account address
/// @return If the account has the whitelist extender role or is the
/// manager
function hasWhitelistExpirationExtenderRoleOrIsManager(address account)
internal
view
returns (bool)
{
return
manager == account ||
IAccessControlRegistry(accessControlRegistry).hasRole(
whitelistExpirationExtenderRole,
account
);
}
/// @dev Returns if the account has the whitelist expriation setter role or
/// is the manager
/// @param account Account address
/// @return If the account has the whitelist setter role or is the
/// manager
function hasWhitelistExpirationSetterRoleOrIsManager(address account)
internal
view
returns (bool)
{
return
manager == account ||
IAccessControlRegistry(accessControlRegistry).hasRole(
whitelistExpirationSetterRole,
account
);
}
/// @dev Returns if the account has the indefinite whitelister role or is the
/// manager
/// @param account Account address
/// @return If the account has the indefinite whitelister role or is the
/// manager
function hasIndefiniteWhitelisterRoleOrIsManager(address account)
internal
view
returns (bool)
{
return
manager == account ||
IAccessControlRegistry(accessControlRegistry).hasRole(
indefiniteWhitelisterRole,
account
);
}
}
@api3/airnode-protocol-v1/contracts/whitelist/WhitelistWithManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Whitelist.sol";
import "./WhitelistRolesWithManager.sol";
import "./interfaces/IWhitelistWithManager.sol";
/// @title Contract to be inherited by Whitelist contracts that are controlled
/// by a manager
contract WhitelistWithManager is
Whitelist,
WhitelistRolesWithManager,
IWhitelistWithManager
{
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager
)
WhitelistRolesWithManager(
_accessControlRegistry,
_adminRoleDescription,
_manager
)
{}
/// @notice Extends the expiration of the temporary whitelist of `user` to
/// be able to use the service with `serviceId` if the sender has the
/// whitelist expiration extender role
/// @param serviceId Service ID
/// @param user User address
/// @param expirationTimestamp Timestamp at which the temporary whitelist
/// will expire
function extendWhitelistExpiration(
bytes32 serviceId,
address user,
uint64 expirationTimestamp
) external override {
require(
hasWhitelistExpirationExtenderRoleOrIsManager(msg.sender),
"Cannot extend expiration"
);
require(serviceId != bytes32(0), "Service ID zero");
require(user != address(0), "User address zero");
_extendWhitelistExpiration(serviceId, user, expirationTimestamp);
emit ExtendedWhitelistExpiration(
serviceId,
user,
msg.sender,
expirationTimestamp
);
}
/// @notice Sets the expiration of the temporary whitelist of `user` to be
/// able to use the service with `serviceId` if the sender has the
/// whitelist expiration setter role
/// @param serviceId Service ID
/// @param user User address
/// @param expirationTimestamp Timestamp at which the temporary whitelist
/// will expire
function setWhitelistExpiration(
bytes32 serviceId,
address user,
uint64 expirationTimestamp
) external override {
require(
hasWhitelistExpirationSetterRoleOrIsManager(msg.sender),
"Cannot set expiration"
);
require(serviceId != bytes32(0), "Service ID zero");
require(user != address(0), "User address zero");
_setWhitelistExpiration(serviceId, user, expirationTimestamp);
emit SetWhitelistExpiration(
serviceId,
user,
msg.sender,
expirationTimestamp
);
}
/// @notice Sets the indefinite whitelist status of `user` to be able to
/// use the service with `serviceId` if the sender has the indefinite
/// whitelister role
/// @param serviceId Service ID
/// @param user User address
/// @param status Indefinite whitelist status
function setIndefiniteWhitelistStatus(
bytes32 serviceId,
address user,
bool status
) external override {
require(
hasIndefiniteWhitelisterRoleOrIsManager(msg.sender),
"Cannot set indefinite status"
);
require(serviceId != bytes32(0), "Service ID zero");
require(user != address(0), "User address zero");
uint192 indefiniteWhitelistCount = _setIndefiniteWhitelistStatus(
serviceId,
user,
status
);
emit SetIndefiniteWhitelistStatus(
serviceId,
user,
msg.sender,
status,
indefiniteWhitelistCount
);
}
/// @notice Revokes the indefinite whitelist status granted by a specific
/// account that no longer has the indefinite whitelister role
/// @param serviceId Service ID
/// @param user User address
/// @param setter Setter of the indefinite whitelist status
function revokeIndefiniteWhitelistStatus(
bytes32 serviceId,
address user,
address setter
) external override {
require(
!hasIndefiniteWhitelisterRoleOrIsManager(setter),
"setter can set indefinite status"
);
(
bool revoked,
uint192 indefiniteWhitelistCount
) = _revokeIndefiniteWhitelistStatus(serviceId, user, setter);
if (revoked) {
emit RevokedIndefiniteWhitelistStatus(
serviceId,
user,
setter,
msg.sender,
indefiniteWhitelistCount
);
}
}
}
@api3/airnode-protocol-v1/contracts/whitelist/interfaces/IWhitelistRoles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IWhitelistRoles {
// solhint-disable-next-line func-name-mixedcase
function WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION()
external
view
returns (string memory);
// solhint-disable-next-line func-name-mixedcase
function WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION()
external
view
returns (string memory);
// solhint-disable-next-line func-name-mixedcase
function INDEFINITE_WHITELISTER_ROLE_DESCRIPTION()
external
view
returns (string memory);
}
@api3/airnode-protocol-v1/contracts/whitelist/interfaces/IWhitelistRolesWithManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IWhitelistRoles.sol";
import "../../access-control-registry/interfaces/IAccessControlRegistryAdminnedWithManager.sol";
interface IWhitelistRolesWithManager is
IWhitelistRoles,
IAccessControlRegistryAdminnedWithManager
{
function whitelistExpirationExtenderRole() external view returns (bytes32);
function whitelistExpirationSetterRole() external view returns (bytes32);
function indefiniteWhitelisterRole() external view returns (bytes32);
}
@api3/airnode-protocol-v1/contracts/whitelist/interfaces/IWhitelistWithManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IWhitelistRolesWithManager.sol";
interface IWhitelistWithManager is IWhitelistRolesWithManager {
event ExtendedWhitelistExpiration(
bytes32 indexed serviceId,
address indexed user,
address indexed sender,
uint256 expiration
);
event SetWhitelistExpiration(
bytes32 indexed serviceId,
address indexed user,
address indexed sender,
uint256 expiration
);
event SetIndefiniteWhitelistStatus(
bytes32 indexed serviceId,
address indexed user,
address indexed sender,
bool status,
uint192 indefiniteWhitelistCount
);
event RevokedIndefiniteWhitelistStatus(
bytes32 indexed serviceId,
address indexed user,
address indexed setter,
address sender,
uint192 indefiniteWhitelistCount
);
function extendWhitelistExpiration(
bytes32 serviceId,
address user,
uint64 expirationTimestamp
) external;
function setWhitelistExpiration(
bytes32 serviceId,
address user,
uint64 expirationTimestamp
) external;
function setIndefiniteWhitelistStatus(
bytes32 serviceId,
address user,
bool status
) external;
function revokeIndefiniteWhitelistStatus(
bytes32 serviceId,
address user,
address setter
) external;
}
@openzeppelin/contracts/access/IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
@openzeppelin/contracts/utils/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
@openzeppelin/contracts/utils/cryptography/ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
Contract ABI
[{"type":"constructor","inputs":[{"type":"address","name":"_accessControlRegistry","internalType":"address"},{"type":"string","name":"_adminRoleDescription","internalType":"string"},{"type":"address","name":"_manager","internalType":"address"},{"type":"address","name":"_airnodeProtocol","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"DAPI_NAME_SETTER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"HUNDRED_PERCENT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"INDEFINITE_WHITELISTER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"WHITELIST_EXPIRATION_EXTENDER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"WHITELIST_EXPIRATION_SETTER_ROLE_DESCRIPTION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"accessControlRegistry","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addUnlimitedReader","inputs":[{"type":"address","name":"unlimitedReader","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"adminRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"adminRoleDescription","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"airnodeProtocol","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"conditionPspBeaconSetUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"conditionParameters","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"conditionPspBeaconUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"conditionParameters","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"dapiNameSetterRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"dapiNameToDataFeedId","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"indefiniteWhitelistStatus","internalType":"bool"}],"name":"dataFeedIdToReaderToSetterToIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"},{"type":"address","name":"reader","internalType":"address"},{"type":"address","name":"setter","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"expirationTimestamp","internalType":"uint64"},{"type":"uint192","name":"indefiniteWhitelistCount","internalType":"uint192"}],"name":"dataFeedIdToReaderToWhitelistStatus","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"},{"type":"address","name":"reader","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"beaconId","internalType":"bytes32"}],"name":"deriveBeaconId","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"beaconSetId","internalType":"bytes32"}],"name":"deriveBeaconSetId","inputs":[{"type":"bytes32[]","name":"beaconIds","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"extendWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"uint64","name":"expirationTimestamp","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fulfillPspBeaconSetUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fulfillPspBeaconUpdate","inputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"},{"type":"address","name":"airnode","internalType":"address"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fulfillRrpBeaconUpdate","inputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBalance","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockBasefee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getChainId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"indefiniteWhitelisterRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"manager","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes[]","name":"results","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"}],"name":"readDataFeedValueWithDapiName","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"}],"name":"readDataFeedValueWithId","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"},{"type":"uint32","name":"timestamp","internalType":"uint32"}],"name":"readDataFeedWithDapiName","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int224","name":"value","internalType":"int224"},{"type":"uint32","name":"timestamp","internalType":"uint32"}],"name":"readDataFeedWithId","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"readerCanReadDataFeed","inputs":[{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"},{"type":"address","name":"reader","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"subscriptionId","internalType":"bytes32"}],"name":"registerBeaconUpdateSubscription","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"bytes","name":"conditions","internalType":"bytes"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}],"name":"requestRrpBeaconUpdate","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"address","name":"sponsor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"requestId","internalType":"bytes32"}],"name":"requestRrpBeaconUpdateRelayed","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"address","name":"relayer","internalType":"address"},{"type":"address","name":"sponsor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"setter","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDapiName","inputs":[{"type":"bytes32","name":"dapiName","internalType":"bytes32"},{"type":"bytes32","name":"dataFeedId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRrpBeaconUpdatePermissionStatus","inputs":[{"type":"address","name":"rrpBeaconUpdateRequester","internalType":"address"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","internalType":"bytes32"},{"type":"address","name":"user","internalType":"address"},{"type":"uint64","name":"expirationTimestamp","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"sponsorToRrpBeaconUpdateRequesterToPermissionStatus","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"subscriptionIdToBeaconId","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"unlimitedReaderStatus","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"beaconSetId","internalType":"bytes32"}],"name":"updateBeaconSetWithBeacons","inputs":[{"type":"bytes32[]","name":"beaconIds","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"updateBeaconSetWithBeaconsAndReturnCondition","inputs":[{"type":"bytes32[]","name":"beaconIds","internalType":"bytes32[]"},{"type":"uint256","name":"deviationThresholdInPercentage","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"beaconSetId","internalType":"bytes32"}],"name":"updateBeaconSetWithSignedData","inputs":[{"type":"address[]","name":"airnodes","internalType":"address[]"},{"type":"bytes32[]","name":"templateIds","internalType":"bytes32[]"},{"type":"uint256[]","name":"timestamps","internalType":"uint256[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"},{"type":"bytes[]","name":"signatures","internalType":"bytes[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateBeaconWithSignedData","inputs":[{"type":"address","name":"airnode","internalType":"address"},{"type":"bytes32","name":"templateId","internalType":"bytes32"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"whitelistExpirationExtenderRole","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"whitelistExpirationSetterRole","inputs":[]},{"type":"event","name":"AddedUnlimitedReader","inputs":[{"type":"address","name":"unlimitedReader","indexed":true}],"anonymous":false},{"type":"event","name":"ExtendedWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"expiration","indexed":false}],"anonymous":false},{"type":"event","name":"RegisteredBeaconUpdateSubscription","inputs":[{"type":"bytes32","name":"subscriptionId","indexed":true},{"type":"address","name":"airnode","indexed":false},{"type":"bytes32","name":"templateId","indexed":false},{"type":"bytes","name":"parameters","indexed":false},{"type":"bytes","name":"conditions","indexed":false},{"type":"address","name":"relayer","indexed":false},{"type":"address","name":"sponsor","indexed":false},{"type":"address","name":"requester","indexed":false},{"type":"bytes4","name":"fulfillFunctionId","indexed":false}],"anonymous":false},{"type":"event","name":"RequestedRrpBeaconUpdate","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"requester","indexed":true},{"type":"bytes32","name":"requestId","indexed":false},{"type":"address","name":"airnode","indexed":false},{"type":"bytes32","name":"templateId","indexed":false}],"anonymous":false},{"type":"event","name":"RequestedRrpBeaconUpdateRelayed","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"requester","indexed":true},{"type":"bytes32","name":"requestId","indexed":false},{"type":"address","name":"airnode","indexed":false},{"type":"address","name":"relayer","indexed":false},{"type":"bytes32","name":"templateId","indexed":false}],"anonymous":false},{"type":"event","name":"RevokedIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"setter","indexed":true},{"type":"address","name":"sender","indexed":false},{"type":"uint192","name":"indefiniteWhitelistCount","indexed":false}],"anonymous":false},{"type":"event","name":"SetDapiName","inputs":[{"type":"bytes32","name":"dapiName","indexed":true},{"type":"bytes32","name":"dataFeedId","indexed":false},{"type":"address","name":"sender","indexed":true}],"anonymous":false},{"type":"event","name":"SetIndefiniteWhitelistStatus","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"bool","name":"status","indexed":false},{"type":"uint192","name":"indefiniteWhitelistCount","indexed":false}],"anonymous":false},{"type":"event","name":"SetRrpBeaconUpdatePermissionStatus","inputs":[{"type":"address","name":"sponsor","indexed":true},{"type":"address","name":"rrpBeaconUpdateRequester","indexed":true},{"type":"bool","name":"status","indexed":false}],"anonymous":false},{"type":"event","name":"SetWhitelistExpiration","inputs":[{"type":"bytes32","name":"serviceId","indexed":true},{"type":"address","name":"user","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"expiration","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconSetWithBeacons","inputs":[{"type":"bytes32","name":"beaconSetId","indexed":true},{"type":"int224","name":"value","indexed":false},{"type":"uint32","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconSetWithSignedData","inputs":[{"type":"bytes32","name":"dapiId","indexed":true},{"type":"int224","name":"value","indexed":false},{"type":"uint32","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconWithPsp","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"bytes32","name":"subscriptionId","indexed":false},{"type":"int224","name":"value","indexed":false},{"type":"uint32","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconWithRrp","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"bytes32","name":"requestId","indexed":false},{"type":"int256","name":"value","indexed":false},{"type":"uint256","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatedBeaconWithSignedData","inputs":[{"type":"bytes32","name":"beaconId","indexed":true},{"type":"int256","name":"value","indexed":false},{"type":"uint256","name":"timestamp","indexed":false}],"anonymous":false}]
Contract Creation Code
0x6101a06040523480156200001257600080fd5b5060405162005d7938038062005d7983398101604081905262000035916200054d565b808484848282828282828282816001600160a01b038116620000915760405162461bcd60e51b815260206004820152601060248201526f4143522061646472657373207a65726f60801b60448201526064015b60405180910390fd5b6001600160a01b03166080528051620000ed5760405162461bcd60e51b815260206004820152601c60248201527f41646d696e20726f6c65206465736372697074696f6e20656d70747900000000604482015260640162000088565b80516200010290600290602084019062000441565b508060405160200162000116919062000640565b60408051601f19818403018152919052805160209091012060a05250506001600160a01b0381166200018b5760405162461bcd60e51b815260206004820152601460248201527f4d616e616765722061646472657373207a65726f000000000000000000000000604482015260640162000088565b6001600160a01b03811660c052620001a3816200039b565b60e081815250505050506200021e60e0516040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e646572000000815250604051602001620001fc919062000640565b604051602081830303815290604052805190602001206200041560201b60201c565b61010081815250506200027560e0516040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e207365747465720000000000815250604051602001620001fc919062000640565b6101208181525050620002cc60e0516040518060400160405280601681526020017f496e646566696e6974652077686974656c697374657200000000000000000000815250604051602001620001fc919062000640565b6101405250505050506001600160a01b03821615159050620003315760405162461bcd60e51b815260206004820152601c60248201527f4169726e6f646550726f746f636f6c2061646472657373207a65726f00000000604482015260640162000088565b6001600160a01b03166101605260c0516200038c9062000351906200039b565b6040518060400160405280601081526020016f3220a824903730b6b29039b2ba3a32b960811b815250604051602001620001fc919062000640565b61018052506200069b92505050565b60006200040f620003e0836040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b60a051604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b92915050565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b8280546200044f906200065e565b90600052602060002090601f016020900481019282620004735760008555620004be565b82601f106200048e57805160ff1916838001178555620004be565b82800160010185558215620004be579182015b82811115620004be578251825591602001919060010190620004a1565b50620004cc929150620004d0565b5090565b5b80821115620004cc5760008155600101620004d1565b80516001600160a01b0381168114620004ff57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620005375781810151838201526020016200051d565b8381111562000547576000848401525b50505050565b600080600080608085870312156200056457600080fd5b6200056f85620004e7565b60208601519094506001600160401b03808211156200058d57600080fd5b818701915087601f830112620005a257600080fd5b815181811115620005b757620005b762000504565b604051601f8201601f19908116603f01168101908382118183101715620005e257620005e262000504565b816040528281528a6020848701011115620005fc57600080fd5b6200060f8360208301602088016200051a565b80975050505050506200062560408601620004e7565b91506200063560608601620004e7565b905092959194509250565b60008251620006548184602087016200051a565b9190910192915050565b600181811c908216806200067357607f821691505b602082108114156200069557634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e051610100516101205161014051610160516101805161560062000779600039600081816105b6015261207401526000818161076a015281816116cd015281816122f00152612bb001526000818161089201526132f101526000818161065301526131b10152600081816105f601526137c1015260006103540152600081816104920152818161203e015281816129f501528181613173015281816132b30152613783015260005050600081816103c9015281816120a0015281816131e70152818161332701526137f701526156006000f3fe608060405234801561001057600080fd5b506004361061034a5760003560e01c80638e85afe0116101bd578063bcb5daec116100f9578063e18de857116100a2578063f60e45d31161007c578063f60e45d3146108da578063f8b2cb4f1461091b578063fc3c1f8b14610936578063fce90be81461094957600080fd5b8063e18de8571461088d578063e406f3b2146108b4578063e705882e146108c757600080fd5b8063d55a42dd116100d3578063d55a42dd1461082b578063d7c0b87914610867578063dc96acc81461087a57600080fd5b8063bcb5daec1461078c578063c4f95dd51461079f578063d39c8968146107b257600080fd5b80639e14caa811610166578063a6b1b9a211610140578063a6b1b9a21461070f578063ac9650d814610722578063b605bbcd14610742578063b76f3c151461076557600080fd5b80639e14caa8146106c9578063a026658f146106dc578063a5fc076f146106fc57600080fd5b806391eed0851161019757806391eed085146106755780639421240514610688578063977cae011461069b57600080fd5b80638e85afe0146106185780638fca9ab91461063b5780638fe47dd81461064e57600080fd5b80634c8f1d8d1161028c5780636db798f9116102355780637512449b1161020f5780637512449b146105b1578063776bcbd2146105d8578063796b89b9146105eb5780637c706738146105f157600080fd5b80636db798f9146105575780636ed93dd01461059357806370c927ca1461059e57600080fd5b80635bc8a5c8116102665780635bc8a5c8146104f557806365101054146105085780636bd2bdd01461051b57600080fd5b80634c8f1d8d146104b45780634dcc19fe146104c957806358c55edd146104cf57600080fd5b8063206b48f4116102f95780633408e470116102d35780633408e4701461044f5780633af1dbfd1461045557806342cbb15c14610487578063481c6a751461048d57600080fd5b8063206b48f4146104165780632ce010e3146104295780633376e7c21461043c57600080fd5b80631a0a0b3e1161032a5780631a0a0b3e146103af5780631ce9ae07146103c45780631d36cf5c1461040357600080fd5b80629f2f3c1461034f578062aae33f1461038957806304dd3cee1461039c575b600080fd5b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6103766103973660046147a4565b610985565b6103766103aa3660046147f5565b610be1565b6103c26103bd366004614861565b610cca565b005b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610380565b6103c26104113660046148f5565b610e46565b6103c2610424366004614942565b610fb1565b6103c2610437366004614942565b6112ad565b6103c261044a366004614a07565b61132c565b46610376565b610468610463366004614a3c565b611472565b60408051601b9390930b835263ffffffff909116602083015201610380565b43610376565b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b6104bc611574565b6040516103809190614ab1565b48610376565b6104e26104dd366004614a3c565b611602565b604051601b9190910b8152602001610380565b6103c2610503366004614ac4565b6116c2565b610376610516366004614c69565b6117f6565b6104bc6040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e20736574746572000000000081525081565b6104bc6040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e64657200000081525081565b6103766305f5e10081565b6104e26105ac366004614a3c565b611d3b565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c26105e6366004614d3b565b611e35565b42610376565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b61062b610626366004614d77565b611efe565b6040519015158152602001610380565b610376610649366004614a3c565b611fa1565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c2610683366004614dbc565b611fe6565b610376610696366004614dde565b6121f6565b61062b6106a9366004614e11565b600460209081526000928352604080842090915290825290205460ff1681565b6103c26106d7366004614e44565b6123e0565b6103766106ea366004614a3c565b60056020526000908152604090205481565b61046861070a366004614a3c565b61249b565b6103c261071d3660046148f5565b612511565b610735610730366004614e7b565b612643565b6040516103809190614ef0565b61062b610750366004614f52565b60036020526000908152604090205460ff1681565b6103eb7f000000000000000000000000000000000000000000000000000000000000000081565b61062b61079a366004614f6d565b612738565b6103766107ad3660046147a4565b61277f565b6108036107c0366004614f6d565b6000918252602082815260408084206001600160a01b0393909316845291905290205467ffffffffffffffff811691600160401b9091046001600160c01b031690565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610380565b6104bc6040518060400160405280601681526020017f496e646566696e6974652077686974656c69737465720000000000000000000081525081565b61062b610875366004614f90565b6127af565b61062b610888366004614f90565b6128b4565b6103767f000000000000000000000000000000000000000000000000000000000000000081565b6103c26108c2366004614f52565b6129ea565b6103766108d536600461500a565b612aae565b61062b6108e8366004614d3b565b60009283526001602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b610376610929366004614f52565b6001600160a01b03163190565b610376610944366004615057565b612cb9565b6104bc6040518060400160405280601081526020017f64415049206e616d65207365747465720000000000000000000000000000000081525081565b8051600090600181116109df5760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064015b60405180910390fd5b60008167ffffffffffffffff8111156109fa576109fa6146ce565b604051908082528060200260200182016040528015610a23578160200160208202803683370190505b5090506000805b83811015610abb57600060066000888481518110610a4a57610a4a6150d0565b602090810291909101810151825281019190915260400160002080548551919250601b0b90859084908110610a8157610a816150d0565b60209081029190910101528054610aa590600160e01b900463ffffffff16846150fc565b9250508080610ab390615114565b915050610a2a565b506000610ac88483615145565b9050610ad38661277f565b60008181526006602052604090205490955063ffffffff600160e01b90910481169082161015610b455760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000610b5084612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008e81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925087917fb7712be6248d021e8c56ac9613c09491354a4d0f4ad0b7db1a664b35be4b2349910160405180910390a25050505050919050565b60006001600160a01b038316610c395760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f00000000000000000000000060448201526064016109d6565b81610c865760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f0000000000000000000000000000000060448201526064016109d6565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260540160405160208183030381529060405280519060200120905092915050565b84610cd481612fd5565b610d165760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b876001600160a01b0316610d9584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508c908c908c908c90602001615159565b60405160208183030381529060405280519060200120613001565b9061303c565b6001600160a01b031614610de05760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b6000610dec8989610be1565b90506000610dfc82898989613060565b60408051828152602081018b905291925083917f403078446dab7471f481ca4bffac706bd84a6dba118980d267676c096f2ba924910160405180910390a250505050505050505050565b610e4f33613165565b610e9b5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f74207365742065787069726174696f6e000000000000000000000060448201526064016109d6565b82610eda5760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b038216610f245760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000838152602081815260408083206001600160a01b03861684529091529020805467ffffffffffffffff191667ffffffffffffffff831617905560405167ffffffffffffffff8216815233906001600160a01b0384169085907fd19e89b7d547ccf349211588a9a1d29461e2ce984b1b1cdbe7150976528b86f1906020015b60405180910390a4505050565b84610fbb81612fd5565b610ffd5760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6040516bffffffffffffffffffffffff1960608b811b821660208401528a811b8216603484015289901b166048820152605c0160408051601f19818403018152918152815160209283012060008d81526008909352912054146110a25760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b876001600160a01b0316896001600160a01b0316141561118b57886001600160a01b031661113b84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c903390602001928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b6001600160a01b0316146111865760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611236565b886001600160a01b03166111eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c9033908d908d9060200161517a565b6001600160a01b0316146112365760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b60008a8152600560205260408120549061125282898989613060565b604080518e8152601b83900b602082015263ffffffff8b1681830152905191925083917f2b85d71813342fd0091d4573a5f78e244c2133755e470943c33a6dbf3cf9c15d9181900360600190a2505050505050505050505050565b6112bc610397848601866147a4565b84846040516112cc9291906151b4565b6040518091039020146113215760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b505050505050505050565b611335336132a5565b6113815760405162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742073657420696e646566696e697465207374617475730000000060448201526064016109d6565b826113c05760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b03821661140a5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000611417848484613356565b6040805184151581526001600160c01b038316602082015291925033916001600160a01b0386169187917f2fa93828cf3f001b9a9f0a7365db04ea068b9db7bcb7a38d289f0cf8aa9ce374910160405180910390a450505050565b60008060008360405160200161148a91815260200190565b6040516020818303038152906040528051906020012090506114ac8133612738565b6114ed5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b600081815260096020526040902054806115495760405162461bcd60e51b815260206004820152601160248201527f64415049206e616d65206e6f742073657400000000000000000000000000000060448201526064016109d6565b600090815260066020526040902054601b81900b95600160e01b90910463ffffffff16945092505050565b60028054611581906151c4565b80601f01602080910402602001604051908101604052809291908181526020018280546115ad906151c4565b80156115fa5780601f106115cf576101008083540402835291602001916115fa565b820191906000526020600020905b8154815290600101906020018083116115dd57829003601f168201915b505050505081565b600061160e8233612738565b61164f5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b60008281526006602052604090208054600160e01b900463ffffffff166116b85760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b92915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461173a5760405162461bcd60e51b815260206004820152601b60248201527f53656e646572206e6f74204169726e6f64652070726f746f636f6c000000000060448201526064016109d6565b8261174481612fd5565b6117865760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6000858152600760205260408120805490829055906117a782878787613060565b604080518981526020810183905290810188905290915082907f7c88b543c4cda65df046e87397c3152c27098419cb22a90a5e37db30ef621b269060600160405180910390a250505050505050565b84518451600091908114801561180c5750845181145b80156118185750835181145b80156118245750825181145b6118705760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d617463680000000000000060448201526064016109d6565b600181116118c05760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064016109d6565b60008167ffffffffffffffff8111156118db576118db6146ce565b604051908082528060200260200182016040528015611904578160200160208202803683370190505b50905060008267ffffffffffffffff811115611922576119226146ce565b60405190808252806020026020018201604052801561194b578160200160208202803683370190505b5090506000805b84811015611c105786818151811061196c5761196c6150d0565b602002602001015151600014611b555760008b8281518110611990576119906150d0565b6020026020010151905060008a83815181106119ae576119ae6150d0565b602002602001015190506119c181612fd5565b611a035760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b816001600160a01b0316611a748a8581518110611a2257611a226150d0565b6020026020010151610d8f8f8781518110611a3f57611a3f6150d0565b6020026020010151858f8981518110611a5a57611a5a6150d0565b6020026020010151604051602001610d74939291906151f9565b6001600160a01b031614611abf5760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611ae18a8481518110611ad457611ad46150d0565b602002602001015161350e565b601b0b858481518110611af657611af66150d0565b6020908102919091010152611b0b81856150fc565b9350611b30828d8581518110611b2357611b236150d0565b6020026020010151610be1565b868481518110611b4257611b426150d0565b6020026020010181815250505050611bfe565b6000611b868c8381518110611b6c57611b6c6150d0565b60200260200101518c8481518110611b2357611b236150d0565b6000818152600660205260409020805486519293509091601b9190910b90869085908110611bb657611bb66150d0565b60209081029190910101528054611bda90600160e01b900463ffffffff16856150fc565b935081868481518110611bef57611bef6150d0565b60200260200101818152505050505b80611c0881615114565b915050611952565b50611c1a8361277f565b94506000611c288583615145565b60008781526006602052604090205490915063ffffffff600160e01b90910481169082161015611c9a5760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000611ca584612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008f81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925088917fb34747dc40d9c985b4857c2955fec7a8f34d88bc06da72f43319795758800407910160405180910390a250505050505095945050505050565b60008082604051602001611d5191815260200190565b604051602081830303815290604052805190602001209050611d738133612738565b611db45760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b6000818152600960209081526040808320548352600690915290208054600160e01b900463ffffffff16611e2a5760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b9392505050565b611e3e816132a5565b15611e8b5760405162461bcd60e51b815260206004820181905260248201527f7365747465722063616e2073657420696e646566696e6974652073746174757360448201526064016109d6565b600080611e99858585613611565b915091508115611ef757604080513381526001600160c01b03831660208201526001600160a01b03808616929087169188917f29c394c1d92801cab93215bf9cd50ae38d23341be6540f27d80ee2bc8a541237910160405180910390a45b5050505050565b600080611f0a8461277f565b600081815260066020908152604091829020825180840190935254601b81900b8352600160e01b900463ffffffff1690820152909150611f4985610985565b506000828152600660205260409020815181548691611f6a91601b0b6136fc565b101580611f975750602082015163ffffffff16158015611f9757508054600160e01b900463ffffffff1615155b9695505050505050565b60006009600083604051602001611fba91815260200190565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b816120335760405162461bcd60e51b815260206004820152600e60248201527f64415049206e616d65207a65726f00000000000000000000000000000000000060448201526064016109d6565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806121225750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d148549060440160206040518083038186803b1580156120ea57600080fd5b505afa1580156120fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121229190615226565b61216e5760405162461bcd60e51b815260206004820152601b60248201527f53656e6465722063616e6e6f74207365742064415049206e616d65000000000060448201526064016109d6565b80600960008460405160200161218691815260200190565b60405160208183030381529060405280519060200120815260200190815260200160002081905550336001600160a01b0316827ff3a9aac9b6ac0f842cb5d9b3491cd5fc1b6a6778d97fd9529f587339865294f5836040516121ea91815260200190565b60405180910390a35050565b6000816001600160a01b03811633148061223357506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b61227f5760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b600061228b8686610be1565b6040517feebecf690000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301526024820188905260a06044830152600060a48301528681166064830152630b7914b960e31b60848301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063eebecf699060c401602060405180830381600087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236e9190615243565b60008181526007602090815260409182902084905581518381526001600160a01b038a811692820192909252918201889052919450339186169083907fc0e8c8735457dd46668d6c01832656e0803b41b1e61ba12354a24f11e114eab39060600160405180910390a450509392505050565b6001600160a01b0382166124365760405162461bcd60e51b815260206004820152601560248201527f55706461746520726571756573746572207a65726f000000000000000000000060448201526064016109d6565b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fba239f737a4075f5efb0d649e16cd7d2b1978690876b079dab642f077050b27991016121ea565b6000806124a88333612738565b6124e95760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b5050600090815260066020526040902054601b81900b91600160e01b90910463ffffffff1690565b61251a33613775565b6125665760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420657874656e642065787069726174696f6e000000000000000060448201526064016109d6565b826125a55760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b0382166125ef5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6125fa838383613826565b60405167ffffffffffffffff8216815233906001600160a01b0384169085907fa9e0c89b898eb7a904617915dc5b5510d539c899810042e9248569b54b9cc2ed90602001610fa4565b60608167ffffffffffffffff81111561265e5761265e6146ce565b60405190808252806020026020018201604052801561269157816020015b606081526020019060019003908161267c5790505b50905060005b8281101561273157612701308585848181106126b5576126b56150d0565b90506020028101906126c7919061525c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506138a392505050565b828281518110612713576127136150d0565b6020026020010181905250808061272990615114565b915050612697565b5092915050565b60006001600160a01b0382161580612755575061275583836138c8565b8061277857506001600160a01b03821660009081526003602052604090205460ff165b9392505050565b60008160405160200161279291906152a3565b604051602081830303815290604052805190602001209050919050565b600033156127ff5760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600061280d858701876147a4565b9050858560405161281f9291906151b4565b60405180910390208160405160200161283891906152a3565b604051602081830303815290604052805190602001201461289b5760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6128a981610626868661391b565b979650505050505050565b600033156129045760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600086815260056020526040902054806129605760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b6000818152600660205260409020612978858561391b565b8154604080516020601f8b018190048102820181019092528981526129c592601b0b916129c091908c908c908190840183828082843760009201919091525061350e92505050565b6136fc565b1015806129de57508054600160e01b900463ffffffff16155b98975050505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612a625760405162461bcd60e51b815260206004820152601260248201527f53656e646572206e6f74206d616e61676572000000000000000000000000000060448201526064016109d6565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f6d71fde11d6c56eaf0154d464b51df0b26dcdac118703056ea3da805fc9e1cd79190a250565b6000816001600160a01b038116331480612aeb57506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b612b375760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b6000612b438787610be1565b6040517fa7e0c85e0000000000000000000000000000000000000000000000000000000081526001600160a01b0389811660048301526024820189905260c06044830152600060c483015287811660648301528681166084830152630b7914b960e31b60a48301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a7e0c85e9060e401602060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2e9190615243565b600081815260076020526040908190208390555190935033906001600160a01b0386169083907faeef85c84fba3ef952ca2738d951d30f5093595cb5eb9830764f5b49a39442a390612ca79088908d908c908e909384526001600160a01b03928316602085015291166040830152606082015260800190565b60405180910390a45050949350505050565b60006001600160a01b038316612d115760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b6001600160a01b038216612d675760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b604051612d8d90469088908890889088908890309063081ad23d60e21b906020016152e7565b60408051601f1981840301815282825280516020918201206bffffffffffffffffffffffff1960608b811b82168487015288811b8216603487015287901b1660488501528251808503603c018152605c909401835283519382019390932060008481526008909252919020559050612e058686610be1565b60008281526005602052604090819020919091555181907f82b139c5e690a3a4a9c2f68133d2ab1f7724bf8a69ad3fe1cd9f5a4923cf41b290612e5c9089908990899089908990309063081ad23d60e21b90615376565b60405180910390a295945050505050565b805160009060098111612f2d57612e8383613979565b612e8e6002826153fd565b60011415612ec25782612ea2600283615145565b81518110612eb257612eb26150d0565b6020026020010151915050919050565b600283612ecf8284615145565b81518110612edf57612edf6150d0565b6020026020010151846001600285612ef79190615145565b612f019190615411565b81518110612f1157612f116150d0565b6020026020010151612f239190615428565b6127789190615467565b612f386002826153fd565b60011415612f555782612ea281612f50600285615145565b613e90565b600080612f78856001612f69600287615145565b612f739190615411565b613eae565b915091506002858281518110612f9057612f906150d0565b6020026020010151868481518110612faa57612faa6150d0565b6020026020010151612fbc9190615428565b612fc69190615467565b95945050505050565b50919050565b600042612fe483610e106150fc565b118015612ffb5750612ff8426103846150fc565b82105b92915050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01612792565b600080600061304b8585613f2b565b9150915061305881613f98565b509392505050565b60006130a183838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061350e92505050565b600086815260066020526040902054601b9190910b9150600160e01b900463ffffffff1684116131135760405162461bcd60e51b815260206004820152601d60248201527f46756c66696c6c6d656e74206f6c646572207468616e20426561636f6e00000060448201526064016109d6565b604080518082018252601b83900b815263ffffffff95861660208083019182526000988952600690529190962095519051909416600160e01b026001600160e01b039094169390931790935550919050565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044015b60206040518083038186803b15801561322a57600080fd5b505afa15801561323e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffb9190615226565b6000928352602083815260408085206001600160a01b039490941685529290529120805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401613212565b6000838152602081815260408083206001600160a01b0386168452909152902054600160401b90046001600160c01b03168180156133bf575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff16155b1561344c5760008481526001602081815260408084206001600160a01b03881685528252808420338552909152909120805460ff191690911790558061340481615495565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b0384160217905591506127789050565b81158015613484575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff165b156127785760008481526001602090815260408083206001600160a01b038716845282528083203384529091529020805460ff19169055806134c5816154bc565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b038416021790559150509392505050565b600081516020146135615760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6000828060200190518101906135779190615243565b90507fffffffff8000000000000000000000000000000000000000000000000000000081128015906135c557507b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff8113155b612ffb5760405162461bcd60e51b815260206004820152601760248201527f56616c7565207479706563617374696e67206572726f7200000000000000000060448201526064016109d6565b6000838152602081815260408083206001600160a01b0386811680865291845282852054888652600185528386209286529184528285209086168552909252822054600160401b9091046001600160c01b03169060ff16156136f45760008581526001602090815260408083206001600160a01b03808916855290835281842090871684529091529020805460ff19169055806136ad816154bc565b6000878152602081815260408083206001600160a01b038a1684529091529020805467ffffffffffffffff16600160401b6001600160c01b03841602179055600193509150505b935093915050565b60008083601b0b83601b0b61371191906154df565b9050600080821361372a576137258261551e565b61372c565b815b905060008086601b0b1361374c5785601b0b6137479061551e565b613751565b85601b0b5b90508061375c575060015b8061376b6305f5e1008461553b565b611f979190615145565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480612ffb5750604051632474521560e21b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0383811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401613212565b6000838152602081815260408083206001600160a01b038616845290915290205467ffffffffffffffff908116908216116132625760405162461bcd60e51b815260206004820152601a60248201527f446f6573206e6f7420657874656e642065787069726174696f6e00000000000060448201526064016109d6565b606061277883836040518060600160405280602781526020016155a460279139614156565b6000828152602081815260408083206001600160a01b038516845290915281208054600160401b90046001600160c01b0316151580613913575080544267ffffffffffffffff909116115b949350505050565b60006020821461396d5760405162461bcd60e51b815260206004820152601a60248201527f496e636f727265637420706172616d65746572206c656e67746800000000000060448201526064016109d6565b61277882840184614a3c565b805160098111156139cc5760405162461bcd60e51b815260206004820152601660248201527f417272617920746f6f206c6f6e6720746f20736f72740000000000000000000060448201526064016109d6565b6006811015613ada576004811015613a28578060031415613a12576139f48260006001614237565b613a018260016002614237565b613a0e8260006001614237565b5050565b8060021415613a0e57613a0e8260006001614237565b8060051415613aa657613a3e8260016002614237565b613a4b8260036004614237565b613a588260016003614237565b613a658260006002614237565b613a728260026004614237565b613a7f8260006003614237565b613a8c8260006001614237565b613a998260026003614237565b613a0e8260016002614237565b613ab38260006001614237565b613ac08260026003614237565b613acd8260016003614237565b613a998260006002614237565b6008811015613c58578060071415613bbc57613af98260016002614237565b613b068260036004614237565b613b138260056006614237565b613b208260006002614237565b613b2d8260046006614237565b613b3a8260036005614237565b613b478260026006614237565b613b548260016005614237565b613b618260006004614237565b613b6e8260026005614237565b613b7b8260006003614237565b613b888260026004614237565b613b958260016003614237565b613ba28260006001614237565b613baf8260026003614237565b613a0e8260046005614237565b613bc98260006001614237565b613bd68260026003614237565b613be38260046005614237565b613bf08260016003614237565b613bfd8260036005614237565b613c0a8260016003614237565b613c178260026004614237565b613c248260006002614237565b613c318260026004614237565b613c3e8260036004614237565b613c4b8260016002614237565b613a0e8260026003614237565b8060091415613d8c57613c6e8260016008614237565b613c7b8260026007614237565b613c888260036006614237565b613c958260046005614237565b613ca28260016004614237565b613caf8260056008614237565b613cbc8260006002614237565b613cc98260066007614237565b613cd68260026006614237565b613ce38260076008614237565b613cf08260006003614237565b613cfd8260046005614237565b613d0a8260006001614237565b613d178260036005614237565b613d248260066007614237565b613d318260026004614237565b613d3e8260016003614237565b613d4b8260056007614237565b613d588260046006614237565b613d658260016002614237565b613d728260036004614237565b613d7f8260056006614237565b613ba28260076008614237565b613d998260006007614237565b613da68260016006614237565b613db38260026005614237565b613dc08260036004614237565b613dcd8260006003614237565b613dda8260046007614237565b613de78260016002614237565b613df48260056006614237565b613e018260006001614237565b613e0e8260026003614237565b613e1b8260046005614237565b613e288260066007614237565b613e358260036005614237565b613e428260026004614237565b613e4f8260016002614237565b613e5c8260036004614237565b613e698260056006614237565b613e768260026003614237565b613e838260046005614237565b613a0e8260036004614237565b600061305883600060018651613ea69190615411565b8560006142e5565b8151600090819060018111613f055760405162461bcd60e51b815260206004820152601d60248201527f417272617920746f6f2073686f727420746f2073656c656374206b2b3100000060448201526064016109d6565b613f1e856000613f16600185615411565b8760016142e5565b92509250505b9250929050565b600080825160411415613f625760208301516040840151606085015160001a613f56878285856143e0565b94509450505050613f24565b825160401415613f8c5760208301516040840151613f818683836144cd565b935093505050613f24565b50600090506002613f24565b6000816004811115613fac57613fac61555a565b1415613fb55750565b6001816004811115613fc957613fc961555a565b14156140175760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016109d6565b600281600481111561402b5761402b61555a565b14156140795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016109d6565b600381600481111561408d5761408d61555a565b14156140e65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016109d6565b60048160048111156140fa576140fa61555a565b14156141535760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016109d6565b50565b6060833b6141cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016109d6565b600080856001600160a01b0316856040516141e79190615570565b600060405180830381855af49150503d8060008114614222576040519150601f19603f3d011682016040523d82523d6000602084013e614227565b606091505b5091509150611f978282866144fc565b828181518110614249576142496150d0565b6020026020010151838381518110614263576142636150d0565b602002602001015113156142e057828181518110614283576142836150d0565b602002602001015183838151811061429d5761429d6150d0565b60200260200101518484815181106142b7576142b76150d0565b602002602001018584815181106142d0576142d06150d0565b6020908102919091010191909152525b505050565b600080848614156142fb575082905060006143d6565b6000614308888888614535565b9050808510156143325761432a8888614322600185615411565b8860006142e5565b509250614356565b808511156143525761432a886143498360016150fc565b888860006142e5565b8092505b83156143d4576143678360016150fc565b915060006143768360016150fc565b90505b88518110156143d257888381518110614394576143946150d0565b60200260200101518982815181106143ae576143ae6150d0565b602002602001015112156143c0578092505b806143ca81615114565b915050614379565b505b505b9550959350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561441757506000905060036144c4565b8460ff16601b1415801561442f57508460ff16601c14155b1561444057506000905060046144c4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614494573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166144bd576000600192509250506144c4565b9150600090505b94509492505050565b6000806001600160ff1b03831660ff84901c601b016144ee878288856143e0565b935093505050935093915050565b6060831561450b575081612778565b82511561451b5782518084602001fd5b8160405162461bcd60e51b81526004016109d69190614ab1565b600081831415614546575081612778565b600084848151811061455a5761455a6150d0565b60209081029190910101519050836145738460016150fc565b92505b8061458081615114565b9150508551811080156145ab5750818682815181106145a1576145a16150d0565b6020026020010151125b614576575b826145ba8161558c565b935050818684815181106145d0576145d06150d0565b6020026020010151136145b05782811061465b578583815181106145f6576145f66150d0565b6020026020010151868681518110614610576146106150d0565b602002602001015187878151811061462a5761462a6150d0565b60200260200101888681518110614643576146436150d0565b60200260200101828152508281525050505050612778565b85838151811061466d5761466d6150d0565b6020026020010151868281518110614687576146876150d0565b60200260200101518783815181106146a1576146a16150d0565b602002602001018886815181106146ba576146ba6150d0565b602090810291909101019190915252614576565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561470d5761470d6146ce565b604052919050565b600067ffffffffffffffff82111561472f5761472f6146ce565b5060051b60200190565b600082601f83011261474a57600080fd5b8135602061475f61475a83614715565b6146e4565b82815260059290921b8401810191818101908684111561477e57600080fd5b8286015b848110156147995780358352918301918301614782565b509695505050505050565b6000602082840312156147b657600080fd5b813567ffffffffffffffff8111156147cd57600080fd5b61391384828501614739565b80356001600160a01b03811681146147f057600080fd5b919050565b6000806040838503121561480857600080fd5b614811836147d9565b946020939093013593505050565b60008083601f84011261483157600080fd5b50813567ffffffffffffffff81111561484957600080fd5b602083019150836020828501011115613f2457600080fd5b600080600080600080600060a0888a03121561487c57600080fd5b614885886147d9565b96506020880135955060408801359450606088013567ffffffffffffffff808211156148b057600080fd5b6148bc8b838c0161481f565b909650945060808a01359150808211156148d557600080fd5b506148e28a828b0161481f565b989b979a50959850939692959293505050565b60008060006060848603121561490a57600080fd5b8335925061491a602085016147d9565b9150604084013567ffffffffffffffff8116811461493757600080fd5b809150509250925092565b600080600080600080600080600060e08a8c03121561496057600080fd5b8935985061497060208b016147d9565b975061497e60408b016147d9565b965061498c60608b016147d9565b955060808a0135945060a08a013567ffffffffffffffff808211156149b057600080fd5b6149bc8d838e0161481f565b909650945060c08c01359150808211156149d557600080fd5b506149e28c828d0161481f565b915080935050809150509295985092959850929598565b801515811461415357600080fd5b600080600060608486031215614a1c57600080fd5b83359250614a2c602085016147d9565b91506040840135614937816149f9565b600060208284031215614a4e57600080fd5b5035919050565b60005b83811015614a70578181015183820152602001614a58565b83811115614a7f576000848401525b50505050565b60008151808452614a9d816020860160208601614a55565b601f01601f19169290920160200192915050565b6020815260006127786020830184614a85565b60008060008060608587031215614ada57600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614aff57600080fd5b614b0b8782880161481f565b95989497509550505050565b600082601f830112614b2857600080fd5b81356020614b3861475a83614715565b82815260059290921b84018101918181019086841115614b5757600080fd5b8286015b8481101561479957614b6c816147d9565b8352918301918301614b5b565b600082601f830112614b8a57600080fd5b813567ffffffffffffffff811115614ba457614ba46146ce565b614bb7601f8201601f19166020016146e4565b818152846020838601011115614bcc57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614bfa57600080fd5b81356020614c0a61475a83614715565b82815260059290921b84018101918181019086841115614c2957600080fd5b8286015b8481101561479957803567ffffffffffffffff811115614c4d5760008081fd5b614c5b8986838b0101614b79565b845250918301918301614c2d565b600080600080600060a08688031215614c8157600080fd5b853567ffffffffffffffff80821115614c9957600080fd5b614ca589838a01614b17565b96506020880135915080821115614cbb57600080fd5b614cc789838a01614739565b95506040880135915080821115614cdd57600080fd5b614ce989838a01614739565b94506060880135915080821115614cff57600080fd5b614d0b89838a01614be9565b93506080880135915080821115614d2157600080fd5b50614d2e88828901614be9565b9150509295509295909350565b600080600060608486031215614d5057600080fd5b83359250614d60602085016147d9565b9150614d6e604085016147d9565b90509250925092565b60008060408385031215614d8a57600080fd5b823567ffffffffffffffff811115614da157600080fd5b614dad85828601614739565b95602094909401359450505050565b60008060408385031215614dcf57600080fd5b50508035926020909101359150565b600080600060608486031215614df357600080fd5b614dfc846147d9565b925060208401359150614d6e604085016147d9565b60008060408385031215614e2457600080fd5b614e2d836147d9565b9150614e3b602084016147d9565b90509250929050565b60008060408385031215614e5757600080fd5b614e60836147d9565b91506020830135614e70816149f9565b809150509250929050565b60008060208385031215614e8e57600080fd5b823567ffffffffffffffff80821115614ea657600080fd5b818501915085601f830112614eba57600080fd5b813581811115614ec957600080fd5b8660208260051b8501011115614ede57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614f4557603f19888603018452614f33858351614a85565b94509285019290850190600101614f17565b5092979650505050505050565b600060208284031215614f6457600080fd5b612778826147d9565b60008060408385031215614f8057600080fd5b82359150614e3b602084016147d9565b600080600080600060608688031215614fa857600080fd5b85359450602086013567ffffffffffffffff80821115614fc757600080fd5b614fd389838a0161481f565b90965094506040880135915080821115614fec57600080fd5b50614ff98882890161481f565b969995985093965092949392505050565b6000806000806080858703121561502057600080fd5b615029856147d9565b93506020850135925061503e604086016147d9565b915061504c606086016147d9565b905092959194509250565b600080600080600060a0868803121561506f57600080fd5b615078866147d9565b945060208601359350604086013567ffffffffffffffff81111561509b57600080fd5b6150a788828901614b79565b9350506150b6606087016147d9565b91506150c4608087016147d9565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000821982111561510f5761510f6150e6565b500190565b6000600019821415615128576151286150e6565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826151545761515461512f565b500490565b84815283602082015281836040830137600091016040019081529392505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b8183823760009101908152919050565b600181811c908216806151d857607f821691505b60208210811415612fcf57634e487b7160e01b600052602260045260246000fd5b83815282602082015260008251615217816040850160208701614a55565b91909101604001949350505050565b60006020828403121561523857600080fd5b8151612778816149f9565b60006020828403121561525557600080fd5b5051919050565b6000808335601e1984360301811261527357600080fd5b83018035915067ffffffffffffffff82111561528e57600080fd5b602001915036819003821315613f2457600080fd5b6020808252825182820181905260009190848201906040850190845b818110156152db578351835292840192918401916001016152bf565b50909695505050505050565b60006101208a83526001600160a01b03808b16602085015289604085015281606085015260008285015261014091508160808501526153288285018a614a85565b97811660a085015295861660c084015250509190921660e08201527fffffffff0000000000000000000000000000000000000000000000000000000090911661010090910152949350505050565b60006101006001600160a01b03808b16845289602085015281604085015260008285015261012091508160608501526153b18285018a614a85565b978116608085015295861660a084015250509190921660c08201527fffffffff0000000000000000000000000000000000000000000000000000000090911660e0909101529392505050565b60008261540c5761540c61512f565b500690565b600082821015615423576154236150e6565b500390565b6000808212826001600160ff1b0303841381151615615449576154496150e6565b82600160ff1b038412811615615461576154616150e6565b50500190565b6000826154765761547661512f565b600160ff1b821460001984141615615490576154906150e6565b500590565b60006001600160c01b03808316818114156154b2576154b26150e6565b6001019392505050565b60006001600160c01b038216806154d5576154d56150e6565b6000190192915050565b600080831283600160ff1b018312811516156154fd576154fd6150e6565b836001600160ff1b03018313811615615518576155186150e6565b50500390565b6000600160ff1b821415615534576155346150e6565b5060000390565b6000816000190483118215151615615555576155556150e6565b500290565b634e487b7160e01b600052602160045260246000fd5b60008251615582818460208701614a55565b9190910192915050565b60008161559b5761559b6150e6565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220aaf5f98df755a777bf2ab38bf5b0d2b2f9740cb9c1a1c935964c5d11caec024864736f6c6343000809003300000000000000000000000092e5125adf385d86bedb950793526106143b6df100000000000000000000000000000000000000000000000000000000000000800000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a4600000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b0000000000000000000000000000000000000000000000000000000000000010446170695365727665722061646d696e00000000000000000000000000000000
Deployed ByteCode
0x608060405234801561001057600080fd5b506004361061034a5760003560e01c80638e85afe0116101bd578063bcb5daec116100f9578063e18de857116100a2578063f60e45d31161007c578063f60e45d3146108da578063f8b2cb4f1461091b578063fc3c1f8b14610936578063fce90be81461094957600080fd5b8063e18de8571461088d578063e406f3b2146108b4578063e705882e146108c757600080fd5b8063d55a42dd116100d3578063d55a42dd1461082b578063d7c0b87914610867578063dc96acc81461087a57600080fd5b8063bcb5daec1461078c578063c4f95dd51461079f578063d39c8968146107b257600080fd5b80639e14caa811610166578063a6b1b9a211610140578063a6b1b9a21461070f578063ac9650d814610722578063b605bbcd14610742578063b76f3c151461076557600080fd5b80639e14caa8146106c9578063a026658f146106dc578063a5fc076f146106fc57600080fd5b806391eed0851161019757806391eed085146106755780639421240514610688578063977cae011461069b57600080fd5b80638e85afe0146106185780638fca9ab91461063b5780638fe47dd81461064e57600080fd5b80634c8f1d8d1161028c5780636db798f9116102355780637512449b1161020f5780637512449b146105b1578063776bcbd2146105d8578063796b89b9146105eb5780637c706738146105f157600080fd5b80636db798f9146105575780636ed93dd01461059357806370c927ca1461059e57600080fd5b80635bc8a5c8116102665780635bc8a5c8146104f557806365101054146105085780636bd2bdd01461051b57600080fd5b80634c8f1d8d146104b45780634dcc19fe146104c957806358c55edd146104cf57600080fd5b8063206b48f4116102f95780633408e470116102d35780633408e4701461044f5780633af1dbfd1461045557806342cbb15c14610487578063481c6a751461048d57600080fd5b8063206b48f4146104165780632ce010e3146104295780633376e7c21461043c57600080fd5b80631a0a0b3e1161032a5780631a0a0b3e146103af5780631ce9ae07146103c45780631d36cf5c1461040357600080fd5b80629f2f3c1461034f578062aae33f1461038957806304dd3cee1461039c575b600080fd5b6103767f104c9d4f4e43e7ba83944d6212af5b9af9a93525e592ed31e398331520de830181565b6040519081526020015b60405180910390f35b6103766103973660046147a4565b610985565b6103766103aa3660046147f5565b610be1565b6103c26103bd366004614861565b610cca565b005b6103eb7f00000000000000000000000092e5125adf385d86bedb950793526106143b6df181565b6040516001600160a01b039091168152602001610380565b6103c26104113660046148f5565b610e46565b6103c2610424366004614942565b610fb1565b6103c2610437366004614942565b6112ad565b6103c261044a366004614a07565b61132c565b46610376565b610468610463366004614a3c565b611472565b60408051601b9390930b835263ffffffff909116602083015201610380565b43610376565b6103eb7f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a4681565b6104bc611574565b6040516103809190614ab1565b48610376565b6104e26104dd366004614a3c565b611602565b604051601b9190910b8152602001610380565b6103c2610503366004614ac4565b6116c2565b610376610516366004614c69565b6117f6565b6104bc6040518060400160405280601b81526020017f57686974656c6973742065787069726174696f6e20736574746572000000000081525081565b6104bc6040518060400160405280601d81526020017f57686974656c6973742065787069726174696f6e20657874656e64657200000081525081565b6103766305f5e10081565b6104e26105ac366004614a3c565b611d3b565b6103767f492677bd30a08301f65b537cfc8252cb14ea0a0615e504f9101eeffd5f9ab89181565b6103c26105e6366004614d3b565b611e35565b42610376565b6103767f076e9207298350b8044dc8d4d473ffd3008f3aa7c89c9237bf72570348c51fcc81565b61062b610626366004614d77565b611efe565b6040519015158152602001610380565b610376610649366004614a3c565b611fa1565b6103767f01f3b33ad3d1e072f204c5a2fc7623570a125883679903f9c72c2b4b9469aac681565b6103c2610683366004614dbc565b611fe6565b610376610696366004614dde565b6121f6565b61062b6106a9366004614e11565b600460209081526000928352604080842090915290825290205460ff1681565b6103c26106d7366004614e44565b6123e0565b6103766106ea366004614a3c565b60056020526000908152604090205481565b61046861070a366004614a3c565b61249b565b6103c261071d3660046148f5565b612511565b610735610730366004614e7b565b612643565b6040516103809190614ef0565b61062b610750366004614f52565b60036020526000908152604090205460ff1681565b6103eb7f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b81565b61062b61079a366004614f6d565b612738565b6103766107ad3660046147a4565b61277f565b6108036107c0366004614f6d565b6000918252602082815260408084206001600160a01b0393909316845291905290205467ffffffffffffffff811691600160401b9091046001600160c01b031690565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610380565b6104bc6040518060400160405280601681526020017f496e646566696e6974652077686974656c69737465720000000000000000000081525081565b61062b610875366004614f90565b6127af565b61062b610888366004614f90565b6128b4565b6103767f9693655d166f407001527e484736ea4c5dca85db0d0a32bb1dadd4b5310d58a281565b6103c26108c2366004614f52565b6129ea565b6103766108d536600461500a565b612aae565b61062b6108e8366004614d3b565b60009283526001602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b610376610929366004614f52565b6001600160a01b03163190565b610376610944366004615057565b612cb9565b6104bc6040518060400160405280601081526020017f64415049206e616d65207365747465720000000000000000000000000000000081525081565b8051600090600181116109df5760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064015b60405180910390fd5b60008167ffffffffffffffff8111156109fa576109fa6146ce565b604051908082528060200260200182016040528015610a23578160200160208202803683370190505b5090506000805b83811015610abb57600060066000888481518110610a4a57610a4a6150d0565b602090810291909101810151825281019190915260400160002080548551919250601b0b90859084908110610a8157610a816150d0565b60209081029190910101528054610aa590600160e01b900463ffffffff16846150fc565b9250508080610ab390615114565b915050610a2a565b506000610ac88483615145565b9050610ad38661277f565b60008181526006602052604090205490955063ffffffff600160e01b90910481169082161015610b455760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000610b5084612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008e81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925087917fb7712be6248d021e8c56ac9613c09491354a4d0f4ad0b7db1a664b35be4b2349910160405180910390a25050505050919050565b60006001600160a01b038316610c395760405162461bcd60e51b815260206004820152601460248201527f4169726e6f64652061646472657373207a65726f00000000000000000000000060448201526064016109d6565b81610c865760405162461bcd60e51b815260206004820152601060248201527f54656d706c617465204944207a65726f0000000000000000000000000000000060448201526064016109d6565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260540160405160208183030381529060405280519060200120905092915050565b84610cd481612fd5565b610d165760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b876001600160a01b0316610d9584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508c908c908c908c90602001615159565b60405160208183030381529060405280519060200120613001565b9061303c565b6001600160a01b031614610de05760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b6000610dec8989610be1565b90506000610dfc82898989613060565b60408051828152602081018b905291925083917f403078446dab7471f481ca4bffac706bd84a6dba118980d267676c096f2ba924910160405180910390a250505050505050505050565b610e4f33613165565b610e9b5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f74207365742065787069726174696f6e000000000000000000000060448201526064016109d6565b82610eda5760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b038216610f245760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000838152602081815260408083206001600160a01b03861684529091529020805467ffffffffffffffff191667ffffffffffffffff831617905560405167ffffffffffffffff8216815233906001600160a01b0384169085907fd19e89b7d547ccf349211588a9a1d29461e2ce984b1b1cdbe7150976528b86f1906020015b60405180910390a4505050565b84610fbb81612fd5565b610ffd5760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6040516bffffffffffffffffffffffff1960608b811b821660208401528a811b8216603484015289901b166048820152605c0160408051601f19818403018152918152815160209283012060008d81526008909352912054146110a25760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b876001600160a01b0316896001600160a01b0316141561118b57886001600160a01b031661113b84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c903390602001928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b6001600160a01b0316146111865760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611236565b886001600160a01b03166111eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604051610d8f9250610d7491508f908c9033908d908d9060200161517a565b6001600160a01b0316146112365760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b60008a8152600560205260408120549061125282898989613060565b604080518e8152601b83900b602082015263ffffffff8b1681830152905191925083917f2b85d71813342fd0091d4573a5f78e244c2133755e470943c33a6dbf3cf9c15d9181900360600190a2505050505050505050505050565b6112bc610397848601866147a4565b84846040516112cc9291906151b4565b6040518091039020146113215760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b505050505050505050565b611335336132a5565b6113815760405162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742073657420696e646566696e697465207374617475730000000060448201526064016109d6565b826113c05760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b03821661140a5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6000611417848484613356565b6040805184151581526001600160c01b038316602082015291925033916001600160a01b0386169187917f2fa93828cf3f001b9a9f0a7365db04ea068b9db7bcb7a38d289f0cf8aa9ce374910160405180910390a450505050565b60008060008360405160200161148a91815260200190565b6040516020818303038152906040528051906020012090506114ac8133612738565b6114ed5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b600081815260096020526040902054806115495760405162461bcd60e51b815260206004820152601160248201527f64415049206e616d65206e6f742073657400000000000000000000000000000060448201526064016109d6565b600090815260066020526040902054601b81900b95600160e01b90910463ffffffff16945092505050565b60028054611581906151c4565b80601f01602080910402602001604051908101604052809291908181526020018280546115ad906151c4565b80156115fa5780601f106115cf576101008083540402835291602001916115fa565b820191906000526020600020905b8154815290600101906020018083116115dd57829003601f168201915b505050505081565b600061160e8233612738565b61164f5760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b60008281526006602052604090208054600160e01b900463ffffffff166116b85760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b92915050565b336001600160a01b037f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b161461173a5760405162461bcd60e51b815260206004820152601b60248201527f53656e646572206e6f74204169726e6f64652070726f746f636f6c000000000060448201526064016109d6565b8261174481612fd5565b6117865760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b6000858152600760205260408120805490829055906117a782878787613060565b604080518981526020810183905290810188905290915082907f7c88b543c4cda65df046e87397c3152c27098419cb22a90a5e37db30ef621b269060600160405180910390a250505050505050565b84518451600091908114801561180c5750845181145b80156118185750835181145b80156118245750825181145b6118705760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d617463680000000000000060448201526064016109d6565b600181116118c05760405162461bcd60e51b815260206004820152601f60248201527f537065636966696564206c657373207468616e2074776f20426561636f6e730060448201526064016109d6565b60008167ffffffffffffffff8111156118db576118db6146ce565b604051908082528060200260200182016040528015611904578160200160208202803683370190505b50905060008267ffffffffffffffff811115611922576119226146ce565b60405190808252806020026020018201604052801561194b578160200160208202803683370190505b5090506000805b84811015611c105786818151811061196c5761196c6150d0565b602002602001015151600014611b555760008b8281518110611990576119906150d0565b6020026020010151905060008a83815181106119ae576119ae6150d0565b602002602001015190506119c181612fd5565b611a035760405162461bcd60e51b8152602060048201526013602482015272151a5b595cdd185b5c081b9bdd081d985b1a59606a1b60448201526064016109d6565b816001600160a01b0316611a748a8581518110611a2257611a226150d0565b6020026020010151610d8f8f8781518110611a3f57611a3f6150d0565b6020026020010151858f8981518110611a5a57611a5a6150d0565b6020026020010151604051602001610d74939291906151f9565b6001600160a01b031614611abf5760405162461bcd60e51b81526020600482015260126024820152710a6d2cedcc2e8eae4ca40dad2e6dac2e8c6d60731b60448201526064016109d6565b611ae18a8481518110611ad457611ad46150d0565b602002602001015161350e565b601b0b858481518110611af657611af66150d0565b6020908102919091010152611b0b81856150fc565b9350611b30828d8581518110611b2357611b236150d0565b6020026020010151610be1565b868481518110611b4257611b426150d0565b6020026020010181815250505050611bfe565b6000611b868c8381518110611b6c57611b6c6150d0565b60200260200101518c8481518110611b2357611b236150d0565b6000818152600660205260409020805486519293509091601b9190910b90869085908110611bb657611bb66150d0565b60209081029190910101528054611bda90600160e01b900463ffffffff16856150fc565b935081868481518110611bef57611bef6150d0565b60200260200101818152505050505b80611c0881615114565b915050611952565b50611c1a8361277f565b94506000611c288583615145565b60008781526006602052604090205490915063ffffffff600160e01b90910481169082161015611c9a5760405162461bcd60e51b815260206004820152601660248201527f557064617465642076616c7565206f757464617465640000000000000000000060448201526064016109d6565b6000611ca584612e6d565b604080518082018252601b83900b80825263ffffffff868116602080850182815260008f81526006835287902095519051909316600160e01b026001600160e01b0390931692909217909355835191825281019190915291925088917fb34747dc40d9c985b4857c2955fec7a8f34d88bc06da72f43319795758800407910160405180910390a250505050505095945050505050565b60008082604051602001611d5191815260200190565b604051602081830303815290604052805190602001209050611d738133612738565b611db45760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b6000818152600960209081526040808320548352600690915290208054600160e01b900463ffffffff16611e2a5760405162461bcd60e51b815260206004820152601860248201527f44617461206665656420646f6573206e6f74206578697374000000000000000060448201526064016109d6565b54601b0b9392505050565b611e3e816132a5565b15611e8b5760405162461bcd60e51b815260206004820181905260248201527f7365747465722063616e2073657420696e646566696e6974652073746174757360448201526064016109d6565b600080611e99858585613611565b915091508115611ef757604080513381526001600160c01b03831660208201526001600160a01b03808616929087169188917f29c394c1d92801cab93215bf9cd50ae38d23341be6540f27d80ee2bc8a541237910160405180910390a45b5050505050565b600080611f0a8461277f565b600081815260066020908152604091829020825180840190935254601b81900b8352600160e01b900463ffffffff1690820152909150611f4985610985565b506000828152600660205260409020815181548691611f6a91601b0b6136fc565b101580611f975750602082015163ffffffff16158015611f9757508054600160e01b900463ffffffff1615155b9695505050505050565b60006009600083604051602001611fba91815260200190565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b816120335760405162461bcd60e51b815260206004820152600e60248201527f64415049206e616d65207a65726f00000000000000000000000000000000000060448201526064016109d6565b336001600160a01b037f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a461614806121225750604051632474521560e21b81527f492677bd30a08301f65b537cfc8252cb14ea0a0615e504f9101eeffd5f9ab89160048201523360248201527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df16001600160a01b0316906391d148549060440160206040518083038186803b1580156120ea57600080fd5b505afa1580156120fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121229190615226565b61216e5760405162461bcd60e51b815260206004820152601b60248201527f53656e6465722063616e6e6f74207365742064415049206e616d65000000000060448201526064016109d6565b80600960008460405160200161218691815260200190565b60405160208183030381529060405280519060200120815260200190815260200160002081905550336001600160a01b0316827ff3a9aac9b6ac0f842cb5d9b3491cd5fc1b6a6778d97fd9529f587339865294f5836040516121ea91815260200190565b60405180910390a35050565b6000816001600160a01b03811633148061223357506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b61227f5760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b600061228b8686610be1565b6040517feebecf690000000000000000000000000000000000000000000000000000000081526001600160a01b0388811660048301526024820188905260a06044830152600060a48301528681166064830152630b7914b960e31b60848301529192507f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b9091169063eebecf699060c401602060405180830381600087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236e9190615243565b60008181526007602090815260409182902084905581518381526001600160a01b038a811692820192909252918201889052919450339186169083907fc0e8c8735457dd46668d6c01832656e0803b41b1e61ba12354a24f11e114eab39060600160405180910390a450509392505050565b6001600160a01b0382166124365760405162461bcd60e51b815260206004820152601560248201527f55706461746520726571756573746572207a65726f000000000000000000000060448201526064016109d6565b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fba239f737a4075f5efb0d649e16cd7d2b1978690876b079dab642f077050b27991016121ea565b6000806124a88333612738565b6124e95760405162461bcd60e51b815260206004820152601260248201527114d95b99195c8818d85b9b9bdd081c99585960721b60448201526064016109d6565b5050600090815260066020526040902054601b81900b91600160e01b90910463ffffffff1690565b61251a33613775565b6125665760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420657874656e642065787069726174696f6e000000000000000060448201526064016109d6565b826125a55760405162461bcd60e51b815260206004820152600f60248201526e53657276696365204944207a65726f60881b60448201526064016109d6565b6001600160a01b0382166125ef5760405162461bcd60e51b8152602060048201526011602482015270557365722061646472657373207a65726f60781b60448201526064016109d6565b6125fa838383613826565b60405167ffffffffffffffff8216815233906001600160a01b0384169085907fa9e0c89b898eb7a904617915dc5b5510d539c899810042e9248569b54b9cc2ed90602001610fa4565b60608167ffffffffffffffff81111561265e5761265e6146ce565b60405190808252806020026020018201604052801561269157816020015b606081526020019060019003908161267c5790505b50905060005b8281101561273157612701308585848181106126b5576126b56150d0565b90506020028101906126c7919061525c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506138a392505050565b828281518110612713576127136150d0565b6020026020010181905250808061272990615114565b915050612697565b5092915050565b60006001600160a01b0382161580612755575061275583836138c8565b8061277857506001600160a01b03821660009081526003602052604090205460ff165b9392505050565b60008160405160200161279291906152a3565b604051602081830303815290604052805190602001209050919050565b600033156127ff5760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600061280d858701876147a4565b9050858560405161281f9291906151b4565b60405180910390208160405160200161283891906152a3565b604051602081830303815290604052805190602001201461289b5760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6128a981610626868661391b565b979650505050505050565b600033156129045760405162461bcd60e51b815260206004820152601760248201527f53656e646572206e6f74207a65726f206164647265737300000000000000000060448201526064016109d6565b600086815260056020526040902054806129605760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e206e6f742072656769737465726564000000000060448201526064016109d6565b6000818152600660205260409020612978858561391b565b8154604080516020601f8b018190048102820181019092528981526129c592601b0b916129c091908c908c908190840183828082843760009201919091525061350e92505050565b6136fc565b1015806129de57508054600160e01b900463ffffffff16155b98975050505050505050565b336001600160a01b037f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a461614612a625760405162461bcd60e51b815260206004820152601260248201527f53656e646572206e6f74206d616e61676572000000000000000000000000000060448201526064016109d6565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f6d71fde11d6c56eaf0154d464b51df0b26dcdac118703056ea3da805fc9e1cd79190a250565b6000816001600160a01b038116331480612aeb57506001600160a01b038116600090815260046020908152604080832033845290915290205460ff165b612b375760405162461bcd60e51b815260206004820152601460248201527f53656e646572206e6f74207065726d697474656400000000000000000000000060448201526064016109d6565b6000612b438787610be1565b6040517fa7e0c85e0000000000000000000000000000000000000000000000000000000081526001600160a01b0389811660048301526024820189905260c06044830152600060c483015287811660648301528681166084830152630b7914b960e31b60a48301529192507f00000000000000000000000073ff8cdeea2770ba2b8ba1fd000090a7dd598f0b9091169063a7e0c85e9060e401602060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2e9190615243565b600081815260076020526040908190208390555190935033906001600160a01b0386169083907faeef85c84fba3ef952ca2738d951d30f5093595cb5eb9830764f5b49a39442a390612ca79088908d908c908e909384526001600160a01b03928316602085015291166040830152606082015260800190565b60405180910390a45050949350505050565b60006001600160a01b038316612d115760405162461bcd60e51b815260206004820152601460248201527f52656c617965722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b6001600160a01b038216612d675760405162461bcd60e51b815260206004820152601460248201527f53706f6e736f722061646472657373207a65726f00000000000000000000000060448201526064016109d6565b604051612d8d90469088908890889088908890309063081ad23d60e21b906020016152e7565b60408051601f1981840301815282825280516020918201206bffffffffffffffffffffffff1960608b811b82168487015288811b8216603487015287901b1660488501528251808503603c018152605c909401835283519382019390932060008481526008909252919020559050612e058686610be1565b60008281526005602052604090819020919091555181907f82b139c5e690a3a4a9c2f68133d2ab1f7724bf8a69ad3fe1cd9f5a4923cf41b290612e5c9089908990899089908990309063081ad23d60e21b90615376565b60405180910390a295945050505050565b805160009060098111612f2d57612e8383613979565b612e8e6002826153fd565b60011415612ec25782612ea2600283615145565b81518110612eb257612eb26150d0565b6020026020010151915050919050565b600283612ecf8284615145565b81518110612edf57612edf6150d0565b6020026020010151846001600285612ef79190615145565b612f019190615411565b81518110612f1157612f116150d0565b6020026020010151612f239190615428565b6127789190615467565b612f386002826153fd565b60011415612f555782612ea281612f50600285615145565b613e90565b600080612f78856001612f69600287615145565b612f739190615411565b613eae565b915091506002858281518110612f9057612f906150d0565b6020026020010151868481518110612faa57612faa6150d0565b6020026020010151612fbc9190615428565b612fc69190615467565b95945050505050565b50919050565b600042612fe483610e106150fc565b118015612ffb5750612ff8426103846150fc565b82105b92915050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01612792565b600080600061304b8585613f2b565b9150915061305881613f98565b509392505050565b60006130a183838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061350e92505050565b600086815260066020526040902054601b9190910b9150600160e01b900463ffffffff1684116131135760405162461bcd60e51b815260206004820152601d60248201527f46756c66696c6c6d656e74206f6c646572207468616e20426561636f6e00000060448201526064016109d6565b604080518082018252601b83900b815263ffffffff95861660208083019182526000988952600690529190962095519051909416600160e01b026001600160e01b039094169390931790935550919050565b6000816001600160a01b03167f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a466001600160a01b03161480612ffb5750604051632474521560e21b81527f01f3b33ad3d1e072f204c5a2fc7623570a125883679903f9c72c2b4b9469aac660048201526001600160a01b0383811660248301527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df116906391d14854906044015b60206040518083038186803b15801561322a57600080fd5b505afa15801561323e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffb9190615226565b6000928352602083815260408085206001600160a01b039490941685529290529120805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b6000816001600160a01b03167f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a466001600160a01b03161480612ffb5750604051632474521560e21b81527f9693655d166f407001527e484736ea4c5dca85db0d0a32bb1dadd4b5310d58a260048201526001600160a01b0383811660248301527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df116906391d1485490604401613212565b6000838152602081815260408083206001600160a01b0386168452909152902054600160401b90046001600160c01b03168180156133bf575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff16155b1561344c5760008481526001602081815260408084206001600160a01b03881685528252808420338552909152909120805460ff191690911790558061340481615495565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b0384160217905591506127789050565b81158015613484575060008481526001602090815260408083206001600160a01b0387168452825280832033845290915290205460ff165b156127785760008481526001602090815260408083206001600160a01b038716845282528083203384529091529020805460ff19169055806134c5816154bc565b6000868152602081815260408083206001600160a01b03891684529091529020805467ffffffffffffffff16600160401b6001600160c01b038416021790559150509392505050565b600081516020146135615760405162461bcd60e51b815260206004820152601760248201527f44617461206c656e677468206e6f7420636f727265637400000000000000000060448201526064016109d6565b6000828060200190518101906135779190615243565b90507fffffffff8000000000000000000000000000000000000000000000000000000081128015906135c557507b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff8113155b612ffb5760405162461bcd60e51b815260206004820152601760248201527f56616c7565207479706563617374696e67206572726f7200000000000000000060448201526064016109d6565b6000838152602081815260408083206001600160a01b0386811680865291845282852054888652600185528386209286529184528285209086168552909252822054600160401b9091046001600160c01b03169060ff16156136f45760008581526001602090815260408083206001600160a01b03808916855290835281842090871684529091529020805460ff19169055806136ad816154bc565b6000878152602081815260408083206001600160a01b038a1684529091529020805467ffffffffffffffff16600160401b6001600160c01b03841602179055600193509150505b935093915050565b60008083601b0b83601b0b61371191906154df565b9050600080821361372a576137258261551e565b61372c565b815b905060008086601b0b1361374c5785601b0b6137479061551e565b613751565b85601b0b5b90508061375c575060015b8061376b6305f5e1008461553b565b611f979190615145565b6000816001600160a01b03167f0000000000000000000000007fc103cc17eb1764c14f8a1ac4745445cea62a466001600160a01b03161480612ffb5750604051632474521560e21b81527f076e9207298350b8044dc8d4d473ffd3008f3aa7c89c9237bf72570348c51fcc60048201526001600160a01b0383811660248301527f00000000000000000000000092e5125adf385d86bedb950793526106143b6df116906391d1485490604401613212565b6000838152602081815260408083206001600160a01b038616845290915290205467ffffffffffffffff908116908216116132625760405162461bcd60e51b815260206004820152601a60248201527f446f6573206e6f7420657874656e642065787069726174696f6e00000000000060448201526064016109d6565b606061277883836040518060600160405280602781526020016155a460279139614156565b6000828152602081815260408083206001600160a01b038516845290915281208054600160401b90046001600160c01b0316151580613913575080544267ffffffffffffffff909116115b949350505050565b60006020821461396d5760405162461bcd60e51b815260206004820152601a60248201527f496e636f727265637420706172616d65746572206c656e67746800000000000060448201526064016109d6565b61277882840184614a3c565b805160098111156139cc5760405162461bcd60e51b815260206004820152601660248201527f417272617920746f6f206c6f6e6720746f20736f72740000000000000000000060448201526064016109d6565b6006811015613ada576004811015613a28578060031415613a12576139f48260006001614237565b613a018260016002614237565b613a0e8260006001614237565b5050565b8060021415613a0e57613a0e8260006001614237565b8060051415613aa657613a3e8260016002614237565b613a4b8260036004614237565b613a588260016003614237565b613a658260006002614237565b613a728260026004614237565b613a7f8260006003614237565b613a8c8260006001614237565b613a998260026003614237565b613a0e8260016002614237565b613ab38260006001614237565b613ac08260026003614237565b613acd8260016003614237565b613a998260006002614237565b6008811015613c58578060071415613bbc57613af98260016002614237565b613b068260036004614237565b613b138260056006614237565b613b208260006002614237565b613b2d8260046006614237565b613b3a8260036005614237565b613b478260026006614237565b613b548260016005614237565b613b618260006004614237565b613b6e8260026005614237565b613b7b8260006003614237565b613b888260026004614237565b613b958260016003614237565b613ba28260006001614237565b613baf8260026003614237565b613a0e8260046005614237565b613bc98260006001614237565b613bd68260026003614237565b613be38260046005614237565b613bf08260016003614237565b613bfd8260036005614237565b613c0a8260016003614237565b613c178260026004614237565b613c248260006002614237565b613c318260026004614237565b613c3e8260036004614237565b613c4b8260016002614237565b613a0e8260026003614237565b8060091415613d8c57613c6e8260016008614237565b613c7b8260026007614237565b613c888260036006614237565b613c958260046005614237565b613ca28260016004614237565b613caf8260056008614237565b613cbc8260006002614237565b613cc98260066007614237565b613cd68260026006614237565b613ce38260076008614237565b613cf08260006003614237565b613cfd8260046005614237565b613d0a8260006001614237565b613d178260036005614237565b613d248260066007614237565b613d318260026004614237565b613d3e8260016003614237565b613d4b8260056007614237565b613d588260046006614237565b613d658260016002614237565b613d728260036004614237565b613d7f8260056006614237565b613ba28260076008614237565b613d998260006007614237565b613da68260016006614237565b613db38260026005614237565b613dc08260036004614237565b613dcd8260006003614237565b613dda8260046007614237565b613de78260016002614237565b613df48260056006614237565b613e018260006001614237565b613e0e8260026003614237565b613e1b8260046005614237565b613e288260066007614237565b613e358260036005614237565b613e428260026004614237565b613e4f8260016002614237565b613e5c8260036004614237565b613e698260056006614237565b613e768260026003614237565b613e838260046005614237565b613a0e8260036004614237565b600061305883600060018651613ea69190615411565b8560006142e5565b8151600090819060018111613f055760405162461bcd60e51b815260206004820152601d60248201527f417272617920746f6f2073686f727420746f2073656c656374206b2b3100000060448201526064016109d6565b613f1e856000613f16600185615411565b8760016142e5565b92509250505b9250929050565b600080825160411415613f625760208301516040840151606085015160001a613f56878285856143e0565b94509450505050613f24565b825160401415613f8c5760208301516040840151613f818683836144cd565b935093505050613f24565b50600090506002613f24565b6000816004811115613fac57613fac61555a565b1415613fb55750565b6001816004811115613fc957613fc961555a565b14156140175760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016109d6565b600281600481111561402b5761402b61555a565b14156140795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016109d6565b600381600481111561408d5761408d61555a565b14156140e65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016109d6565b60048160048111156140fa576140fa61555a565b14156141535760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016109d6565b50565b6060833b6141cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016109d6565b600080856001600160a01b0316856040516141e79190615570565b600060405180830381855af49150503d8060008114614222576040519150601f19603f3d011682016040523d82523d6000602084013e614227565b606091505b5091509150611f978282866144fc565b828181518110614249576142496150d0565b6020026020010151838381518110614263576142636150d0565b602002602001015113156142e057828181518110614283576142836150d0565b602002602001015183838151811061429d5761429d6150d0565b60200260200101518484815181106142b7576142b76150d0565b602002602001018584815181106142d0576142d06150d0565b6020908102919091010191909152525b505050565b600080848614156142fb575082905060006143d6565b6000614308888888614535565b9050808510156143325761432a8888614322600185615411565b8860006142e5565b509250614356565b808511156143525761432a886143498360016150fc565b888860006142e5565b8092505b83156143d4576143678360016150fc565b915060006143768360016150fc565b90505b88518110156143d257888381518110614394576143946150d0565b60200260200101518982815181106143ae576143ae6150d0565b602002602001015112156143c0578092505b806143ca81615114565b915050614379565b505b505b9550959350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561441757506000905060036144c4565b8460ff16601b1415801561442f57508460ff16601c14155b1561444057506000905060046144c4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614494573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166144bd576000600192509250506144c4565b9150600090505b94509492505050565b6000806001600160ff1b03831660ff84901c601b016144ee878288856143e0565b935093505050935093915050565b6060831561450b575081612778565b82511561451b5782518084602001fd5b8160405162461bcd60e51b81526004016109d69190614ab1565b600081831415614546575081612778565b600084848151811061455a5761455a6150d0565b60209081029190910101519050836145738460016150fc565b92505b8061458081615114565b9150508551811080156145ab5750818682815181106145a1576145a16150d0565b6020026020010151125b614576575b826145ba8161558c565b935050818684815181106145d0576145d06150d0565b6020026020010151136145b05782811061465b578583815181106145f6576145f66150d0565b6020026020010151868681518110614610576146106150d0565b602002602001015187878151811061462a5761462a6150d0565b60200260200101888681518110614643576146436150d0565b60200260200101828152508281525050505050612778565b85838151811061466d5761466d6150d0565b6020026020010151868281518110614687576146876150d0565b60200260200101518783815181106146a1576146a16150d0565b602002602001018886815181106146ba576146ba6150d0565b602090810291909101019190915252614576565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561470d5761470d6146ce565b604052919050565b600067ffffffffffffffff82111561472f5761472f6146ce565b5060051b60200190565b600082601f83011261474a57600080fd5b8135602061475f61475a83614715565b6146e4565b82815260059290921b8401810191818101908684111561477e57600080fd5b8286015b848110156147995780358352918301918301614782565b509695505050505050565b6000602082840312156147b657600080fd5b813567ffffffffffffffff8111156147cd57600080fd5b61391384828501614739565b80356001600160a01b03811681146147f057600080fd5b919050565b6000806040838503121561480857600080fd5b614811836147d9565b946020939093013593505050565b60008083601f84011261483157600080fd5b50813567ffffffffffffffff81111561484957600080fd5b602083019150836020828501011115613f2457600080fd5b600080600080600080600060a0888a03121561487c57600080fd5b614885886147d9565b96506020880135955060408801359450606088013567ffffffffffffffff808211156148b057600080fd5b6148bc8b838c0161481f565b909650945060808a01359150808211156148d557600080fd5b506148e28a828b0161481f565b989b979a50959850939692959293505050565b60008060006060848603121561490a57600080fd5b8335925061491a602085016147d9565b9150604084013567ffffffffffffffff8116811461493757600080fd5b809150509250925092565b600080600080600080600080600060e08a8c03121561496057600080fd5b8935985061497060208b016147d9565b975061497e60408b016147d9565b965061498c60608b016147d9565b955060808a0135945060a08a013567ffffffffffffffff808211156149b057600080fd5b6149bc8d838e0161481f565b909650945060c08c01359150808211156149d557600080fd5b506149e28c828d0161481f565b915080935050809150509295985092959850929598565b801515811461415357600080fd5b600080600060608486031215614a1c57600080fd5b83359250614a2c602085016147d9565b91506040840135614937816149f9565b600060208284031215614a4e57600080fd5b5035919050565b60005b83811015614a70578181015183820152602001614a58565b83811115614a7f576000848401525b50505050565b60008151808452614a9d816020860160208601614a55565b601f01601f19169290920160200192915050565b6020815260006127786020830184614a85565b60008060008060608587031215614ada57600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614aff57600080fd5b614b0b8782880161481f565b95989497509550505050565b600082601f830112614b2857600080fd5b81356020614b3861475a83614715565b82815260059290921b84018101918181019086841115614b5757600080fd5b8286015b8481101561479957614b6c816147d9565b8352918301918301614b5b565b600082601f830112614b8a57600080fd5b813567ffffffffffffffff811115614ba457614ba46146ce565b614bb7601f8201601f19166020016146e4565b818152846020838601011115614bcc57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614bfa57600080fd5b81356020614c0a61475a83614715565b82815260059290921b84018101918181019086841115614c2957600080fd5b8286015b8481101561479957803567ffffffffffffffff811115614c4d5760008081fd5b614c5b8986838b0101614b79565b845250918301918301614c2d565b600080600080600060a08688031215614c8157600080fd5b853567ffffffffffffffff80821115614c9957600080fd5b614ca589838a01614b17565b96506020880135915080821115614cbb57600080fd5b614cc789838a01614739565b95506040880135915080821115614cdd57600080fd5b614ce989838a01614739565b94506060880135915080821115614cff57600080fd5b614d0b89838a01614be9565b93506080880135915080821115614d2157600080fd5b50614d2e88828901614be9565b9150509295509295909350565b600080600060608486031215614d5057600080fd5b83359250614d60602085016147d9565b9150614d6e604085016147d9565b90509250925092565b60008060408385031215614d8a57600080fd5b823567ffffffffffffffff811115614da157600080fd5b614dad85828601614739565b95602094909401359450505050565b60008060408385031215614dcf57600080fd5b50508035926020909101359150565b600080600060608486031215614df357600080fd5b614dfc846147d9565b925060208401359150614d6e604085016147d9565b60008060408385031215614e2457600080fd5b614e2d836147d9565b9150614e3b602084016147d9565b90509250929050565b60008060408385031215614e5757600080fd5b614e60836147d9565b91506020830135614e70816149f9565b809150509250929050565b60008060208385031215614e8e57600080fd5b823567ffffffffffffffff80821115614ea657600080fd5b818501915085601f830112614eba57600080fd5b813581811115614ec957600080fd5b8660208260051b8501011115614ede57600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614f4557603f19888603018452614f33858351614a85565b94509285019290850190600101614f17565b5092979650505050505050565b600060208284031215614f6457600080fd5b612778826147d9565b60008060408385031215614f8057600080fd5b82359150614e3b602084016147d9565b600080600080600060608688031215614fa857600080fd5b85359450602086013567ffffffffffffffff80821115614fc757600080fd5b614fd389838a0161481f565b90965094506040880135915080821115614fec57600080fd5b50614ff98882890161481f565b969995985093965092949392505050565b6000806000806080858703121561502057600080fd5b615029856147d9565b93506020850135925061503e604086016147d9565b915061504c606086016147d9565b905092959194509250565b600080600080600060a0868803121561506f57600080fd5b615078866147d9565b945060208601359350604086013567ffffffffffffffff81111561509b57600080fd5b6150a788828901614b79565b9350506150b6606087016147d9565b91506150c4608087016147d9565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000821982111561510f5761510f6150e6565b500190565b6000600019821415615128576151286150e6565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826151545761515461512f565b500490565b84815283602082015281836040830137600091016040019081529392505050565b8581528460208201526bffffffffffffffffffffffff198460601b1660408201528183605483013760009101605401908152949350505050565b8183823760009101908152919050565b600181811c908216806151d857607f821691505b60208210811415612fcf57634e487b7160e01b600052602260045260246000fd5b83815282602082015260008251615217816040850160208701614a55565b91909101604001949350505050565b60006020828403121561523857600080fd5b8151612778816149f9565b60006020828403121561525557600080fd5b5051919050565b6000808335601e1984360301811261527357600080fd5b83018035915067ffffffffffffffff82111561528e57600080fd5b602001915036819003821315613f2457600080fd5b6020808252825182820181905260009190848201906040850190845b818110156152db578351835292840192918401916001016152bf565b50909695505050505050565b60006101208a83526001600160a01b03808b16602085015289604085015281606085015260008285015261014091508160808501526153288285018a614a85565b97811660a085015295861660c084015250509190921660e08201527fffffffff0000000000000000000000000000000000000000000000000000000090911661010090910152949350505050565b60006101006001600160a01b03808b16845289602085015281604085015260008285015261012091508160608501526153b18285018a614a85565b978116608085015295861660a084015250509190921660c08201527fffffffff0000000000000000000000000000000000000000000000000000000090911660e0909101529392505050565b60008261540c5761540c61512f565b500690565b600082821015615423576154236150e6565b500390565b6000808212826001600160ff1b0303841381151615615449576154496150e6565b82600160ff1b038412811615615461576154616150e6565b50500190565b6000826154765761547661512f565b600160ff1b821460001984141615615490576154906150e6565b500590565b60006001600160c01b03808316818114156154b2576154b26150e6565b6001019392505050565b60006001600160c01b038216806154d5576154d56150e6565b6000190192915050565b600080831283600160ff1b018312811516156154fd576154fd6150e6565b836001600160ff1b03018313811615615518576155186150e6565b50500390565b6000600160ff1b821415615534576155346150e6565b5060000390565b6000816000190483118215151615615555576155556150e6565b500290565b634e487b7160e01b600052602160045260246000fd5b60008251615582818460208701614a55565b9190910192915050565b60008161559b5761559b6150e6565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220aaf5f98df755a777bf2ab38bf5b0d2b2f9740cb9c1a1c935964c5d11caec024864736f6c63430008090033