Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
FeeQuoter
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 8000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IFeeQuoter} from "./interfaces/IFeeQuoter.sol";
import {IPriceRegistry} from "./interfaces/IPriceRegistry.sol";
import {IReceiver} from "@chainlink/contracts/src/v0.8/keystone/interfaces/IReceiver.sol";
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";
import {Client} from "./libraries/Client.sol";
import {Internal} from "./libraries/Internal.sol";
import {Pool} from "./libraries/Pool.sol";
import {USDPriceWith18Decimals} from "./libraries/USDPriceWith18Decimals.sol";
import {KeystoneFeedsPermissionHandler} from "@chainlink/contracts/src/v0.8/keystone/KeystoneFeedsPermissionHandler.sol";
import {KeystoneFeedDefaultMetadataLib} from
"@chainlink/contracts/src/v0.8/keystone/lib/KeystoneFeedDefaultMetadataLib.sol";
import {AuthorizedCallers} from "@chainlink/contracts/src/v0.8/shared/access/AuthorizedCallers.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {IERC165} from
"@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol";
import {EnumerableSet} from
"@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol";
/// @notice The FeeQuoter contract responsibility is to:
/// - Store the current gas price in USD for a given destination chain.
/// - Store the price of a token in USD allowing the owner or priceUpdater to update this value.
/// - Manage chain specific fee calculations.
/// The authorized callers in the contract represent the fee price updaters.
contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver, KeystoneFeedsPermissionHandler {
using EnumerableSet for EnumerableSet.AddressSet;
using USDPriceWith18Decimals for uint224;
using KeystoneFeedDefaultMetadataLib for bytes;
error TokenNotSupported(address token);
error FeeTokenNotSupported(address token);
error StaleGasPrice(uint64 destChainSelector, uint256 threshold, uint256 timePassed);
error DataFeedValueOutOfUint224Range();
error InvalidDestBytesOverhead(address token, uint32 destBytesOverhead);
error MessageGasLimitTooHigh();
error MessageComputeUnitLimitTooHigh();
error DestinationChainNotEnabled(uint64 destChainSelector);
error ExtraArgOutOfOrderExecutionMustBeTrue();
error InvalidExtraArgsTag();
error InvalidExtraArgsData();
error SourceTokenDataTooLarge(address token);
error InvalidDestChainConfig(uint64 destChainSelector);
error MessageFeeTooHigh(uint256 msgFeeJuels, uint256 maxFeeJuelsPerMsg);
error InvalidStaticConfig();
error MessageTooLarge(uint256 maxSize, uint256 actualSize);
error UnsupportedNumberOfTokens(uint256 numberOfTokens, uint256 maxNumberOfTokensPerMsg);
error InvalidFeeRange(uint256 minFeeUSDCents, uint256 maxFeeUSDCents);
error InvalidChainFamilySelector(bytes4 chainFamilySelector);
error InvalidTokenReceiver();
error TooManySVMExtraArgsAccounts(uint256 numAccounts, uint256 maxAccounts);
error InvalidSVMExtraArgsWritableBitmap(uint64 accountIsWritableBitmap, uint256 numAccounts);
event FeeTokenAdded(address indexed feeToken);
event FeeTokenRemoved(address indexed feeToken);
event UsdPerUnitGasUpdated(uint64 indexed destChain, uint256 value, uint256 timestamp);
event UsdPerTokenUpdated(address indexed token, uint256 value, uint256 timestamp);
event PriceFeedPerTokenUpdated(address indexed token, TokenPriceFeedConfig priceFeedConfig);
event TokenTransferFeeConfigUpdated(
uint64 indexed destChainSelector, address indexed token, TokenTransferFeeConfig tokenTransferFeeConfig
);
event TokenTransferFeeConfigDeleted(uint64 indexed destChainSelector, address indexed token);
event PremiumMultiplierWeiPerEthUpdated(address indexed token, uint64 premiumMultiplierWeiPerEth);
event DestChainConfigUpdated(uint64 indexed destChainSelector, DestChainConfig destChainConfig);
event DestChainAdded(uint64 indexed destChainSelector, DestChainConfig destChainConfig);
/// @dev Contains token price configuration used in both the keystone price updates and the price feed fallback logic.
struct TokenPriceFeedConfig {
address dataFeedAddress; // ─╮ Price feed contract. Can be address(0) to indicate no feed is configured.
uint8 tokenDecimals; // │ Decimals of the token, used for both keystone and price feed decimal multiplications.
bool isEnabled; // ──────────╯ Whether the token is configured to receive keystone and/or price feed updates.
}
/// @dev Token price data feed update.
struct TokenPriceFeedUpdate {
address sourceToken; // Source token to update feed for.
TokenPriceFeedConfig feedConfig; // Feed config update data.
}
/// @dev Struct that contains the static configuration.
/// RMN depends on this struct, if changing, please notify the RMN maintainers.
// solhint-disable-next-line gas-struct-packing
struct StaticConfig {
uint96 maxFeeJuelsPerMsg; // ─╮ Maximum fee that can be charged for a message.
address linkToken; // ────────╯ LINK token address.
// The amount of time a token price can be stale before it is considered invalid. Gas price staleness is configured
// per dest chain.
uint32 tokenPriceStalenessThreshold;
}
/// @dev The struct representing the received CCIP feed report from keystone IReceiver.onReport().
struct ReceivedCCIPFeedReport {
address token; // Token address.
uint224 price; // ────╮ Price of the token in USD with 18 decimals.
uint32 timestamp; // ─╯ Timestamp of the price update.
}
/// @dev Struct to hold the fee & validation configs for a destination chain.
struct DestChainConfig {
bool isEnabled; // ─────────────────────────╮ Whether this destination chain is enabled.
uint16 maxNumberOfTokensPerMsg; // │ Maximum number of distinct ERC20 tokens transferred per message.
uint32 maxDataBytes; // │ Maximum data payload size in bytes.
uint32 maxPerMsgGasLimit; // │ Maximum gas limit for messages targeting EVMs.
uint32 destGasOverhead; // │ Gas charged on top of the gasLimit to cover destination chain costs.
uint8 destGasPerPayloadByteBase; // │ Default dest-chain gas charged each byte of `data` payload.
uint8 destGasPerPayloadByteHigh; // │ High dest-chain gas charged each byte of `data` payload, used to account for eip-7623.
uint16 destGasPerPayloadByteThreshold; // │ The value at which the billing switches from destGasPerPayloadByteBase to destGasPerPayloadByteHigh.
uint32 destDataAvailabilityOverheadGas; // │ Data availability gas charged for overhead costs e.g. for OCR.
uint16 destGasPerDataAvailabilityByte; // │ Gas units charged per byte of message data that needs availability.
uint16 destDataAvailabilityMultiplierBps; //│ Multiplier for data availability gas, multiples of bps, or 0.0001.
bytes4 chainFamilySelector; // │ Selector that identifies the destination chain's family. Used to determine the correct validations to perform for the dest chain.
bool enforceOutOfOrder; // ─────────────────╯ Whether to enforce the allowOutOfOrderExecution extraArg value to be true.
// The following three properties are defaults, they can be overridden by setting the TokenTransferFeeConfig for a token.
uint16 defaultTokenFeeUSDCents; // ────╮ Default token fee charged per token transfer.
uint32 defaultTokenDestGasOverhead; // │ Default gas charged to execute a token transfer on the destination chain.
uint32 defaultTxGasLimit; // │ Default gas limit for a tx.
uint64 gasMultiplierWeiPerEth; // │ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost.
uint32 gasPriceStalenessThreshold; // │ The amount of time a gas price can be stale before it is considered invalid (0 means disabled).
uint32 networkFeeUSDCents; // ─────────╯ Flat network fee to charge for messages, multiples of 0.01 USD.
}
/// @dev Struct to hold the configs and its destination chain selector. Same as DestChainConfig but with the
/// destChainSelector so that an array of these can be passed in the constructor and applyDestChainConfigUpdates.
/// solhint-disable gas-struct-packing
struct DestChainConfigArgs {
uint64 destChainSelector; // Destination chain selector.
DestChainConfig destChainConfig; // Config to update for the chain selector.
}
/// @dev Struct with transfer fee configuration for token transfers.
struct TokenTransferFeeConfig {
uint32 minFeeUSDCents; // ───╮ Minimum fee to charge per token transfer, multiples of 0.01 USD.
uint32 maxFeeUSDCents; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD.
uint16 deciBps; // │ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5.
uint32 destGasOverhead; // │ Gas charged to execute the token transfer on the destination chain.
// │ Data availability bytes that are returned from the source pool and sent to the dest
uint32 destBytesOverhead; // │ pool. Must be >= Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES. Set as multiple of 32 bytes.
bool isEnabled; // ──────────╯ Whether this token has custom transfer fees.
}
/// @dev Struct with token transfer fee configurations for a token, same as TokenTransferFeeConfig but with the token
/// address included.
struct TokenTransferFeeConfigSingleTokenArgs {
address token; // Token address.
TokenTransferFeeConfig tokenTransferFeeConfig; // Struct to hold the transfer fee configuration for token transfers.
}
/// @dev Struct with args for setting the token transfer fee configurations for a destination chain and a set of tokens.
struct TokenTransferFeeConfigArgs {
uint64 destChainSelector; // Destination chain selector.
TokenTransferFeeConfigSingleTokenArgs[] tokenTransferFeeConfigs; // Array of token transfer fee configurations.
}
/// @dev Struct with a pair of destination chain selector and token address so that an array of these can be passed in
/// the applyTokenTransferFeeConfigUpdates function to remove the token transfer fee configuration for a token.
struct TokenTransferFeeConfigRemoveArgs {
uint64 destChainSelector; // ─╮ Destination chain selector.
address token; // ────────────╯ Token address.
}
/// @dev Struct with fee token configuration for a token.
struct PremiumMultiplierWeiPerEthArgs {
address token; // // ──────────────────╮ Token address.
uint64 premiumMultiplierWeiPerEth; // ─╯ Multiplier for destination chain specific premiums.
}
/// @dev The base decimals for cost calculations.
uint256 public constant FEE_BASE_DECIMALS = 36;
/// @dev The decimals that Keystone reports prices in.
uint256 public constant KEYSTONE_PRICE_DECIMALS = 18;
string public constant override typeAndVersion = "FeeQuoter 1.6.0";
/// @dev The gas price per unit of gas for a given destination chain, in USD with 18 decimals. Multiple gas prices can
/// be encoded into the same value. Each price takes {Internal.GAS_PRICE_BITS} bits. For example, if Optimism is the
/// destination chain, gas price can include L1 base fee and L2 gas price. Logic to parse the price components is
/// chain-specific, and should live in OnRamp.
/// @dev Price of 1e18 is 1 USD. Examples:
/// Very Expensive: 1 unit of gas costs 1 USD -> 1e18.
/// Expensive: 1 unit of gas costs 0.1 USD -> 1e17.
/// Cheap: 1 unit of gas costs 0.000001 USD -> 1e12.
mapping(uint64 destChainSelector => Internal.TimestampedPackedUint224 price) private
s_usdPerUnitGasByDestChainSelector;
/// @dev The price, in USD with 18 decimals, per 1e18 of the smallest token denomination.
/// @dev Price of 1e18 represents 1 USD per 1e18 token amount.
/// 1 USDC = 1.00 USD per full token, each full token is 1e6 units -> 1 * 1e18 * 1e18 / 1e6 = 1e30.
/// 1 ETH = 2,000 USD per full token, each full token is 1e18 units -> 2000 * 1e18 * 1e18 / 1e18 = 2_000e18.
/// 1 LINK = 5.00 USD per full token, each full token is 1e18 units -> 5 * 1e18 * 1e18 / 1e18 = 5e18.
mapping(address token => Internal.TimestampedPackedUint224 price) private s_usdPerToken;
/// @dev Stores the price data feed configurations per token.
mapping(address token => TokenPriceFeedConfig dataFeedAddress) private s_usdPriceFeedsPerToken;
/// @dev The multiplier for destination chain specific premiums that can be set by the owner or fee admin.
mapping(address token => uint64 premiumMultiplierWeiPerEth) private s_premiumMultiplierWeiPerEth;
/// @dev The destination chain specific fee configs.
mapping(uint64 destChainSelector => DestChainConfig destChainConfig) internal s_destChainConfigs;
/// @dev The token transfer fee config that can be set by the owner or fee admin.
mapping(uint64 destChainSelector => mapping(address token => TokenTransferFeeConfig tranferFeeConfig)) private
s_tokenTransferFeeConfig;
/// @dev Maximum fee that can be charged for a message. This is a guard to prevent massively overcharging due to
/// misconfiguration.
uint96 internal immutable i_maxFeeJuelsPerMsg;
/// @dev The link token address.
address internal immutable i_linkToken;
/// @dev Subset of tokens which prices tracked by this registry which are fee tokens.
EnumerableSet.AddressSet private s_feeTokens;
/// @dev The amount of time a token price can be stale before it is considered invalid.
uint32 private immutable i_tokenPriceStalenessThreshold;
constructor(
StaticConfig memory staticConfig,
address[] memory priceUpdaters,
address[] memory feeTokens,
TokenPriceFeedUpdate[] memory tokenPriceFeeds,
TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs,
PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs,
DestChainConfigArgs[] memory destChainConfigArgs
) AuthorizedCallers(priceUpdaters) {
if (
staticConfig.linkToken == address(0) || staticConfig.maxFeeJuelsPerMsg == 0
|| staticConfig.tokenPriceStalenessThreshold == 0
) {
revert InvalidStaticConfig();
}
i_linkToken = staticConfig.linkToken;
i_maxFeeJuelsPerMsg = staticConfig.maxFeeJuelsPerMsg;
i_tokenPriceStalenessThreshold = staticConfig.tokenPriceStalenessThreshold;
_applyFeeTokensUpdates(new address[](0), feeTokens);
_updateTokenPriceFeeds(tokenPriceFeeds);
_applyDestChainConfigUpdates(destChainConfigArgs);
_applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs);
_applyTokenTransferFeeConfigUpdates(tokenTransferFeeConfigArgs, new TokenTransferFeeConfigRemoveArgs[](0));
}
// ================================================================
// │ Price calculations │
// ================================================================
/// @inheritdoc IPriceRegistry
function getTokenPrice(
address token
) public view override returns (Internal.TimestampedPackedUint224 memory) {
Internal.TimestampedPackedUint224 memory tokenPrice = s_usdPerToken[token];
// If the token price is not stale, return it.
if (block.timestamp - tokenPrice.timestamp < i_tokenPriceStalenessThreshold) {
return tokenPrice;
}
// When we have a stale price we should check if there is a more up to date source. If not, return the stale price.
TokenPriceFeedConfig memory priceFeedConfig = s_usdPriceFeedsPerToken[token];
if (!priceFeedConfig.isEnabled || priceFeedConfig.dataFeedAddress == address(0)) {
return tokenPrice;
}
// If the token price feed is set, retrieve the price from the feed.
Internal.TimestampedPackedUint224 memory feedPrice = _getTokenPriceFromDataFeed(priceFeedConfig);
// We check if the feed price isn't more stale than the stored price. Return the most recent one.
return feedPrice.timestamp >= tokenPrice.timestamp ? feedPrice : tokenPrice;
}
/// @notice Get the `tokenPrice` for a given token, checks if the price is valid.
/// @param token The token to get the price for.
/// @return tokenPrice The tokenPrice for the given token if it exists and is valid.
function getValidatedTokenPrice(
address token
) external view returns (uint224) {
return _getValidatedTokenPrice(token);
}
/// @notice Get the `tokenPrice` for an array of tokens.
/// @param tokens The tokens to get prices for.
/// @return tokenPrices The tokenPrices for the given tokens.
function getTokenPrices(
address[] calldata tokens
) external view returns (Internal.TimestampedPackedUint224[] memory) {
uint256 length = tokens.length;
Internal.TimestampedPackedUint224[] memory tokenPrices = new Internal.TimestampedPackedUint224[](length);
for (uint256 i = 0; i < length; ++i) {
tokenPrices[i] = getTokenPrice(tokens[i]);
}
return tokenPrices;
}
/// @notice Returns the token price data feed configuration.
/// @param token The token to retrieve the feed config for.
/// @return tokenPriceFeedConfig The token price data feed config (if feed address is 0, the feed config is disabled).
function getTokenPriceFeedConfig(
address token
) external view returns (TokenPriceFeedConfig memory) {
return s_usdPriceFeedsPerToken[token];
}
/// @notice Get an encoded `gasPrice` for a given destination chain ID.
/// The 224-bit result encodes necessary gas price components.
/// - On L1 chains like Ethereum or Avax, the only component is the gas price.
/// - On Optimistic Rollups, there are two components - the L2 gas price, and L1 base fee for data availability.
/// - On future chains, there could be more or differing price components.
/// PriceRegistry does not contain chain-specific logic to parse destination chain price components.
/// @param destChainSelector The destination chain to get the price for.
/// @return gasPrice The encoded gasPrice for the given destination chain ID.
/// @dev Does not validate if the chain is enabled
function getDestinationChainGasPrice(
uint64 destChainSelector
) external view returns (Internal.TimestampedPackedUint224 memory) {
return s_usdPerUnitGasByDestChainSelector[destChainSelector];
}
/// @notice Gets the fee token price and the gas price, both denominated in dollars.
/// @param token The source token to get the price for.
/// @param destChainSelector The destination chain to get the gas price for.
/// @return tokenPrice The price of the feeToken in 1e18 dollars per base unit.
/// @return gasPriceValue The price of gas in 1e18 dollars per base unit.
function getTokenAndGasPrices(
address token,
uint64 destChainSelector
) external view returns (uint224 tokenPrice, uint224 gasPriceValue) {
if (!s_destChainConfigs[destChainSelector].isEnabled) revert DestinationChainNotEnabled(destChainSelector);
return (
_getValidatedTokenPrice(token),
_getValidatedGasPrice(destChainSelector, s_destChainConfigs[destChainSelector].gasPriceStalenessThreshold)
);
}
/// @notice Convert a given token amount to target token amount.
/// @dev this function assumes that no more than 1e59 dollars are sent as payment.
/// If more is sent, the multiplication of feeTokenAmount and feeTokenValue will overflow.
/// Since there isn't even close to 1e59 dollars in the world economy this is safe.
/// @param fromToken The given token address.
/// @param fromTokenAmount The given token amount.
/// @param toToken The target token address.
/// @return toTokenAmount The target token amount.
function convertTokenAmount(
address fromToken,
uint256 fromTokenAmount,
address toToken
) public view returns (uint256) {
/// Example:
/// fromTokenAmount: 1e18 // 1 ETH
/// ETH: 2_000e18
/// LINK: 5e18
/// return: 1e18 * 2_000e18 / 5e18 = 400e18 (400 LINK)
return (fromTokenAmount * _getValidatedTokenPrice(fromToken)) / _getValidatedTokenPrice(toToken);
}
/// @notice Gets the token price for a given token and reverts if the token is not supported.
/// @param token The address of the token to get the price for.
/// @return tokenPriceValue The token price.
function _getValidatedTokenPrice(
address token
) internal view returns (uint224) {
Internal.TimestampedPackedUint224 memory tokenPrice = getTokenPrice(token);
// Token price must be set at least once.
if (tokenPrice.timestamp == 0 || tokenPrice.value == 0) revert TokenNotSupported(token);
return tokenPrice.value;
}
/// @notice Gets the token price from a data feed address, rebased to the same units as s_usdPerToken.
/// @param priceFeedConfig token data feed configuration with valid data feed address (used to retrieve price & timestamp).
/// @return tokenPrice data feed price answer rebased to s_usdPerToken units, with latest block timestamp.
function _getTokenPriceFromDataFeed(
TokenPriceFeedConfig memory priceFeedConfig
) internal view returns (Internal.TimestampedPackedUint224 memory tokenPrice) {
AggregatorV3Interface dataFeedContract = AggregatorV3Interface(priceFeedConfig.dataFeedAddress);
(
// uint80 roundID
,
int256 dataFeedAnswer,
// uint startedAt
,
uint256 updatedAt,
// uint80 answeredInRound
) = dataFeedContract.latestRoundData();
if (dataFeedAnswer < 0) {
revert DataFeedValueOutOfUint224Range();
}
uint224 rebasedValue =
_calculateRebasedValue(dataFeedContract.decimals(), priceFeedConfig.tokenDecimals, uint256(dataFeedAnswer));
// Data feed staleness is unchecked to decouple the FeeQuoter from data feed delay issues.
return Internal.TimestampedPackedUint224({value: rebasedValue, timestamp: uint32(updatedAt)});
}
/// @dev Gets the fee token price and the gas price, both denominated in dollars.
/// @param destChainSelector The destination chain to get the gas price for.
/// @param gasPriceStalenessThreshold The amount of time a gas price can be stale before it is considered invalid.
/// @return gasPriceValue The price of gas in 1e18 dollars per base unit.
function _getValidatedGasPrice(
uint64 destChainSelector,
uint32 gasPriceStalenessThreshold
) private view returns (uint224 gasPriceValue) {
Internal.TimestampedPackedUint224 memory gasPrice = s_usdPerUnitGasByDestChainSelector[destChainSelector];
// If the staleness threshold is 0, we consider the gas price to be always valid.
if (gasPriceStalenessThreshold != 0) {
// We do allow a gas price of 0, but no stale or unset gas prices.
uint256 timePassed = block.timestamp - gasPrice.timestamp;
if (timePassed > gasPriceStalenessThreshold) {
revert StaleGasPrice(destChainSelector, gasPriceStalenessThreshold, timePassed);
}
}
return gasPrice.value;
}
// ================================================================
// │ Fee tokens │
// ================================================================
/// @inheritdoc IPriceRegistry
function getFeeTokens() external view returns (address[] memory) {
return s_feeTokens.values();
}
/// @notice Add and remove tokens from feeTokens set.
/// @param feeTokensToRemove The addresses of the tokens which are no longer considered feeTokens.
/// @param feeTokensToAdd The addresses of the tokens which are now considered fee tokens and can be used
/// to calculate fees.
function applyFeeTokensUpdates(
address[] memory feeTokensToRemove,
address[] memory feeTokensToAdd
) external onlyOwner {
_applyFeeTokensUpdates(feeTokensToRemove, feeTokensToAdd);
}
/// @notice Add and remove tokens from feeTokens set.
/// @param feeTokensToRemove The addresses of the tokens which are no longer considered feeTokens.
/// @param feeTokensToAdd The addresses of the tokens which are now considered fee tokens.
/// and can be used to calculate fees.
function _applyFeeTokensUpdates(address[] memory feeTokensToRemove, address[] memory feeTokensToAdd) private {
for (uint256 i = 0; i < feeTokensToRemove.length; ++i) {
if (s_feeTokens.remove(feeTokensToRemove[i])) {
emit FeeTokenRemoved(feeTokensToRemove[i]);
}
}
for (uint256 i = 0; i < feeTokensToAdd.length; ++i) {
if (s_feeTokens.add(feeTokensToAdd[i])) {
emit FeeTokenAdded(feeTokensToAdd[i]);
}
}
}
// ================================================================
// │ Price updates │
// ================================================================
/// @inheritdoc IPriceRegistry
function updatePrices(
Internal.PriceUpdates calldata priceUpdates
) external override {
// The caller must be a fee updater.
_validateCaller();
uint256 tokenUpdatesLength = priceUpdates.tokenPriceUpdates.length;
for (uint256 i = 0; i < tokenUpdatesLength; ++i) {
Internal.TokenPriceUpdate memory update = priceUpdates.tokenPriceUpdates[i];
s_usdPerToken[update.sourceToken] =
Internal.TimestampedPackedUint224({value: update.usdPerToken, timestamp: uint32(block.timestamp)});
emit UsdPerTokenUpdated(update.sourceToken, update.usdPerToken, block.timestamp);
}
uint256 gasUpdatesLength = priceUpdates.gasPriceUpdates.length;
for (uint256 i = 0; i < gasUpdatesLength; ++i) {
Internal.GasPriceUpdate memory update = priceUpdates.gasPriceUpdates[i];
s_usdPerUnitGasByDestChainSelector[update.destChainSelector] =
Internal.TimestampedPackedUint224({value: update.usdPerUnitGas, timestamp: uint32(block.timestamp)});
emit UsdPerUnitGasUpdated(update.destChainSelector, update.usdPerUnitGas, block.timestamp);
}
}
/// @notice Updates the USD token price feeds for given tokens.
/// @param tokenPriceFeedUpdates Token price feed updates to apply.
function updateTokenPriceFeeds(
TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates
) external onlyOwner {
_updateTokenPriceFeeds(tokenPriceFeedUpdates);
}
/// @notice Updates the USD token price feeds for given tokens.
/// @param tokenPriceFeedUpdates Token price feed updates to apply.
function _updateTokenPriceFeeds(
TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates
) private {
for (uint256 i; i < tokenPriceFeedUpdates.length; ++i) {
TokenPriceFeedUpdate memory update = tokenPriceFeedUpdates[i];
address sourceToken = update.sourceToken;
TokenPriceFeedConfig memory tokenPriceFeedConfig = update.feedConfig;
s_usdPriceFeedsPerToken[sourceToken] = tokenPriceFeedConfig;
emit PriceFeedPerTokenUpdated(sourceToken, tokenPriceFeedConfig);
}
}
/// @notice Signals which version of the pool interface is supported
function supportsInterface(
bytes4 interfaceId
) public pure override returns (bool) {
return interfaceId == type(IReceiver).interfaceId || interfaceId == type(IFeeQuoter).interfaceId
|| interfaceId == type(ITypeAndVersion).interfaceId || interfaceId == type(IERC165).interfaceId;
}
/// @inheritdoc IReceiver
/// @notice Handles the report containing price feeds and updates the internal price storage.
/// @dev This function is called to process incoming price feed data.
/// @param metadata Arbitrary metadata associated with the report (not used in this implementation).
/// @param report Encoded report containing an array of `ReceivedCCIPFeedReport` structs.
function onReport(bytes calldata metadata, bytes calldata report) external {
(bytes10 workflowName, address workflowOwner, bytes2 reportName) = metadata._extractMetadataInfo();
_validateReportPermission(msg.sender, workflowOwner, workflowName, reportName);
ReceivedCCIPFeedReport[] memory feeds = abi.decode(report, (ReceivedCCIPFeedReport[]));
for (uint256 i = 0; i < feeds.length; ++i) {
TokenPriceFeedConfig memory feedConfig = s_usdPriceFeedsPerToken[feeds[i].token];
// If the token is not enabled we revert the entire report as that indicates some type of misconfiguration.
if (!feedConfig.isEnabled) {
revert TokenNotSupported(feeds[i].token);
}
// Keystone reports prices in USD with 18 decimals, so we passing it as 18 in the _calculateRebasedValue function.
uint224 rebasedValue =
_calculateRebasedValue(uint8(KEYSTONE_PRICE_DECIMALS), feedConfig.tokenDecimals, feeds[i].price);
// If the feed timestamp is older than the current stored price, skip the update.
// We do not revert Keystone price feeds deliberately.
if (feeds[i].timestamp < s_usdPerToken[feeds[i].token].timestamp) {
continue;
}
// Update the token price with the new value and timestamp.
s_usdPerToken[feeds[i].token] =
Internal.TimestampedPackedUint224({value: rebasedValue, timestamp: feeds[i].timestamp});
emit UsdPerTokenUpdated(feeds[i].token, rebasedValue, feeds[i].timestamp);
}
}
// ================================================================
// │ Fee quoting │
// ================================================================
/// @inheritdoc IFeeQuoter
/// @dev The function should always validate message.extraArgs, message.receiver and family-specific configs.
function getValidatedFee(
uint64 destChainSelector,
Client.EVM2AnyMessage calldata message
) external view returns (uint256 feeTokenAmount) {
DestChainConfig memory destChainConfig = s_destChainConfigs[destChainSelector];
if (!destChainConfig.isEnabled) revert DestinationChainNotEnabled(destChainSelector);
if (!s_feeTokens.contains(message.feeToken)) revert FeeTokenNotSupported(message.feeToken);
uint256 numberOfTokens = message.tokenAmounts.length;
uint256 gasLimit = _validateMessageAndResolveGasLimitForDestination(destChainSelector, destChainConfig, message);
// The below call asserts that feeToken is a supported token.
uint224 feeTokenPrice = _getValidatedTokenPrice(message.feeToken);
uint224 packedGasPrice = _getValidatedGasPrice(destChainSelector, destChainConfig.gasPriceStalenessThreshold);
// Calculate premiumFee in USD with 18 decimals precision first.
// If message-only and no token transfers, a flat network fee is charged.
// If there are token transfers, premiumFee is calculated from token transfer fee.
// If there are both token transfers and message, premiumFee is only calculated from token transfer fee.
uint256 premiumFeeUSDWei = 0;
uint32 tokenTransferGas = 0;
uint32 tokenTransferBytesOverhead = 0;
if (numberOfTokens > 0) {
(premiumFeeUSDWei, tokenTransferGas, tokenTransferBytesOverhead) = _getTokenTransferCost(
destChainConfig.defaultTokenFeeUSDCents,
destChainConfig.defaultTokenDestGasOverhead,
destChainSelector,
message.feeToken,
feeTokenPrice,
message.tokenAmounts
);
} else {
// Convert USD cents with 2 decimals to 18 decimals.
premiumFeeUSDWei = uint256(destChainConfig.networkFeeUSDCents) * 1e16;
}
// Apply the premium multiplier for the fee token, making it 36 decimals
premiumFeeUSDWei *= s_premiumMultiplierWeiPerEth[message.feeToken];
// Calculate data availability cost in USD with 36 decimals. Data availability cost exists on rollups that need to
// post transaction calldata onto another storage layer, e.g. Eth mainnet, incurring additional storage gas costs.
uint256 dataAvailabilityCostUSD36Decimals = 0;
// Only calculate data availability cost if data availability multiplier is non-zero.
// The multiplier should be set to 0 if destination chain does not charge data availability cost.
if (destChainConfig.destDataAvailabilityMultiplierBps > 0) {
dataAvailabilityCostUSD36Decimals = _getDataAvailabilityCost(
destChainConfig,
// Parse the data availability gas price stored in the higher-order 112 bits of the encoded gas price.
uint112(packedGasPrice >> Internal.GAS_PRICE_BITS),
message.data.length,
numberOfTokens,
tokenTransferBytesOverhead
);
}
// Calculate the calldata, taking into account EIP-7623. We charge destGasPerPayloadByteBase for the calldata cost
// up to destGasPerPayloadByteThreshold, even when the total calldata length exceeds the threshold. This is safe
// because we also charge for execution gas on top of this. When correct values are chosen, the execution gas we
// charge is always higher than the difference between the base and high calldata costs for the first
// destGasPerPayloadByteThreshold bytes. Since we don't pay for execution gas in EIP-7623, this execution gas is
// effectively used to cover the higher calldata costs for the first destGasPerPayloadByteThreshold bytes.
// The threshold should be adjusted based on expected execution cost and, potentially, to discourage large payloads.
// Example: 16 base, 40 high, 100k execution cost. 100k/(40-16) = max 4.16kb as the threshold. Take 4kb threshold.
// Calldata length = 5000
// Our calculations: 1000 * 40 + 4000 * 16 = 104k calldata cost + 100k execution cost = 204k calculated cost.
// Actual cost: 5000 * 40 = 200k
// The difference is 4k in favour of CCIP. The lower the threshold, the more premium is charged for large payloads.
uint256 calldataLength = message.data.length + tokenTransferBytesOverhead;
uint256 destCallDataCost = calldataLength * destChainConfig.destGasPerPayloadByteBase;
if (calldataLength > destChainConfig.destGasPerPayloadByteThreshold) {
destCallDataCost = destChainConfig.destGasPerPayloadByteBase * destChainConfig.destGasPerPayloadByteThreshold
+ (calldataLength - destChainConfig.destGasPerPayloadByteThreshold) * destChainConfig.destGasPerPayloadByteHigh;
}
// We add the destination chain CCIP overhead (commit, exec), the token transfer gas, the calldata cost and the msg
// gas limit to get the total gas the tx costs to execute on the destination chain.
uint256 totalDestChainGas = destChainConfig.destGasOverhead + tokenTransferGas + destCallDataCost + gasLimit;
// Total USD fee is in 36 decimals, feeTokenPrice is in 18 decimals USD for 1e18 smallest token denominations.
// The result is the fee in the feeTokens smallest denominations (e.g. wei for ETH).
// uint112(packedGasPrice) = executionGasPrice
return (
totalDestChainGas * uint112(packedGasPrice) * destChainConfig.gasMultiplierWeiPerEth + premiumFeeUSDWei
+ dataAvailabilityCostUSD36Decimals
) / feeTokenPrice;
}
/// @notice Sets the fee configuration for a token.
/// @param premiumMultiplierWeiPerEthArgs Array of PremiumMultiplierWeiPerEthArgs structs.
function applyPremiumMultiplierWeiPerEthUpdates(
PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs
) external onlyOwner {
_applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs);
}
/// @dev Sets the fee config.
/// @param premiumMultiplierWeiPerEthArgs The multiplier for destination chain specific premiums.
function _applyPremiumMultiplierWeiPerEthUpdates(
PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs
) internal {
for (uint256 i = 0; i < premiumMultiplierWeiPerEthArgs.length; ++i) {
address token = premiumMultiplierWeiPerEthArgs[i].token;
uint64 premiumMultiplierWeiPerEth = premiumMultiplierWeiPerEthArgs[i].premiumMultiplierWeiPerEth;
s_premiumMultiplierWeiPerEth[token] = premiumMultiplierWeiPerEth;
emit PremiumMultiplierWeiPerEthUpdated(token, premiumMultiplierWeiPerEth);
}
}
/// @notice Gets the fee configuration for a token.
/// @param token The token to get the fee configuration for.
/// @return premiumMultiplierWeiPerEth The multiplier for destination chain specific premiums.
function getPremiumMultiplierWeiPerEth(
address token
) external view returns (uint64 premiumMultiplierWeiPerEth) {
return s_premiumMultiplierWeiPerEth[token];
}
/// @notice Returns the token transfer cost parameters.
/// A basis point fee is calculated from the USD value of each token transfer.
/// For each individual transfer, this fee is between [minFeeUSD, maxFeeUSD].
/// Total transfer fee is the sum of each individual token transfer fee.
/// @dev Assumes that tokenAmounts are validated to be listed tokens elsewhere.
/// @dev Splitting one token transfer into multiple transfers is discouraged, as it will result in a transferFee
/// equal or greater than the same amount aggregated/de-duped.
/// @param defaultTokenFeeUSDCents the default token fee in USD cents.
/// @param defaultTokenDestGasOverhead the default token destination gas overhead.
/// @param destChainSelector the destination chain selector.
/// @param feeToken address of the feeToken.
/// @param feeTokenPrice price of feeToken in USD with 18 decimals.
/// @param tokenAmounts token transfers in the message.
/// @return tokenTransferFeeUSDWei total token transfer bps fee in USD with 18 decimals.
/// @return tokenTransferGas total execution gas of the token transfers.
/// @return tokenTransferBytesOverhead additional token transfer data passed to destination, e.g. USDC attestation.
function _getTokenTransferCost(
uint256 defaultTokenFeeUSDCents,
uint32 defaultTokenDestGasOverhead,
uint64 destChainSelector,
address feeToken,
uint224 feeTokenPrice,
Client.EVMTokenAmount[] calldata tokenAmounts
) internal view returns (uint256 tokenTransferFeeUSDWei, uint32 tokenTransferGas, uint32 tokenTransferBytesOverhead) {
uint256 numberOfTokens = tokenAmounts.length;
for (uint256 i = 0; i < numberOfTokens; ++i) {
Client.EVMTokenAmount memory tokenAmount = tokenAmounts[i];
TokenTransferFeeConfig memory transferFeeConfig = s_tokenTransferFeeConfig[destChainSelector][tokenAmount.token];
// If the token has no specific overrides configured, we use the global defaults.
if (!transferFeeConfig.isEnabled) {
tokenTransferFeeUSDWei += defaultTokenFeeUSDCents * 1e16;
tokenTransferGas += defaultTokenDestGasOverhead;
tokenTransferBytesOverhead += Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES;
continue;
}
uint256 bpsFeeUSDWei = 0;
// Only calculate bps fee if ratio is greater than 0. Ratio of 0 means no bps fee for a token.
// Useful for when the FeeQuoter cannot return a valid price for the token.
if (transferFeeConfig.deciBps > 0) {
uint224 tokenPrice = 0;
if (tokenAmount.token != feeToken) {
tokenPrice = _getValidatedTokenPrice(tokenAmount.token);
} else {
tokenPrice = feeTokenPrice;
}
// Calculate token transfer value, then apply fee ratio.
// ratio represents multiples of 0.1bps, or 1e-5.
bpsFeeUSDWei = (tokenPrice._calcUSDValueFromTokenAmount(tokenAmount.amount) * transferFeeConfig.deciBps) / 1e5;
}
tokenTransferGas += transferFeeConfig.destGasOverhead;
tokenTransferBytesOverhead += transferFeeConfig.destBytesOverhead;
// Bps fees should be kept within range of [minFeeUSD, maxFeeUSD].
// Convert USD values with 2 decimals to 18 decimals.
uint256 minFeeUSDWei = uint256(transferFeeConfig.minFeeUSDCents) * 1e16;
if (bpsFeeUSDWei < minFeeUSDWei) {
tokenTransferFeeUSDWei += minFeeUSDWei;
continue;
}
uint256 maxFeeUSDWei = uint256(transferFeeConfig.maxFeeUSDCents) * 1e16;
if (bpsFeeUSDWei > maxFeeUSDWei) {
tokenTransferFeeUSDWei += maxFeeUSDWei;
continue;
}
// In the case where bpsFeeUSDWei, minFeeUSDWei, and maxFeeUSDWei are all 0, we skip the fee. This is intended
// to allow for a fee of 0 to be set.
tokenTransferFeeUSDWei += bpsFeeUSDWei;
}
return (tokenTransferFeeUSDWei, tokenTransferGas, tokenTransferBytesOverhead);
}
/// @notice calculates the rebased value for 1e18 smallest token denomination.
/// @param dataFeedDecimal decimal of the data feed.
/// @param tokenDecimal decimal of the token.
/// @param feedValue value of the data feed.
/// @return rebasedValue rebased value.
function _calculateRebasedValue(
uint8 dataFeedDecimal,
uint8 tokenDecimal,
uint256 feedValue
) internal pure returns (uint224 rebasedValue) {
// Rebase formula for units in smallest token denomination: usdValue * (1e18 * 1e18) / 1eTokenDecimals.
// feedValue * (10 ** (18 - feedDecimals)) * (10 ** (18 - erc20Decimals))
// feedValue * (10 ** ((18 - feedDecimals) + (18 - erc20Decimals)))
// feedValue * (10 ** (36 - feedDecimals - erc20Decimals))
// feedValue * (10 ** (36 - (feedDecimals + erc20Decimals)))
// feedValue * (10 ** (36 - excessDecimals))
// If excessDecimals > 36 => flip it to feedValue / (10 ** (excessDecimals - 36)).
uint8 excessDecimals = dataFeedDecimal + tokenDecimal;
uint256 rebasedVal;
if (excessDecimals > FEE_BASE_DECIMALS) {
rebasedVal = feedValue / (10 ** (excessDecimals - FEE_BASE_DECIMALS));
} else {
rebasedVal = feedValue * (10 ** (FEE_BASE_DECIMALS - excessDecimals));
}
if (rebasedVal > type(uint224).max) {
revert DataFeedValueOutOfUint224Range();
}
return uint224(rebasedVal);
}
/// @notice Returns the estimated data availability cost of the message.
/// @dev To save on gas, we use a single destGasPerDataAvailabilityByte value for both zero and non-zero bytes.
/// @param destChainConfig the config configured for the destination chain selector.
/// @param dataAvailabilityGasPrice USD per data availability gas in 18 decimals.
/// @param messageDataLength length of the data field in the message.
/// @param numberOfTokens number of distinct token transfers in the message.
/// @param tokenTransferBytesOverhead additional token transfer data passed to destination, e.g. USDC attestation.
/// @return dataAvailabilityCostUSD36Decimal total data availability cost in USD with 36 decimals.
function _getDataAvailabilityCost(
DestChainConfig memory destChainConfig,
uint112 dataAvailabilityGasPrice,
uint256 messageDataLength,
uint256 numberOfTokens,
uint32 tokenTransferBytesOverhead
) internal pure returns (uint256 dataAvailabilityCostUSD36Decimal) {
// dataAvailabilityLengthBytes sums up byte lengths of fixed message fields and dynamic message fields.
// Fixed message fields do account for the offset and length slot of the dynamic fields.
uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength
+ (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead;
// destDataAvailabilityOverheadGas is a separate config value for flexibility to be updated independently of message
// cost. Its value is determined by CCIP lane implementation, e.g. the overhead data posted for OCR.
uint256 dataAvailabilityGas = (dataAvailabilityLengthBytes * destChainConfig.destGasPerDataAvailabilityByte)
+ destChainConfig.destDataAvailabilityOverheadGas;
// dataAvailabilityGasPrice is in 18 decimals, destDataAvailabilityMultiplierBps is in 4 decimals.
// We pad 14 decimals to bring the result to 36 decimals, in line with token bps and execution fee.
return ((dataAvailabilityGas * dataAvailabilityGasPrice) * destChainConfig.destDataAvailabilityMultiplierBps) * 1e14;
}
/// @notice Gets the transfer fee config for a given token.
/// @param destChainSelector The destination chain selector.
/// @param token The token address.
/// @return tokenTransferFeeConfig The transfer fee config for the token.
function getTokenTransferFeeConfig(
uint64 destChainSelector,
address token
) external view returns (TokenTransferFeeConfig memory tokenTransferFeeConfig) {
return s_tokenTransferFeeConfig[destChainSelector][token];
}
/// @notice Sets the transfer fee config.
/// @dev only callable by the owner or admin.
function applyTokenTransferFeeConfigUpdates(
TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs,
TokenTransferFeeConfigRemoveArgs[] memory tokensToUseDefaultFeeConfigs
) external onlyOwner {
_applyTokenTransferFeeConfigUpdates(tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs);
}
/// @notice internal helper to set the token transfer fee config.
function _applyTokenTransferFeeConfigUpdates(
TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs,
TokenTransferFeeConfigRemoveArgs[] memory tokensToUseDefaultFeeConfigs
) internal {
for (uint256 i = 0; i < tokenTransferFeeConfigArgs.length; ++i) {
TokenTransferFeeConfigArgs memory tokenTransferFeeConfigArg = tokenTransferFeeConfigArgs[i];
uint64 destChainSelector = tokenTransferFeeConfigArg.destChainSelector;
for (uint256 j = 0; j < tokenTransferFeeConfigArg.tokenTransferFeeConfigs.length; ++j) {
TokenTransferFeeConfig memory tokenTransferFeeConfig =
tokenTransferFeeConfigArg.tokenTransferFeeConfigs[j].tokenTransferFeeConfig;
address token = tokenTransferFeeConfigArg.tokenTransferFeeConfigs[j].token;
if (tokenTransferFeeConfig.minFeeUSDCents >= tokenTransferFeeConfig.maxFeeUSDCents) {
revert InvalidFeeRange(tokenTransferFeeConfig.minFeeUSDCents, tokenTransferFeeConfig.maxFeeUSDCents);
}
if (tokenTransferFeeConfig.destBytesOverhead < Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) {
revert InvalidDestBytesOverhead(token, tokenTransferFeeConfig.destBytesOverhead);
}
s_tokenTransferFeeConfig[destChainSelector][token] = tokenTransferFeeConfig;
emit TokenTransferFeeConfigUpdated(destChainSelector, token, tokenTransferFeeConfig);
}
}
// Remove the custom fee configs for the tokens that are in the tokensToUseDefaultFeeConfigs array.
for (uint256 i = 0; i < tokensToUseDefaultFeeConfigs.length; ++i) {
uint64 destChainSelector = tokensToUseDefaultFeeConfigs[i].destChainSelector;
address token = tokensToUseDefaultFeeConfigs[i].token;
delete s_tokenTransferFeeConfig[destChainSelector][token];
emit TokenTransferFeeConfigDeleted(destChainSelector, token);
}
}
// ================================================================
// │ Validations & message processing │
// ================================================================
/// @notice Validates that the destAddress matches the expected format of the family.
/// @param chainFamilySelector Tag to identify the target family.
/// @param destAddress Dest address to validate.
/// @dev precondition - assumes the family tag is correct and validated.
function _validateDestFamilyAddress(
bytes4 chainFamilySelector,
bytes memory destAddress,
uint256 gasLimit
) internal pure {
if (chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_EVM) {
return Internal._validateEVMAddress(destAddress);
}
if (chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_SVM) {
// SVM addresses don't have a precompile space at the first X addresses, instead we validate that if the gasLimit
// is non-zero, the address must not be 0x0.
return Internal._validate32ByteAddress(destAddress, gasLimit > 0 ? 1 : 0);
}
if (
chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_APTOS
|| chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_SUI
) {
return Internal._validate32ByteAddress(destAddress, Internal.APTOS_PRECOMPILE_SPACE);
}
revert InvalidChainFamilySelector(chainFamilySelector);
}
/// @notice Parse and validate the SVM specific Extra Args Bytes.
function _parseSVMExtraArgsFromBytes(
bytes calldata extraArgs,
uint256 maxPerMsgGasLimit,
bool enforceOutOfOrder
) internal pure returns (Client.SVMExtraArgsV1 memory svmExtraArgs) {
if (extraArgs.length == 0) {
revert InvalidExtraArgsData();
}
bytes4 tag = bytes4(extraArgs[:4]);
if (tag != Client.SVM_EXTRA_ARGS_V1_TAG) {
revert InvalidExtraArgsTag();
}
svmExtraArgs = abi.decode(extraArgs[4:], (Client.SVMExtraArgsV1));
if (enforceOutOfOrder && !svmExtraArgs.allowOutOfOrderExecution) {
revert ExtraArgOutOfOrderExecutionMustBeTrue();
}
if (svmExtraArgs.computeUnits > maxPerMsgGasLimit) {
revert MessageComputeUnitLimitTooHigh();
}
return svmExtraArgs;
}
/// @dev Convert the extra args bytes into a struct with validations against the dest chain config.
/// @param extraArgs The extra args bytes.
/// @return genericExtraArgs The GenericExtraArgs struct.
function _parseGenericExtraArgsFromBytes(
bytes calldata extraArgs,
uint32 defaultTxGasLimit,
uint256 maxPerMsgGasLimit,
bool enforceOutOfOrder
) internal pure returns (Client.GenericExtraArgsV2 memory) {
// Since GenericExtraArgs are simply a superset of EVMExtraArgsV1, we can parse them as such. For Aptos, this
// technically means EVMExtraArgsV1 are processed like they would be valid, but they will always fail on the
// allowedOutOfOrderExecution check below.
Client.GenericExtraArgsV2 memory parsedExtraArgs =
_parseUnvalidatedEVMExtraArgsFromBytes(extraArgs, defaultTxGasLimit);
if (parsedExtraArgs.gasLimit > maxPerMsgGasLimit) revert MessageGasLimitTooHigh();
// If the chain enforces out of order execution, the extra args must allow it, otherwise revert. We cannot assume
// the user intended to use OOO on any chain that requires it as it may lead to unexpected behavior. Therefore we
// revert instead of assuming the user intended to use OOO.
if (enforceOutOfOrder && !parsedExtraArgs.allowOutOfOrderExecution) {
revert ExtraArgOutOfOrderExecutionMustBeTrue();
}
return parsedExtraArgs;
}
/// @dev Convert the extra args bytes into a struct.
/// @param extraArgs The extra args bytes.
/// @param defaultTxGasLimit default tx gas limit to use in the absence of extra args.
/// @return EVMExtraArgsV2 the extra args struct populated with either the given args or default values.
function _parseUnvalidatedEVMExtraArgsFromBytes(
bytes calldata extraArgs,
uint64 defaultTxGasLimit
) private pure returns (Client.GenericExtraArgsV2 memory) {
if (extraArgs.length == 0) {
// If extra args are empty, generate default values.
return Client.GenericExtraArgsV2({gasLimit: defaultTxGasLimit, allowOutOfOrderExecution: false});
}
bytes4 extraArgsTag = bytes4(extraArgs);
bytes memory argsData = extraArgs[4:];
if (extraArgsTag == Client.GENERIC_EXTRA_ARGS_V2_TAG) {
return abi.decode(argsData, (Client.GenericExtraArgsV2));
} else if (extraArgsTag == Client.EVM_EXTRA_ARGS_V1_TAG) {
// EVMExtraArgsV1 originally included a second boolean (strict) field which has been deprecated.
// Clients may still include it but it will be ignored.
return Client.GenericExtraArgsV2({gasLimit: abi.decode(argsData, (uint256)), allowOutOfOrderExecution: false});
}
revert InvalidExtraArgsTag();
}
/// @notice Validate the forwarded message to ensure it matches the configuration limits (message length, number of
/// tokens) and family-specific expectations (address format).
/// @param destChainSelector The destination chain selector.
/// @param destChainConfig The destination chain config.
/// @param message The message to validate.
/// @return gasLimit The gas limit to use for the message.
function _validateMessageAndResolveGasLimitForDestination(
uint64 destChainSelector,
DestChainConfig memory destChainConfig,
Client.EVM2AnyMessage calldata message
) internal view returns (uint256 gasLimit) {
uint256 dataLength = message.data.length;
uint256 numberOfTokens = message.tokenAmounts.length;
// Check that payload is formed correctly.
if (dataLength > uint256(destChainConfig.maxDataBytes)) {
revert MessageTooLarge(uint256(destChainConfig.maxDataBytes), dataLength);
}
if (numberOfTokens > uint256(destChainConfig.maxNumberOfTokensPerMsg)) {
revert UnsupportedNumberOfTokens(numberOfTokens, destChainConfig.maxNumberOfTokensPerMsg);
}
// resolve gas limit and validate chainFamilySelector
if (
destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_EVM
|| destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_APTOS
|| destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_SUI
) {
gasLimit = _parseGenericExtraArgsFromBytes(
message.extraArgs,
destChainConfig.defaultTxGasLimit,
destChainConfig.maxPerMsgGasLimit,
destChainConfig.enforceOutOfOrder
).gasLimit;
_validateDestFamilyAddress(destChainConfig.chainFamilySelector, message.receiver, gasLimit);
} else if (destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_SVM) {
Client.SVMExtraArgsV1 memory svmExtraArgsV1 = _parseSVMExtraArgsFromBytes(
message.extraArgs, destChainConfig.maxPerMsgGasLimit, destChainConfig.enforceOutOfOrder
);
gasLimit = svmExtraArgsV1.computeUnits;
_validateDestFamilyAddress(destChainConfig.chainFamilySelector, message.receiver, gasLimit);
uint256 accountsLength = svmExtraArgsV1.accounts.length;
// The max payload size for SVM is heavily dependent on the accounts passed into extra args and the number of
// tokens. Below, token and account overhead will count towards maxDataBytes.
uint256 svmExpandedDataLength = dataLength;
// This abi.decode is safe because the address is validated above.
if (abi.decode(message.receiver, (uint256)) == 0) {
// When message receiver is zero, CCIP receiver is not invoked on SVM.
// There should not be additional accounts specified for the receiver.
if (accountsLength > 0) {
revert TooManySVMExtraArgsAccounts(accountsLength, 0);
}
} else {
// The messaging accounts needed for CCIP receiver on SVM are:
// message receiver, offramp PDA signer,
// plus remaining accounts specified in SVM extraArgs. Each account is 32 bytes.
svmExpandedDataLength +=
((accountsLength + Client.SVM_MESSAGING_ACCOUNTS_OVERHEAD) * Client.SVM_ACCOUNT_BYTE_SIZE);
}
if (numberOfTokens > 0 && svmExtraArgsV1.tokenReceiver == bytes32(0)) {
revert InvalidTokenReceiver();
}
if (accountsLength > Client.SVM_EXTRA_ARGS_MAX_ACCOUNTS) {
revert TooManySVMExtraArgsAccounts(accountsLength, Client.SVM_EXTRA_ARGS_MAX_ACCOUNTS);
}
if (svmExtraArgsV1.accountIsWritableBitmap >> accountsLength != 0) {
revert InvalidSVMExtraArgsWritableBitmap(svmExtraArgsV1.accountIsWritableBitmap, accountsLength);
}
svmExpandedDataLength += (numberOfTokens * Client.SVM_TOKEN_TRANSFER_DATA_OVERHEAD);
// The token destBytesOverhead can be very different per token so we have to take it into account as well.
for (uint256 i = 0; i < numberOfTokens; ++i) {
uint256 destBytesOverhead =
s_tokenTransferFeeConfig[destChainSelector][message.tokenAmounts[i].token].destBytesOverhead;
// Pools get Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES by default, but if an override is set we use that instead.
if (destBytesOverhead > 0) {
svmExpandedDataLength += destBytesOverhead;
} else {
svmExpandedDataLength += Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES;
}
}
if (svmExpandedDataLength > uint256(destChainConfig.maxDataBytes)) {
revert MessageTooLarge(uint256(destChainConfig.maxDataBytes), svmExpandedDataLength);
}
} else {
revert InvalidChainFamilySelector(destChainConfig.chainFamilySelector);
}
return gasLimit;
}
/// @inheritdoc IFeeQuoter
/// @dev precondition - onRampTokenTransfers and sourceTokenAmounts lengths must be equal.
function processMessageArgs(
uint64 destChainSelector,
address feeToken,
uint256 feeTokenAmount,
bytes calldata extraArgs,
bytes calldata messageReceiver
)
external
view
returns (
uint256 msgFeeJuels,
bool isOutOfOrderExecution,
bytes memory convertedExtraArgs,
bytes memory tokenReceiver
)
{
// Convert feeToken to link if not already in link.
if (feeToken == i_linkToken) {
msgFeeJuels = feeTokenAmount;
} else {
msgFeeJuels = convertTokenAmount(feeToken, feeTokenAmount, i_linkToken);
}
if (msgFeeJuels > i_maxFeeJuelsPerMsg) revert MessageFeeTooHigh(msgFeeJuels, i_maxFeeJuelsPerMsg);
(convertedExtraArgs, isOutOfOrderExecution, tokenReceiver) =
_processChainFamilySelector(destChainSelector, messageReceiver, extraArgs);
return (msgFeeJuels, isOutOfOrderExecution, convertedExtraArgs, tokenReceiver);
}
/// @notice Parses the extra Args based on the chain family selector. Isolated into a separate function
/// as it was the only way to prevent a stack too deep error, and makes future chain family additions easier.
// solhint-disable-next-line chainlink-solidity/explicit-returns
function _processChainFamilySelector(
uint64 destChainSelector,
bytes calldata messageReceiver,
bytes calldata extraArgs
) internal view returns (bytes memory validatedExtraArgs, bool allowOutOfOrderExecution, bytes memory tokenReceiver) {
// Since this function is called after getFee, which already validates the params, no validation is necessary.
DestChainConfig memory destChainConfig = s_destChainConfigs[destChainSelector];
// EVM and Aptos both use the same GenericExtraArgs, with EVM also supporting EVMExtraArgsV1 which is handled inside
// the generic function.
if (
destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_EVM
|| destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_APTOS
|| destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_SUI
) {
Client.GenericExtraArgsV2 memory parsedExtraArgs =
_parseUnvalidatedEVMExtraArgsFromBytes(extraArgs, destChainConfig.defaultTxGasLimit);
return (Client._argsToBytes(parsedExtraArgs), parsedExtraArgs.allowOutOfOrderExecution, messageReceiver);
}
if (destChainConfig.chainFamilySelector == Internal.CHAIN_FAMILY_SELECTOR_SVM) {
// If extraArgs passes the parsing it's valid and can be returned unchanged.
// ExtraArgs are required on SVM, meaning the supplied extraArgs are either invalid and we would have reverted
// or we have valid extraArgs and we can return them without having to re-encode them.
return (
extraArgs,
true,
abi.encode(
_parseSVMExtraArgsFromBytes(extraArgs, destChainConfig.maxPerMsgGasLimit, destChainConfig.enforceOutOfOrder)
.tokenReceiver
)
);
}
revert InvalidChainFamilySelector(destChainConfig.chainFamilySelector);
}
/// @inheritdoc IFeeQuoter
function processPoolReturnData(
uint64 destChainSelector,
Internal.EVM2AnyTokenTransfer[] calldata onRampTokenTransfers,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
) external view returns (bytes[] memory destExecDataPerToken) {
bytes4 chainFamilySelector = s_destChainConfigs[destChainSelector].chainFamilySelector;
destExecDataPerToken = new bytes[](onRampTokenTransfers.length);
for (uint256 i = 0; i < onRampTokenTransfers.length; ++i) {
address sourceToken = sourceTokenAmounts[i].token;
// Since the DON has to pay for the extraData to be included on the destination chain, we cap the length of the
// extraData. This prevents gas bomb attacks on the NOPs. As destBytesOverhead accounts for both.
// extraData and offchainData, this caps the worst case abuse to the number of bytes reserved for offchainData.
uint256 destPoolDataLength = onRampTokenTransfers[i].extraData.length;
if (destPoolDataLength > Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) {
if (destPoolDataLength > s_tokenTransferFeeConfig[destChainSelector][sourceToken].destBytesOverhead) {
revert SourceTokenDataTooLarge(sourceToken);
}
}
// We pass '1' here so that SVM validation requires a non-zero token address.
// The 'gasLimit' parameter isn't actually used for gas in this context; it simply
// signals that the address must not be zero on SVM.
_validateDestFamilyAddress(chainFamilySelector, onRampTokenTransfers[i].destTokenAddress, 1);
FeeQuoter.TokenTransferFeeConfig memory tokenTransferFeeConfig =
s_tokenTransferFeeConfig[destChainSelector][sourceToken];
uint32 destGasAmount = tokenTransferFeeConfig.isEnabled
? tokenTransferFeeConfig.destGasOverhead
: s_destChainConfigs[destChainSelector].defaultTokenDestGasOverhead;
// The user will be billed either the default or the override, so we send the exact amount that we billed for
// to the destination chain to be used for the token releaseOrMint and transfer.
destExecDataPerToken[i] = abi.encode(destGasAmount);
}
return destExecDataPerToken;
}
// ================================================================
// │ Configs │
// ================================================================
/// @notice Returns the configured config for the dest chain selector.
/// @param destChainSelector Destination chain selector to fetch config for.
/// @return destChainConfig Config for the destination chain.
function getDestChainConfig(
uint64 destChainSelector
) external view returns (DestChainConfig memory) {
return s_destChainConfigs[destChainSelector];
}
/// @notice Updates the destination chain specific config.
/// @param destChainConfigArgs Array of source chain specific configs.
function applyDestChainConfigUpdates(
DestChainConfigArgs[] memory destChainConfigArgs
) external onlyOwner {
_applyDestChainConfigUpdates(destChainConfigArgs);
}
/// @notice Internal version of applyDestChainConfigUpdates.
function _applyDestChainConfigUpdates(
DestChainConfigArgs[] memory destChainConfigArgs
) internal {
for (uint256 i = 0; i < destChainConfigArgs.length; ++i) {
DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[i];
uint64 destChainSelector = destChainConfigArgs[i].destChainSelector;
DestChainConfig memory destChainConfig = destChainConfigArg.destChainConfig;
// destChainSelector must be non-zero, defaultTxGasLimit must be set, must be less than maxPerMsgGasLimit
if (
destChainSelector == 0 || destChainConfig.defaultTxGasLimit == 0
|| destChainConfig.defaultTxGasLimit > destChainConfig.maxPerMsgGasLimit
|| (
destChainConfig.chainFamilySelector != Internal.CHAIN_FAMILY_SELECTOR_EVM
&& destChainConfig.chainFamilySelector != Internal.CHAIN_FAMILY_SELECTOR_SVM
&& destChainConfig.chainFamilySelector != Internal.CHAIN_FAMILY_SELECTOR_APTOS
&& destChainConfig.chainFamilySelector != Internal.CHAIN_FAMILY_SELECTOR_SUI
)
) {
revert InvalidDestChainConfig(destChainSelector);
}
// If the chain family selector is zero, it indicates that the chain was never configured and we
// are adding a new chain.
if (s_destChainConfigs[destChainSelector].chainFamilySelector == 0) {
emit DestChainAdded(destChainSelector, destChainConfig);
} else {
emit DestChainConfigUpdated(destChainSelector, destChainConfig);
}
s_destChainConfigs[destChainSelector] = destChainConfig;
}
}
/// @notice Returns the static FeeQuoter config.
/// @dev RMN depends on this function, if updated, please notify the RMN maintainers.
/// @return staticConfig The static configuration.
function getStaticConfig() external view returns (StaticConfig memory) {
return StaticConfig({
maxFeeJuelsPerMsg: i_maxFeeJuelsPerMsg,
linkToken: i_linkToken,
tokenPriceStalenessThreshold: i_tokenPriceStalenessThreshold
});
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Client} from "../libraries/Client.sol";
import {Internal} from "../libraries/Internal.sol";
import {IPriceRegistry} from "./IPriceRegistry.sol";
interface IFeeQuoter is IPriceRegistry {
/// @notice Validates the ccip message & returns the fee.
/// @param destChainSelector The destination chain selector.
/// @param message The message to get quote for.
/// @return feeTokenAmount The amount of fee token needed for the fee, in smallest denomination of the fee token.
function getValidatedFee(
uint64 destChainSelector,
Client.EVM2AnyMessage calldata message
) external view returns (uint256 feeTokenAmount);
/// @notice Converts the extraArgs to the latest version and returns the converted message fee in juels.
/// @notice Validates pool return data.
/// @param destChainSelector destination chain selector to process, must be a configured valid chain.
/// @param feeToken token address used to pay for message fees, must be a configured valid fee token.
/// @param feeTokenAmount Fee token amount.
/// @param extraArgs Message extra args that were passed in by the client.
/// @param messageReceiver Message receiver address in bytes from EVM2AnyMessage.receiver
/// @return msgFeeJuels message fee in juels.
/// @return isOutOfOrderExecution true if the message should be executed out of order.
/// @return convertedExtraArgs extra args converted to the latest family-specific args version.
/// @return tokenReceiver token receiver address in bytes on destination chain
function processMessageArgs(
uint64 destChainSelector,
address feeToken,
uint256 feeTokenAmount,
bytes calldata extraArgs,
bytes calldata messageReceiver
)
external
view
returns (
uint256 msgFeeJuels,
bool isOutOfOrderExecution,
bytes memory convertedExtraArgs,
bytes memory tokenReceiver
);
/// @notice Validates pool return data.
/// @param destChainSelector Destination chain selector to which the token amounts are sent to.
/// @param onRampTokenTransfers Token amounts with populated pool return data.
/// @param sourceTokenAmounts Token amounts originally sent in a Client.EVM2AnyMessage message.
/// @return destExecDataPerToken Destination chain execution data.
function processPoolReturnData(
uint64 destChainSelector,
Internal.EVM2AnyTokenTransfer[] calldata onRampTokenTransfers,
Client.EVMTokenAmount[] calldata sourceTokenAmounts
) external view returns (bytes[] memory destExecDataPerToken);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Internal} from "../libraries/Internal.sol";
interface IPriceRegistry {
/// @notice Update the price for given tokens and gas prices for given chains.
/// @param priceUpdates The price updates to apply.
function updatePrices(
Internal.PriceUpdates memory priceUpdates
) external;
/// @notice Get the `tokenPrice` for a given token.
/// @param token The token to get the price for.
/// @return tokenPrice The tokenPrice for the given token.
function getTokenPrice(
address token
) external view returns (Internal.TimestampedPackedUint224 memory);
/// @notice Get the `tokenPrice` for a given token, checks if the price is valid.
/// @param token The token to get the price for.
/// @return tokenPrice The tokenPrice for the given token if it exists and is valid.
function getValidatedTokenPrice(
address token
) external view returns (uint224);
/// @notice Get the `tokenPrice` for an array of tokens.
/// @param tokens The tokens to get prices for.
/// @return tokenPrices The tokenPrices for the given tokens.
function getTokenPrices(
address[] calldata tokens
) external view returns (Internal.TimestampedPackedUint224[] memory);
/// @notice Get an encoded `gasPrice` for a given destination chain ID.
/// The 224-bit result encodes necessary gas price components.
/// On L1 chains like Ethereum or Avax, the only component is the gas price.
/// On Optimistic Rollups, there are two components - the L2 gas price, and L1 base fee for data availability.
/// On future chains, there could be more or differing price components.
/// PriceRegistry does not contain chain-specific logic to parse destination chain price components.
/// @param destChainSelector The destination chain to get the price for.
/// @return gasPrice The encoded gasPrice for the given destination chain ID.
function getDestinationChainGasPrice(
uint64 destChainSelector
) external view returns (Internal.TimestampedPackedUint224 memory);
/// @notice Gets the fee token price and the gas price, both denominated in dollars.
/// @param token The source token to get the price for.
/// @param destChainSelector The destination chain to get the gas price for.
/// @return tokenPrice The price of the feeToken in 1e18 dollars per base unit.
/// @return gasPrice The price of gas in 1e18 dollars per base unit.
function getTokenAndGasPrices(
address token,
uint64 destChainSelector
) external view returns (uint224 tokenPrice, uint224 gasPrice);
/// @notice Convert a given token amount to target token amount.
/// @param fromToken The given token address.
/// @param fromTokenAmount The given token amount.
/// @param toToken The target token address.
/// @return toTokenAmount The target token amount.
function convertTokenAmount(
address fromToken,
uint256 fromTokenAmount,
address toToken
) external view returns (uint256 toTokenAmount);
/// @notice Get the list of fee tokens.
/// @return feeTokens The tokens set as fee tokens.
function getFeeTokens() external view returns (address[] memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// End consumer library.
library Client {
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct EVMTokenAmount {
address token; // token address on the local chain.
uint256 amount; // Amount of tokens.
}
struct Any2EVMMessage {
bytes32 messageId; // MessageId corresponding to ccipSend on source.
uint64 sourceChainSelector; // Source chain selector.
bytes sender; // abi.decode(sender) if coming from an EVM chain.
bytes data; // payload sent in original message.
EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
}
// If extraArgs is empty bytes, the default is 200k gas limit.
struct EVM2AnyMessage {
bytes receiver; // abi.encode(receiver address) for dest EVM chains.
bytes data; // Data payload.
EVMTokenAmount[] tokenAmounts; // Token transfers.
address feeToken; // Address of feeToken. address(0) means you will send msg.value.
bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2).
}
// Tag to indicate only a gas limit. Only usable for EVM as destination chain.
bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;
struct EVMExtraArgsV1 {
uint256 gasLimit;
}
function _argsToBytes(
EVMExtraArgsV1 memory extraArgs
) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);
}
// Tag to indicate a gas limit (or dest chain equivalent processing units) and Out Of Order Execution. This tag is
// available for multiple chain families. If there is no chain family specific tag, this is the default available
// for a chain.
// Note: not available for Solana VM based chains.
bytes4 public constant GENERIC_EXTRA_ARGS_V2_TAG = 0x181dcf10;
/// @param gasLimit: gas limit for the callback on the destination chain.
/// @param allowOutOfOrderExecution: if true, it indicates that the message can be executed in any order relative to
/// other messages from the same sender. This value's default varies by chain. On some chains, a particular value is
/// enforced, meaning if the expected value is not set, the message request will revert.
/// @dev Fully compatible with the previously existing EVMExtraArgsV2.
struct GenericExtraArgsV2 {
uint256 gasLimit;
bool allowOutOfOrderExecution;
}
// Extra args tag for chains that use the Solana VM.
bytes4 public constant SVM_EXTRA_ARGS_V1_TAG = 0x1f3b3aba;
struct SVMExtraArgsV1 {
uint32 computeUnits;
uint64 accountIsWritableBitmap;
bool allowOutOfOrderExecution;
bytes32 tokenReceiver;
// Additional accounts needed for execution of CCIP receiver. Must be empty if message.receiver is zero.
// Token transfer related accounts are specified in the token pool lookup table on SVM.
bytes32[] accounts;
}
/// @dev The maximum number of accounts that can be passed in SVMExtraArgs.
uint256 public constant SVM_EXTRA_ARGS_MAX_ACCOUNTS = 64;
/// @dev The expected static payload size of a token transfer when Borsh encoded and submitted to SVM.
/// TokenPool extra data and offchain data sizes are dynamic, and should be accounted for separately.
uint256 public constant SVM_TOKEN_TRANSFER_DATA_OVERHEAD = (4 + 32) // source_pool
+ 32 // token_address
+ 4 // gas_amount
+ 4 // extra_data overhead
+ 32 // amount
+ 32 // size of the token lookup table account
+ 32 // token-related accounts in the lookup table, over-estimated to 32, typically between 11 - 13
+ 32 // token account belonging to the token receiver, e.g ATA, not included in the token lookup table
+ 32 // per-chain token pool config, not included in the token lookup table
+ 32 // per-chain token billing config, not always included in the token lookup table
+ 32; // OffRamp pool signer PDA, not included in the token lookup table
/// @dev Number of overhead accounts needed for message execution on SVM.
/// @dev These are message.receiver, and the OffRamp Signer PDA specific to the receiver.
uint256 public constant SVM_MESSAGING_ACCOUNTS_OVERHEAD = 2;
/// @dev The size of each SVM account address in bytes.
uint256 public constant SVM_ACCOUNT_BYTE_SIZE = 32;
function _argsToBytes(
GenericExtraArgsV2 memory extraArgs
) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(GENERIC_EXTRA_ARGS_V2_TAG, extraArgs);
}
function _svmArgsToBytes(
SVMExtraArgsV1 memory extraArgs
) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(SVM_EXTRA_ARGS_V1_TAG, extraArgs);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {MerkleMultiProof} from "../libraries/MerkleMultiProof.sol";
/// @notice Library for CCIP internal definitions common to multiple contracts.
/// @dev The following is a non-exhaustive list of "known issues" for CCIP:
/// - We could implement yield claiming for Blast. This is not worth the custom code path on non-blast chains.
/// - uint32 is used for timestamps, which will overflow in 2106. This is not a concern for the current use case, as we
/// expect to have migrated to a new version by then.
library Internal {
error InvalidEVMAddress(bytes encodedAddress);
error Invalid32ByteAddress(bytes encodedAddress);
/// @dev We limit return data to a selector plus 4 words. This is to avoid malicious contracts from returning
/// large amounts of data and causing repeated out-of-gas scenarios.
uint16 internal constant MAX_RET_BYTES = 4 + 4 * 32;
/// @dev The expected number of bytes returned by the balanceOf function.
uint256 internal constant MAX_BALANCE_OF_RET_BYTES = 32;
/// @dev The address used to send calls for gas estimation.
/// You only need to use this address if the minimum gas limit specified by the user is not actually enough to execute the
/// given message and you're attempting to estimate the actual necessary gas limit
address public constant GAS_ESTIMATION_SENDER = address(0xC11C11C11C11C11C11C11C11C11C11C11C11C1);
/// @notice A collection of token price and gas price updates.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct PriceUpdates {
TokenPriceUpdate[] tokenPriceUpdates;
GasPriceUpdate[] gasPriceUpdates;
}
/// @notice Token price in USD.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct TokenPriceUpdate {
address sourceToken; // Source token.
uint224 usdPerToken; // 1e18 USD per 1e18 of the smallest token denomination.
}
/// @notice Gas price for a given chain in USD, its value may contain tightly packed fields.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct GasPriceUpdate {
uint64 destChainSelector; // Destination chain selector.
uint224 usdPerUnitGas; // 1e18 USD per smallest unit (e.g. wei) of destination chain gas.
}
/// @notice A timestamped uint224 value that can contain several tightly packed fields.
struct TimestampedPackedUint224 {
uint224 value; // ────╮ Value in uint224, packed.
uint32 timestamp; // ─╯ Timestamp of the most recent price update.
}
/// @dev Gas price is stored in 112-bit unsigned int. uint224 can pack 2 prices.
/// When packing L1 and L2 gas prices, L1 gas price is left-shifted to the higher-order bits.
/// Using uint8 type, which cannot be higher than other bit shift operands, to avoid shift operand type warning.
uint8 public constant GAS_PRICE_BITS = 112;
struct SourceTokenData {
// The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be
// relied upon by the destination pool to validate the source pool.
bytes sourcePoolAddress;
// The address of the destination token, abi encoded in the case of EVM chains.
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes extraData;
uint32 destGasAmount; // The amount of gas available for the releaseOrMint and balanceOf calls on the offRamp
}
/// @notice Report that is submitted by the execution DON at the execution phase, including chain selector data.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct ExecutionReport {
uint64 sourceChainSelector; // Source chain selector for which the report is submitted.
Any2EVMRampMessage[] messages;
// Contains a bytes array for each message, each inner bytes array contains bytes per transferred token.
bytes[][] offchainTokenData;
bytes32[] proofs;
uint256 proofFlagBits;
}
/// @dev Any2EVMRampMessage struct has 10 fields, including 3 variable unnested arrays, sender, data and tokenAmounts.
/// Each variable array takes 1 more slot to store its length.
/// When abi encoded, excluding array contents, Any2EVMMessage takes up a fixed number of 13 slots, 32 bytes each.
/// Assume 1 slot for sender
/// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 14.
/// The fixed bytes does not cover struct data (this is represented by MESSAGE_FIXED_BYTES_PER_TOKEN)
uint256 public constant MESSAGE_FIXED_BYTES = 32 * 15;
/// @dev Any2EVMTokensTransfer struct bytes length
/// 0x20
/// sourcePoolAddress_offset
/// destTokenAddress
/// destGasAmount
/// extraData_offset
/// amount
/// sourcePoolAddress_length
/// sourcePoolAddress_content // assume 1 slot
/// extraData_length // contents billed separately
uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * (4 + (3 + 2));
bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1");
bytes32 internal constant EVM_2_ANY_MESSAGE_HASH = keccak256("EVM2AnyMessageHashV1");
/// @dev Used to hash messages for multi-lane family-agnostic OffRamps.
/// OnRamp hash(EVM2AnyMessage) != Any2EVMRampMessage.messageId.
/// OnRamp hash(EVM2AnyMessage) != OffRamp hash(Any2EVMRampMessage).
/// @param original OffRamp message to hash.
/// @param metadataHash Hash preimage to ensure global uniqueness.
/// @return hashedMessage hashed message as a keccak256.
function _hash(Any2EVMRampMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) {
// Fixed-size message fields are included in nested hash to reduce stack pressure.
// This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
return keccak256(
abi.encode(
MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
metadataHash,
keccak256(
abi.encode(
original.header.messageId,
original.receiver,
original.header.sequenceNumber,
original.gasLimit,
original.header.nonce
)
),
keccak256(original.sender),
keccak256(original.data),
keccak256(abi.encode(original.tokenAmounts))
)
);
}
function _hash(EVM2AnyRampMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) {
// Fixed-size message fields are included in nested hash to reduce stack pressure.
// This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
return keccak256(
abi.encode(
MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
metadataHash,
keccak256(
abi.encode(
original.sender,
original.header.sequenceNumber,
original.header.nonce,
original.feeToken,
original.feeTokenAmount
)
),
keccak256(original.receiver),
keccak256(original.data),
keccak256(abi.encode(original.tokenAmounts)),
keccak256(original.extraArgs)
)
);
}
/// @dev We disallow the first 1024 addresses to avoid calling into a range known for hosting precompiles. Calling
/// into precompiles probably won't cause any issues, but to be safe we can disallow this range. It is extremely
/// unlikely that anyone would ever be able to generate an address in this range. There is no official range of
/// precompiles, but EIP-7587 proposes to reserve the range 0x100 to 0x1ff. Our range is more conservative, even
/// though it might not be exhaustive for all chains, which is OK. We also disallow the zero address, which is a
/// common practice.
uint256 public constant EVM_PRECOMPILE_SPACE = 1024;
// According to the Aptos docs, the first 0xa addresses are reserved for precompiles.
// https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/doc/account.md#function-create_framework_reserved_account-1
uint256 public constant APTOS_PRECOMPILE_SPACE = 0x0b;
/// @notice This methods provides validation for parsing abi encoded addresses by ensuring the address is within the
/// EVM address space. If it isn't it will revert with an InvalidEVMAddress error, which we can catch and handle
/// more gracefully than a revert from abi.decode.
function _validateEVMAddress(
bytes memory encodedAddress
) internal pure {
if (encodedAddress.length != 32) revert InvalidEVMAddress(encodedAddress);
uint256 encodedAddressUint = abi.decode(encodedAddress, (uint256));
if (encodedAddressUint > type(uint160).max || encodedAddressUint < EVM_PRECOMPILE_SPACE) {
revert InvalidEVMAddress(encodedAddress);
}
}
/// @notice This methods provides validation for parsing abi encoded addresses by ensuring the address is within the
/// bounds of [minValue, uint256.max]. If it isn't it will revert with an Invalid32ByteAddress error.
function _validate32ByteAddress(bytes memory encodedAddress, uint256 minValue) internal pure {
if (encodedAddress.length != 32) revert Invalid32ByteAddress(encodedAddress);
if (minValue > 0) {
if (abi.decode(encodedAddress, (uint256)) < minValue) {
revert Invalid32ByteAddress(encodedAddress);
}
}
}
/// @notice Enum listing the possible message execution states within the offRamp contract.
/// UNTOUCHED never executed.
/// IN_PROGRESS currently being executed, used a replay protection.
/// SUCCESS successfully executed. End state.
/// FAILURE unsuccessfully executed, manual execution is now enabled.
/// @dev RMN depends on this enum, if changing, please notify the RMN maintainers.
enum MessageExecutionState {
UNTOUCHED,
IN_PROGRESS,
SUCCESS,
FAILURE
}
/// @notice CCIP OCR plugin type, used to separate execution & commit transmissions and configs.
enum OCRPluginType {
Commit,
Execution
}
/// @notice Family-agnostic header for OnRamp & OffRamp messages.
/// The messageId is not expected to match hash(message), since it may originate from another ramp family.
struct RampMessageHeader {
bytes32 messageId; // Unique identifier for the message, generated with the source chain's encoding scheme (i.e. not necessarily abi.encoded).
uint64 sourceChainSelector; // ─╮ the chain selector of the source chain, note: not chainId.
uint64 destChainSelector; // │ the chain selector of the destination chain, note: not chainId.
uint64 sequenceNumber; // │ sequence number, not unique across lanes.
uint64 nonce; // ───────────────╯ nonce for this lane for this sender, not unique across senders/lanes.
}
struct EVM2AnyTokenTransfer {
// The source pool EVM address. This value is trusted as it was obtained through the onRamp. It can be relied
// upon by the destination pool to validate the source pool.
address sourcePoolAddress;
// The EVM address of the destination token.
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes extraData;
uint256 amount; // Amount of tokens.
// Destination chain data used to execute the token transfer on the destination chain. For an EVM destination, it
// consists of the amount of gas available for the releaseOrMint and transfer calls made by the offRamp.
bytes destExecData;
}
struct Any2EVMTokenTransfer {
// The source pool EVM address encoded to bytes. This value is trusted as it is obtained through the onRamp. It can
// be relied upon by the destination pool to validate the source pool.
bytes sourcePoolAddress;
address destTokenAddress; // ─╮ Address of destination token
uint32 destGasAmount; // ─────╯ The amount of gas available for the releaseOrMint and transfer calls on the offRamp.
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes extraData;
uint256 amount; // Amount of tokens.
}
/// @notice Family-agnostic message routed to an OffRamp.
/// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage), hash(Any2EVMRampMessage) != messageId due to encoding
/// and parameter differences.
struct Any2EVMRampMessage {
RampMessageHeader header; // Message header.
bytes sender; // sender address on the source chain.
bytes data; // arbitrary data payload supplied by the message sender.
address receiver; // receiver address on the destination chain.
uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution.
Any2EVMTokenTransfer[] tokenAmounts; // array of tokens and amounts to transfer.
}
/// @notice Family-agnostic message emitted from the OnRamp.
/// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage) due to encoding & parameter differences.
/// messageId = hash(EVM2AnyRampMessage) using the source EVM chain's encoding format.
struct EVM2AnyRampMessage {
RampMessageHeader header; // Message header.
address sender; // sender address on the source chain.
bytes data; // arbitrary data payload supplied by the message sender.
bytes receiver; // receiver address on the destination chain.
bytes extraArgs; // destination-chain specific extra args, such as the gasLimit for EVM chains.
address feeToken; // fee token.
uint256 feeTokenAmount; // fee token amount.
uint256 feeValueJuels; // fee amount in Juels.
EVM2AnyTokenTransfer[] tokenAmounts; // array of tokens and amounts to transfer.
}
// bytes4(keccak256("CCIP ChainFamilySelector EVM"));
bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c;
// bytes4(keccak256("CCIP ChainFamilySelector SVM"));
bytes4 public constant CHAIN_FAMILY_SELECTOR_SVM = 0x1e10bdc4;
// bytes4(keccak256("CCIP ChainFamilySelector APTOS"));
bytes4 public constant CHAIN_FAMILY_SELECTOR_APTOS = 0xac77ffec;
// bytes4(keccak256("CCIP ChainFamilySelector SUI"));
bytes4 public constant CHAIN_FAMILY_SELECTOR_SUI = 0xc4e05953;
/// @dev Holds a merkle root and interval for a source chain so that an array of these can be passed in the CommitReport.
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
/// @dev inefficient struct packing intentionally chosen to maintain order of specificity. Not a storage struct so impact is minimal.
// solhint-disable-next-line gas-struct-packing
struct MerkleRoot {
uint64 sourceChainSelector; // Remote source chain selector that the Merkle Root is scoped to
bytes onRampAddress; // Generic onRamp address, to support arbitrary sources; for EVM, use abi.encode
uint64 minSeqNr; // ─────────╮ Minimum sequence number, inclusive
uint64 maxSeqNr; // ─────────╯ Maximum sequence number, inclusive
bytes32 merkleRoot; // Merkle root covering the interval & source chain messages
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
library MerkleMultiProof {
/// @notice Leaf domain separator, should be used as the first 32 bytes of a leaf's preimage.
bytes32 internal constant LEAF_DOMAIN_SEPARATOR = 0x0000000000000000000000000000000000000000000000000000000000000000;
/// @notice Internal domain separator, should be used as the first 32 bytes of an internal node's preimage.
bytes32 internal constant INTERNAL_DOMAIN_SEPARATOR =
0x0000000000000000000000000000000000000000000000000000000000000001;
uint256 internal constant MAX_NUM_HASHES = 256;
error InvalidProof();
error LeavesCannotBeEmpty();
/// @notice Computes the root based on provided pre-hashed leaf nodes in leaves, internal nodes in proofs, and using
/// proofFlagBits' i-th bit to determine if an element of proofs or one of the previously computed leafs or internal
/// nodes will be used for the i-th hash.
/// @param leaves Should be pre-hashed and the first 32 bytes of a leaf's preimage should match LEAF_DOMAIN_SEPARATOR.
/// @param proofs Hashes to be used instead of a leaf hash when the proofFlagBits indicates a proof should be used.
/// @param proofFlagBits A single uint256 of which each bit indicates whether a leaf or a proof needs to be used in
/// a hash operation.
/// @dev the maximum number of hash operations it set to 256. Any input that would require more than 256 hashes to get
/// to a root will revert.
/// @dev For given input `leaves` = [a,b,c] `proofs` = [D] and `proofFlagBits` = 5
/// totalHashes = 3 + 1 - 1 = 3
/// ** round 1 **
/// proofFlagBits = (5 >> 0) & 1 = true
/// hashes[0] = hashPair(a, b)
/// (leafPos, hashPos, proofPos) = (2, 0, 0);
///
/// ** round 2 **
/// proofFlagBits = (5 >> 1) & 1 = false
/// hashes[1] = hashPair(D, c)
/// (leafPos, hashPos, proofPos) = (3, 0, 1);
///
/// ** round 3 **
/// proofFlagBits = (5 >> 2) & 1 = true
/// hashes[2] = hashPair(hashes[0], hashes[1])
/// (leafPos, hashPos, proofPos) = (3, 2, 1);
///
/// i = 3 and no longer < totalHashes. The algorithm is done
/// return hashes[totalHashes - 1] = hashes[2]; the last hash we computed.
// We mark this function as internal to force it to be inlined in contracts that use it, but semantically it is public.
function _merkleRoot(
bytes32[] memory leaves,
bytes32[] memory proofs,
uint256 proofFlagBits
) internal pure returns (bytes32) {
unchecked {
uint256 leavesLen = leaves.length;
uint256 proofsLen = proofs.length;
if (leavesLen == 0) revert LeavesCannotBeEmpty();
if (!(leavesLen <= MAX_NUM_HASHES + 1 && proofsLen <= MAX_NUM_HASHES + 1)) revert InvalidProof();
uint256 totalHashes = leavesLen + proofsLen - 1;
if (!(totalHashes <= MAX_NUM_HASHES)) revert InvalidProof();
if (totalHashes == 0) {
return leaves[0];
}
bytes32[] memory hashes = new bytes32[](totalHashes);
(uint256 leafPos, uint256 hashPos, uint256 proofPos) = (0, 0, 0);
for (uint256 i = 0; i < totalHashes; ++i) {
// Checks if the bit flag signals the use of a supplied proof or a leaf/previous hash.
bytes32 a;
if (proofFlagBits & (1 << i) == (1 << i)) {
// Use a leaf or a previously computed hash.
if (leafPos < leavesLen) {
a = leaves[leafPos++];
} else {
a = hashes[hashPos++];
}
} else {
// Use a supplied proof.
a = proofs[proofPos++];
}
// The second part of the hashed pair is never a proof as hashing two proofs would result in a
// hash that can already be computed offchain.
bytes32 b;
if (leafPos < leavesLen) {
b = leaves[leafPos++];
} else {
b = hashes[hashPos++];
}
if (!(hashPos <= i)) revert InvalidProof();
hashes[i] = _hashPair(a, b);
}
if (!(hashPos == totalHashes - 1 && leafPos == leavesLen && proofPos == proofsLen)) revert InvalidProof();
// Return the last hash.
return hashes[totalHashes - 1];
}
}
/// @notice Hashes two bytes32 objects in their given order, prepended by the INTERNAL_DOMAIN_SEPARATOR.
function _hashInternalNode(bytes32 left, bytes32 right) private pure returns (bytes32 hash) {
return keccak256(abi.encode(INTERNAL_DOMAIN_SEPARATOR, left, right));
}
/// @notice Hashes two bytes32 objects. The order is taken into account, using the lower value first.
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _hashInternalNode(a, b) : _hashInternalNode(b, a);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This library contains various token pool functions to aid constructing the return data.
library Pool {
// The tag used to signal support for the pool v1 standard.
// bytes4(keccak256("CCIP_POOL_V1"))
bytes4 public constant CCIP_POOL_V1 = 0xaff2afbf;
// The number of bytes in the return data for a pool v1 releaseOrMint call.
// This should match the size of the ReleaseOrMintOutV1 struct.
uint16 public constant CCIP_POOL_V1_RET_BYTES = 32;
// The default max number of bytes in the return data for a pool v1 lockOrBurn call.
// This data can be used to send information to the destination chain token pool. Can be overwritten
// in the TokenTransferFeeConfig.destBytesOverhead if more data is required.
uint32 public constant CCIP_LOCK_OR_BURN_V1_RET_BYTES = 32;
struct LockOrBurnInV1 {
bytes receiver; // The recipient of the tokens on the destination chain, abi encoded.
uint64 remoteChainSelector; // ─╮ The chain ID of the destination chain.
address originalSender; // ─────╯ The original sender of the tx on the source chain.
uint256 amount; // The amount of tokens to lock or burn, denominated in the source token's decimals.
address localToken; // The address on this chain of the token to lock or burn.
}
struct LockOrBurnOutV1 {
// The address of the destination token, abi encoded in the case of EVM chains.
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes destPoolData;
}
struct ReleaseOrMintInV1 {
bytes originalSender; // The original sender of the tx on the source chain.
uint64 remoteChainSelector; // ─╮ The chain ID of the source chain.
address receiver; // ───────────╯ The recipient of the tokens on the destination chain.
uint256 amount; // The amount of tokens to release or mint, denominated in the source token's decimals.
address localToken; // The address on this chain of the token to release or mint.
/// @dev WARNING: sourcePoolAddress should be checked prior to any processing of funds. Make sure it matches the
/// expected pool address for the given remoteChainSelector.
bytes sourcePoolAddress; // The address of the source pool, abi encoded in the case of EVM chains.
bytes sourcePoolData; // The data received from the source pool to process the release or mint.
/// @dev WARNING: offchainTokenData is untrusted data.
bytes offchainTokenData; // The offchain data to process the release or mint.
}
struct ReleaseOrMintOutV1 {
// The number of tokens released or minted on the destination chain, denominated in the local token's decimals.
// This value is expected to be equal to the ReleaseOrMintInV1.amount in the case where the source and destination
// chain have the same number of decimals.
uint256 destinationAmount;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library USDPriceWith18Decimals {
/// @notice Takes a price in USD, with 18 decimals per 1e18 token amount, and amount of the smallest token
/// denomination, calculates the value in USD with 18 decimals.
/// @param tokenPrice The USD price of the token.
/// @param tokenAmount Amount of the smallest token denomination.
/// @return USD value with 18 decimals.
/// @dev this function assumes that no more than 1e59 US dollar worth of token is passed in. If more is sent, this
/// function will overflow and revert. Since there isn't even close to 1e59 dollars, this is ok for all legit tokens.
function _calcUSDValueFromTokenAmount(uint224 tokenPrice, uint256 tokenAmount) internal pure returns (uint256) {
/// LINK Example:
/// tokenPrice: 8e18 -> $8/LINK, as 1e18 token amount is 1 LINK, worth 8 USD, or 8e18 with 18 decimals
/// tokenAmount: 2e18 -> 2 LINK
/// result: 8e18 * 2e18 / 1e18 -> 16e18 with 18 decimals = $16
/// USDC Example:
/// tokenPrice: 1e30 -> $1/USDC, as 1e18 token amount is 1e12 USDC, worth 1e12 USD, or 1e30 with 18 decimals
/// tokenAmount: 5e6 -> 5 USDC
/// result: 1e30 * 5e6 / 1e18 -> 5e18 with 18 decimals = $5
return (tokenPrice * tokenAmount) / 1e18;
}
/// @notice Takes a price in USD, with 18 decimals per 1e18 token amount, and USD value with 18 decimals, calculates
/// amount of the smallest token denomination.
/// @param tokenPrice The USD price of the token.
/// @param usdValue USD value with 18 decimals.
/// @return Amount of the smallest token denomination.
function _calcTokenAmountFromUSDValue(uint224 tokenPrice, uint256 usdValue) internal pure returns (uint256) {
/// LINK Example:
/// tokenPrice: 8e18 -> $8/LINK, as 1e18 token amount is 1 LINK, worth 8 USD, or 8e18 with 18 decimals
/// usdValue: 16e18 -> $16
/// result: 16e18 * 1e18 / 8e18 -> 2e18 = 2 LINK
/// USDC Example:
/// tokenPrice: 1e30 -> $1/USDC, as 1e18 token amount is 1e12 USDC, worth 1e12 USD, or 1e30 with 18 decimals
/// usdValue: 5e18 -> $5
/// result: 5e18 * 1e18 / 1e30 -> 5e6 = 5 USDC
return (usdValue * 1e18) / tokenPrice;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Ownable2StepMsgSender} from "../shared/access/Ownable2StepMsgSender.sol";
/// @title Keystone Feeds Permission Handler
/// @notice This contract is designed to manage and validate permissions for accessing specific reports within a decentralized system.
/// @dev The contract uses mappings to keep track of report permissions associated with a unique report ID.
abstract contract KeystoneFeedsPermissionHandler is Ownable2StepMsgSender {
/// @notice Holds the details for permissions of a report
/// @dev Workflow names and report names are stored as bytes to optimize for gas efficiency.
struct Permission {
address forwarder; // ─────╮ The address of the forwarder (20 bytes)
bytes10 workflowName; // │ The name of the workflow in bytes10
bytes2 reportName; // ─────╯ The name of the report in bytes2
address workflowOwner; // ─╮ The address of the workflow owner (20 bytes)
bool isAllowed; // ────────╯ Whether the report is allowed or not (1 byte)
}
/// @notice Event emitted when report permissions are set
event ReportPermissionSet(bytes32 indexed reportId, Permission permission);
/// @notice Error to be thrown when an unauthorized access attempt is made
error ReportForwarderUnauthorized(address forwarder, address workflowOwner, bytes10 workflowName, bytes2 reportName);
/// @dev Mapping from a report ID to a boolean indicating whether the report is allowed or not
mapping(bytes32 reportId => bool isAllowed) internal s_allowedReports;
/// @notice Sets permissions for multiple reports
/// @param permissions An array of Permission structs for which to set permissions
/// @dev Emits a ReportPermissionSet event for each permission set
function setReportPermissions(Permission[] memory permissions) external onlyOwner {
for (uint256 i; i < permissions.length; ++i) {
_setReportPermission(permissions[i]);
}
}
/// @dev Internal function to set a single report permission
/// @param permission The Permission struct containing details about the permission to set
/// @dev Emits a ReportPermissionSet event
function _setReportPermission(Permission memory permission) internal {
bytes32 reportId = _createReportId(
permission.forwarder,
permission.workflowOwner,
permission.workflowName,
permission.reportName
);
s_allowedReports[reportId] = permission.isAllowed;
emit ReportPermissionSet(reportId, permission);
}
/// @dev Internal view function to validate if a report is allowed for a given set of details
/// @param forwarder The address of the forwarder
/// @param workflowOwner The address of the workflow owner
/// @param workflowName The name of the workflow in bytes10
/// @param reportName The name of the report in bytes2
/// @dev Reverts with Unauthorized if the report is not allowed
function _validateReportPermission(
address forwarder,
address workflowOwner,
bytes10 workflowName,
bytes2 reportName
) internal view {
bytes32 reportId = _createReportId(forwarder, workflowOwner, workflowName, reportName);
if (!s_allowedReports[reportId]) {
revert ReportForwarderUnauthorized(forwarder, workflowOwner, workflowName, reportName);
}
}
/// @notice Generates a unique report ID based on the provided parameters.
/// @dev The report ID is computed using the Keccak-256 hash function over the encoded parameters.
/// @param forwarder The address of the forwarder associated with the report.
/// @param workflowOwner The address of the owner of the workflow.
/// @param workflowName The name of the workflow, represented as a 10-byte value.
/// @param reportName The name of the report, represented as a 2-byte value.
/// @return reportId The computed unique report ID as a bytes32 value.
function _createReportId(
address forwarder,
address workflowOwner,
bytes10 workflowName,
bytes2 reportName
) internal pure returns (bytes32 reportId) {
return keccak256(abi.encode(forwarder, workflowOwner, workflowName, reportName));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol";
/// @title IReceiver - receives keystone reports
/// @notice Implementations must support the IReceiver interface through ERC165.
interface IReceiver is IERC165 {
/// @notice Handles incoming keystone reports.
/// @dev If this function call reverts, it can be retried with a higher gas
/// limit. The receiver is responsible for discarding stale reports.
/// @param metadata Report's metadata.
/// @param report Workflow report.
function onReport(bytes calldata metadata, bytes calldata report) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library KeystoneFeedDefaultMetadataLib {
/**
* Metadata Layout:
*
* +-------------------------------+--------------------+---------------------+---------------+
* | 32 bytes (length prefix) | 32 bytes | 10 bytes | 20 bytes | 2 bytes |
* | (Not used in function) | workflow_cid | workflow_name | workflow_owner| report_name |
* +-------------------------------+--------------------+---------------------+---------------+----------------+
* | | | | | |
* | (Offset 0) | (Offset 32) | (Offset 64) | (Offset 74) | (Offset 94) |
* +-------------------------------+--------------------+---------------------+---------------+----------------+
* @dev used to slice metadata bytes into workflowName, workflowOwner and report name
*/
function _extractMetadataInfo(
bytes memory metadata
) internal pure returns (bytes10 workflowName, address workflowOwner, bytes2 reportName) {
// (first 32 bytes contain length of the byte array)
// workflow_cid // offset 32, size 32
// workflow_name // offset 64, size 10
// workflow_owner // offset 74, size 20
// report_name // offset 94, size 2
assembly {
// no shifting needed for bytes10 type
workflowName := mload(add(metadata, 64))
// shift right by 12 bytes to get the actual value
workflowOwner := shr(mul(12, 8), mload(add(metadata, 74)))
// no shifting needed for bytes2 type
reportName := mload(add(metadata, 94))
}
return (workflowName, workflowOwner, reportName);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import {Ownable2StepMsgSender} from "./Ownable2StepMsgSender.sol";
import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol";
/// @title The AuthorizedCallers contract
/// @notice A contract that manages multiple authorized callers. Enables restricting access to certain functions to a set of addresses.
contract AuthorizedCallers is Ownable2StepMsgSender {
using EnumerableSet for EnumerableSet.AddressSet;
event AuthorizedCallerAdded(address caller);
event AuthorizedCallerRemoved(address caller);
error UnauthorizedCaller(address caller);
error ZeroAddressNotAllowed();
/// @notice Update args for changing the authorized callers
struct AuthorizedCallerArgs {
address[] addedCallers;
address[] removedCallers;
}
/// @dev Set of authorized callers
EnumerableSet.AddressSet internal s_authorizedCallers;
/// @param authorizedCallers the authorized callers to set
constructor(address[] memory authorizedCallers) {
_applyAuthorizedCallerUpdates(
AuthorizedCallerArgs({addedCallers: authorizedCallers, removedCallers: new address[](0)})
);
}
/// @return authorizedCallers Returns all authorized callers
function getAllAuthorizedCallers() external view returns (address[] memory) {
return s_authorizedCallers.values();
}
/// @notice Updates the list of authorized callers
/// @param authorizedCallerArgs Callers to add and remove. Removals are performed first.
function applyAuthorizedCallerUpdates(AuthorizedCallerArgs memory authorizedCallerArgs) external onlyOwner {
_applyAuthorizedCallerUpdates(authorizedCallerArgs);
}
/// @notice Updates the list of authorized callers
/// @param authorizedCallerArgs Callers to add and remove. Removals are performed first.
function _applyAuthorizedCallerUpdates(AuthorizedCallerArgs memory authorizedCallerArgs) internal {
address[] memory removedCallers = authorizedCallerArgs.removedCallers;
for (uint256 i = 0; i < removedCallers.length; ++i) {
address caller = removedCallers[i];
if (s_authorizedCallers.remove(caller)) {
emit AuthorizedCallerRemoved(caller);
}
}
address[] memory addedCallers = authorizedCallerArgs.addedCallers;
for (uint256 i = 0; i < addedCallers.length; ++i) {
address caller = addedCallers[i];
if (caller == address(0)) {
revert ZeroAddressNotAllowed();
}
s_authorizedCallers.add(caller);
emit AuthorizedCallerAdded(caller);
}
}
/// @notice Checks the sender and reverts if it is anyone other than a listed authorized caller.
function _validateCaller() internal view {
if (!s_authorizedCallers.contains(msg.sender)) {
revert UnauthorizedCaller(msg.sender);
}
}
/// @notice Checks the sender and reverts if it is anyone other than a listed authorized caller.
modifier onlyAuthorizedCallers() {
_validateCaller();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @notice A minimal contract that implements 2-step ownership transfer and nothing more. It's made to be minimal
/// to reduce the impact of the bytecode size on any contract that inherits from it.
contract Ownable2Step is IOwnable {
/// @notice The pending owner is the address to which ownership may be transferred.
address private s_pendingOwner;
/// @notice The owner is the current owner of the contract.
/// @dev The owner is the second storage variable so any implementing contract could pack other state with it
/// instead of the much less used s_pendingOwner.
address private s_owner;
error OwnerCannotBeZero();
error MustBeProposedOwner();
error CannotTransferToSelf();
error OnlyCallableByOwner();
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
if (newOwner == address(0)) {
revert OwnerCannotBeZero();
}
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice Allows an owner to begin transferring ownership to a new address. The new owner needs to call
/// `acceptOwnership` to accept the transfer before any permissions are changed.
/// @param to The address to which ownership will be transferred.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice validate, transfer ownership, and emit relevant events
/// @param to The address to which ownership will be transferred.
function _transferOwnership(address to) private {
if (to == msg.sender) {
revert CannotTransferToSelf();
}
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
if (msg.sender != s_pendingOwner) {
revert MustBeProposedOwner();
}
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice validate access
function _validateOwnership() internal view {
if (msg.sender != s_owner) {
revert OnlyCallableByOwner();
}
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Ownable2Step} from "./Ownable2Step.sol";
/// @notice Sets the msg.sender to be the owner of the contract and does not set a pending owner.
contract Ownable2StepMsgSender is Ownable2Step {
constructor() Ownable2Step(msg.sender, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITypeAndVersion {
function typeAndVersion() external pure returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}{
"evmVersion": "paris",
"libraries": {},
"metadata": {
"appendCBOR": true,
"bytecodeHash": "none",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 8000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"forge-std/=node_modules/@chainlink/contracts/src/v0.8/vendor/forge-std/src/",
"@chainlink/contracts/=node_modules/@chainlink/contracts/"
],
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"uint96","name":"maxFeeJuelsPerMsg","type":"uint96"},{"internalType":"address","name":"linkToken","type":"address"},{"internalType":"uint32","name":"tokenPriceStalenessThreshold","type":"uint32"}],"internalType":"struct FeeQuoter.StaticConfig","name":"staticConfig","type":"tuple"},{"internalType":"address[]","name":"priceUpdaters","type":"address[]"},{"internalType":"address[]","name":"feeTokens","type":"address[]"},{"components":[{"internalType":"address","name":"sourceToken","type":"address"},{"components":[{"internalType":"address","name":"dataFeedAddress","type":"address"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct FeeQuoter.TokenPriceFeedConfig","name":"feedConfig","type":"tuple"}],"internalType":"struct FeeQuoter.TokenPriceFeedUpdate[]","name":"tokenPriceFeeds","type":"tuple[]"},{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"minFeeUSDCents","type":"uint32"},{"internalType":"uint32","name":"maxFeeUSDCents","type":"uint32"},{"internalType":"uint16","name":"deciBps","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint32","name":"destBytesOverhead","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfig","name":"tokenTransferFeeConfig","type":"tuple"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfigSingleTokenArgs[]","name":"tokenTransferFeeConfigs","type":"tuple[]"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfigArgs[]","name":"tokenTransferFeeConfigArgs","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"}],"internalType":"struct FeeQuoter.PremiumMultiplierWeiPerEthArgs[]","name":"premiumMultiplierWeiPerEthArgs","type":"tuple[]"},{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint8","name":"destGasPerPayloadByteBase","type":"uint8"},{"internalType":"uint8","name":"destGasPerPayloadByteHigh","type":"uint8"},{"internalType":"uint16","name":"destGasPerPayloadByteThreshold","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"bytes4","name":"chainFamilySelector","type":"bytes4"},{"internalType":"bool","name":"enforceOutOfOrder","type":"bool"},{"internalType":"uint16","name":"defaultTokenFeeUSDCents","type":"uint16"},{"internalType":"uint32","name":"defaultTokenDestGasOverhead","type":"uint32"},{"internalType":"uint32","name":"defaultTxGasLimit","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint32","name":"gasPriceStalenessThreshold","type":"uint32"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"}],"internalType":"struct FeeQuoter.DestChainConfig","name":"destChainConfig","type":"tuple"}],"internalType":"struct FeeQuoter.DestChainConfigArgs[]","name":"destChainConfigArgs","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CannotTransferToSelf","type":"error"},{"inputs":[],"name":"DataFeedValueOutOfUint224Range","type":"error"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"DestinationChainNotEnabled","type":"error"},{"inputs":[],"name":"ExtraArgOutOfOrderExecutionMustBeTrue","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"FeeTokenNotSupported","type":"error"},{"inputs":[{"internalType":"bytes","name":"encodedAddress","type":"bytes"}],"name":"Invalid32ByteAddress","type":"error"},{"inputs":[{"internalType":"bytes4","name":"chainFamilySelector","type":"bytes4"}],"name":"InvalidChainFamilySelector","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"destBytesOverhead","type":"uint32"}],"name":"InvalidDestBytesOverhead","type":"error"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"InvalidDestChainConfig","type":"error"},{"inputs":[{"internalType":"bytes","name":"encodedAddress","type":"bytes"}],"name":"InvalidEVMAddress","type":"error"},{"inputs":[],"name":"InvalidExtraArgsData","type":"error"},{"inputs":[],"name":"InvalidExtraArgsTag","type":"error"},{"inputs":[{"internalType":"uint256","name":"minFeeUSDCents","type":"uint256"},{"internalType":"uint256","name":"maxFeeUSDCents","type":"uint256"}],"name":"InvalidFeeRange","type":"error"},{"inputs":[{"internalType":"uint64","name":"accountIsWritableBitmap","type":"uint64"},{"internalType":"uint256","name":"numAccounts","type":"uint256"}],"name":"InvalidSVMExtraArgsWritableBitmap","type":"error"},{"inputs":[],"name":"InvalidStaticConfig","type":"error"},{"inputs":[],"name":"InvalidTokenReceiver","type":"error"},{"inputs":[],"name":"MessageComputeUnitLimitTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"msgFeeJuels","type":"uint256"},{"internalType":"uint256","name":"maxFeeJuelsPerMsg","type":"uint256"}],"name":"MessageFeeTooHigh","type":"error"},{"inputs":[],"name":"MessageGasLimitTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxSize","type":"uint256"},{"internalType":"uint256","name":"actualSize","type":"uint256"}],"name":"MessageTooLarge","type":"error"},{"inputs":[],"name":"MustBeProposedOwner","type":"error"},{"inputs":[],"name":"OnlyCallableByOwner","type":"error"},{"inputs":[],"name":"OwnerCannotBeZero","type":"error"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"address","name":"workflowOwner","type":"address"},{"internalType":"bytes10","name":"workflowName","type":"bytes10"},{"internalType":"bytes2","name":"reportName","type":"bytes2"}],"name":"ReportForwarderUnauthorized","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SourceTokenDataTooLarge","type":"error"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"timePassed","type":"uint256"}],"name":"StaleGasPrice","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"numAccounts","type":"uint256"},{"internalType":"uint256","name":"maxAccounts","type":"uint256"}],"name":"TooManySVMExtraArgsAccounts","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"UnauthorizedCaller","type":"error"},{"inputs":[{"internalType":"uint256","name":"numberOfTokens","type":"uint256"},{"internalType":"uint256","name":"maxNumberOfTokensPerMsg","type":"uint256"}],"name":"UnsupportedNumberOfTokens","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"AuthorizedCallerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"AuthorizedCallerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint8","name":"destGasPerPayloadByteBase","type":"uint8"},{"internalType":"uint8","name":"destGasPerPayloadByteHigh","type":"uint8"},{"internalType":"uint16","name":"destGasPerPayloadByteThreshold","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"bytes4","name":"chainFamilySelector","type":"bytes4"},{"internalType":"bool","name":"enforceOutOfOrder","type":"bool"},{"internalType":"uint16","name":"defaultTokenFeeUSDCents","type":"uint16"},{"internalType":"uint32","name":"defaultTokenDestGasOverhead","type":"uint32"},{"internalType":"uint32","name":"defaultTxGasLimit","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint32","name":"gasPriceStalenessThreshold","type":"uint32"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"}],"indexed":false,"internalType":"struct FeeQuoter.DestChainConfig","name":"destChainConfig","type":"tuple"}],"name":"DestChainAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint8","name":"destGasPerPayloadByteBase","type":"uint8"},{"internalType":"uint8","name":"destGasPerPayloadByteHigh","type":"uint8"},{"internalType":"uint16","name":"destGasPerPayloadByteThreshold","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"bytes4","name":"chainFamilySelector","type":"bytes4"},{"internalType":"bool","name":"enforceOutOfOrder","type":"bool"},{"internalType":"uint16","name":"defaultTokenFeeUSDCents","type":"uint16"},{"internalType":"uint32","name":"defaultTokenDestGasOverhead","type":"uint32"},{"internalType":"uint32","name":"defaultTxGasLimit","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint32","name":"gasPriceStalenessThreshold","type":"uint32"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"}],"indexed":false,"internalType":"struct FeeQuoter.DestChainConfig","name":"destChainConfig","type":"tuple"}],"name":"DestChainConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeToken","type":"address"}],"name":"FeeTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeToken","type":"address"}],"name":"FeeTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"}],"name":"PremiumMultiplierWeiPerEthUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"address","name":"dataFeedAddress","type":"address"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"indexed":false,"internalType":"struct FeeQuoter.TokenPriceFeedConfig","name":"priceFeedConfig","type":"tuple"}],"name":"PriceFeedPerTokenUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"reportId","type":"bytes32"},{"components":[{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"bytes10","name":"workflowName","type":"bytes10"},{"internalType":"bytes2","name":"reportName","type":"bytes2"},{"internalType":"address","name":"workflowOwner","type":"address"},{"internalType":"bool","name":"isAllowed","type":"bool"}],"indexed":false,"internalType":"struct KeystoneFeedsPermissionHandler.Permission","name":"permission","type":"tuple"}],"name":"ReportPermissionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenTransferFeeConfigDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"minFeeUSDCents","type":"uint32"},{"internalType":"uint32","name":"maxFeeUSDCents","type":"uint32"},{"internalType":"uint16","name":"deciBps","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint32","name":"destBytesOverhead","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"indexed":false,"internalType":"struct FeeQuoter.TokenTransferFeeConfig","name":"tokenTransferFeeConfig","type":"tuple"}],"name":"TokenTransferFeeConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"UsdPerTokenUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChain","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"UsdPerUnitGasUpdated","type":"event"},{"inputs":[],"name":"FEE_BASE_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEYSTONE_PRICE_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"addedCallers","type":"address[]"},{"internalType":"address[]","name":"removedCallers","type":"address[]"}],"internalType":"struct AuthorizedCallers.AuthorizedCallerArgs","name":"authorizedCallerArgs","type":"tuple"}],"name":"applyAuthorizedCallerUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint8","name":"destGasPerPayloadByteBase","type":"uint8"},{"internalType":"uint8","name":"destGasPerPayloadByteHigh","type":"uint8"},{"internalType":"uint16","name":"destGasPerPayloadByteThreshold","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"bytes4","name":"chainFamilySelector","type":"bytes4"},{"internalType":"bool","name":"enforceOutOfOrder","type":"bool"},{"internalType":"uint16","name":"defaultTokenFeeUSDCents","type":"uint16"},{"internalType":"uint32","name":"defaultTokenDestGasOverhead","type":"uint32"},{"internalType":"uint32","name":"defaultTxGasLimit","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint32","name":"gasPriceStalenessThreshold","type":"uint32"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"}],"internalType":"struct FeeQuoter.DestChainConfig","name":"destChainConfig","type":"tuple"}],"internalType":"struct FeeQuoter.DestChainConfigArgs[]","name":"destChainConfigArgs","type":"tuple[]"}],"name":"applyDestChainConfigUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"feeTokensToRemove","type":"address[]"},{"internalType":"address[]","name":"feeTokensToAdd","type":"address[]"}],"name":"applyFeeTokensUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"}],"internalType":"struct FeeQuoter.PremiumMultiplierWeiPerEthArgs[]","name":"premiumMultiplierWeiPerEthArgs","type":"tuple[]"}],"name":"applyPremiumMultiplierWeiPerEthUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"minFeeUSDCents","type":"uint32"},{"internalType":"uint32","name":"maxFeeUSDCents","type":"uint32"},{"internalType":"uint16","name":"deciBps","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint32","name":"destBytesOverhead","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfig","name":"tokenTransferFeeConfig","type":"tuple"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfigSingleTokenArgs[]","name":"tokenTransferFeeConfigs","type":"tuple[]"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfigArgs[]","name":"tokenTransferFeeConfigArgs","type":"tuple[]"},{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"address","name":"token","type":"address"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfigRemoveArgs[]","name":"tokensToUseDefaultFeeConfigs","type":"tuple[]"}],"name":"applyTokenTransferFeeConfigUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"uint256","name":"fromTokenAmount","type":"uint256"},{"internalType":"address","name":"toToken","type":"address"}],"name":"convertTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllAuthorizedCallers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"getDestChainConfig","outputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint8","name":"destGasPerPayloadByteBase","type":"uint8"},{"internalType":"uint8","name":"destGasPerPayloadByteHigh","type":"uint8"},{"internalType":"uint16","name":"destGasPerPayloadByteThreshold","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"bytes4","name":"chainFamilySelector","type":"bytes4"},{"internalType":"bool","name":"enforceOutOfOrder","type":"bool"},{"internalType":"uint16","name":"defaultTokenFeeUSDCents","type":"uint16"},{"internalType":"uint32","name":"defaultTokenDestGasOverhead","type":"uint32"},{"internalType":"uint32","name":"defaultTxGasLimit","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint32","name":"gasPriceStalenessThreshold","type":"uint32"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"}],"internalType":"struct FeeQuoter.DestChainConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"getDestinationChainGasPrice","outputs":[{"components":[{"internalType":"uint224","name":"value","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"internalType":"struct Internal.TimestampedPackedUint224","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getPremiumMultiplierWeiPerEth","outputs":[{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticConfig","outputs":[{"components":[{"internalType":"uint96","name":"maxFeeJuelsPerMsg","type":"uint96"},{"internalType":"address","name":"linkToken","type":"address"},{"internalType":"uint32","name":"tokenPriceStalenessThreshold","type":"uint32"}],"internalType":"struct FeeQuoter.StaticConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"getTokenAndGasPrices","outputs":[{"internalType":"uint224","name":"tokenPrice","type":"uint224"},{"internalType":"uint224","name":"gasPriceValue","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenPrice","outputs":[{"components":[{"internalType":"uint224","name":"value","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"internalType":"struct Internal.TimestampedPackedUint224","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenPriceFeedConfig","outputs":[{"components":[{"internalType":"address","name":"dataFeedAddress","type":"address"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct FeeQuoter.TokenPriceFeedConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"getTokenPrices","outputs":[{"components":[{"internalType":"uint224","name":"value","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"internalType":"struct Internal.TimestampedPackedUint224[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"address","name":"token","type":"address"}],"name":"getTokenTransferFeeConfig","outputs":[{"components":[{"internalType":"uint32","name":"minFeeUSDCents","type":"uint32"},{"internalType":"uint32","name":"maxFeeUSDCents","type":"uint32"},{"internalType":"uint16","name":"deciBps","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint32","name":"destBytesOverhead","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct FeeQuoter.TokenTransferFeeConfig","name":"tokenTransferFeeConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"bytes","name":"receiver","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Client.EVMTokenAmount[]","name":"tokenAmounts","type":"tuple[]"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"bytes","name":"extraArgs","type":"bytes"}],"internalType":"struct Client.EVM2AnyMessage","name":"message","type":"tuple"}],"name":"getValidatedFee","outputs":[{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getValidatedTokenPrice","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"metadata","type":"bytes"},{"internalType":"bytes","name":"report","type":"bytes"}],"name":"onReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"internalType":"bytes","name":"extraArgs","type":"bytes"},{"internalType":"bytes","name":"messageReceiver","type":"bytes"}],"name":"processMessageArgs","outputs":[{"internalType":"uint256","name":"msgFeeJuels","type":"uint256"},{"internalType":"bool","name":"isOutOfOrderExecution","type":"bool"},{"internalType":"bytes","name":"convertedExtraArgs","type":"bytes"},{"internalType":"bytes","name":"tokenReceiver","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"components":[{"internalType":"address","name":"sourcePoolAddress","type":"address"},{"internalType":"bytes","name":"destTokenAddress","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"destExecData","type":"bytes"}],"internalType":"struct Internal.EVM2AnyTokenTransfer[]","name":"onRampTokenTransfers","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Client.EVMTokenAmount[]","name":"sourceTokenAmounts","type":"tuple[]"}],"name":"processPoolReturnData","outputs":[{"internalType":"bytes[]","name":"destExecDataPerToken","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"bytes10","name":"workflowName","type":"bytes10"},{"internalType":"bytes2","name":"reportName","type":"bytes2"},{"internalType":"address","name":"workflowOwner","type":"address"},{"internalType":"bool","name":"isAllowed","type":"bool"}],"internalType":"struct KeystoneFeedsPermissionHandler.Permission[]","name":"permissions","type":"tuple[]"}],"name":"setReportPermissions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"sourceToken","type":"address"},{"internalType":"uint224","name":"usdPerToken","type":"uint224"}],"internalType":"struct Internal.TokenPriceUpdate[]","name":"tokenPriceUpdates","type":"tuple[]"},{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"uint224","name":"usdPerUnitGas","type":"uint224"}],"internalType":"struct Internal.GasPriceUpdate[]","name":"gasPriceUpdates","type":"tuple[]"}],"internalType":"struct Internal.PriceUpdates","name":"priceUpdates","type":"tuple"}],"name":"updatePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sourceToken","type":"address"},{"components":[{"internalType":"address","name":"dataFeedAddress","type":"address"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct FeeQuoter.TokenPriceFeedConfig","name":"feedConfig","type":"tuple"}],"internalType":"struct FeeQuoter.TokenPriceFeedUpdate[]","name":"tokenPriceFeedUpdates","type":"tuple[]"}],"name":"updateTokenPriceFeeds","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e06040523461109a576173ec8038038061001981611306565b928339810190808203610120811261109a5760601361109a5761003a6112c8565b81516001600160601b038116810361109a57815261005a6020830161132b565b906020810191825261006e6040840161133f565b6040820190815260608401516001600160401b03811161109a5785610094918601611367565b60808501519094906001600160401b03811161109a57866100b6918301611367565b60a08201519096906001600160401b03811161109a5782019080601f8301121561109a5781516100ed6100e882611350565b611306565b9260208085848152019260071b8201019083821161109a57602001915b8183106112535750505060c08301516001600160401b03811161109a5783019781601f8a01121561109a578851986101446100e88b611350565b996020808c838152019160051b8301019184831161109a5760208101915b8383106110f1575050505060e08401516001600160401b03811161109a5784019382601f8601121561109a57845161019c6100e882611350565b9560208088848152019260061b8201019085821161109a57602001915b8183106110b557505050610100810151906001600160401b03821161109a570182601f8201121561109a578051906101f36100e883611350565b93602061028081878681520194028301019181831161109a57602001925b828410610ed857505050503315610ec757600180546001600160a01b031916331790556020986102408a611306565b976000895260003681376102526112e7565b998a52888b8b015260005b89518110156102c4576001906001600160a01b0361027b828d611400565b51168d610287826115ec565b610294575b50500161025d565b7fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda7758091604051908152a1388d61028c565b508a985089519660005b885181101561033f576001600160a01b036102e9828b611400565b511690811561032e577feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef8c83610320600195611574565b50604051908152a1016102ce565b6342bcdf7f60e11b60005260046000fd5b5081518a985089906001600160a01b0316158015610eb5575b8015610ea6575b610e955791516001600160a01b031660a05290516001600160601b03166080525163ffffffff1660c05261039286611306565b9360008552600036813760005b855181101561040e576001906103c76001600160a01b036103c0838a611400565b5116611481565b6103d2575b0161039f565b818060a01b036103e28289611400565b51167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f91600080a26103cc565b508694508560005b84518110156104855760019061043e6001600160a01b036104378389611400565b51166115b3565b610449575b01610416565b818060a01b036104598288611400565b51167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba23600080a2610443565b508593508460005b835181101561054757806104a360019286611400565b517fe6a7a17d710bf0b2cd05e5397dc6f97a5da4ee79e31e234bf5f965ee2bd9a5bf606089858060a01b038451169301518360005260078b5260406000209060ff878060a01b038251169283898060a01b03198254161781558d8301908151604082549501948460a81b8651151560a81b16918560a01b9060a01b169061ffff60a01b19161717905560405193845251168c8301525115156040820152a20161048d565b5091509160005b8251811015610b2d576105618184611400565b51856001600160401b036105758487611400565b5151169101519080158015610b1a575b8015610afc575b8015610a92575b610a7e57600081815260098852604090205460019392919060081b6001600160e01b03191661093657807f71e9302ab4e912a9678ae7f5a8542856706806f2817e1bf2a20b171e265cb4ad604051806106fc868291909161024063ffffffff8161026084019580511515855261ffff602082015116602086015282604082015116604086015282606082015116606086015282608082015116608086015260ff60a08201511660a086015260ff60c08201511660c086015261ffff60e08201511660e0860152826101008201511661010086015261ffff6101208201511661012086015261ffff610140820151166101408601528260e01b61016082015116610160860152610180810151151561018086015261ffff6101a0820151166101a0860152826101c0820151166101c0860152826101e0820151166101e086015260018060401b03610200820151166102008601528261022082015116610220860152015116910152565b0390a25b60005260098752826040600020825115158382549162ffff008c83015160081b169066ffffffff000000604084015160181b166affffffff00000000000000606085015160381b16926effffffff0000000000000000000000608086015160581b169260ff60781b60a087015160781b169460ff60801b60c088015160801b169161ffff60881b60e089015160881b169063ffffffff60981b6101008a015160981b169361ffff60b81b6101208b015160b81b169661ffff60c81b6101408c015160c81b169963ffffffff60d81b6101608d015160081c169b61018060ff60f81b910151151560f81b169c8f8060f81b039a63ffffffff60d81b199961ffff60c81b199861ffff60b81b199763ffffffff60981b199661ffff60881b199560ff60801b199460ff60781b19936effffffff0000000000000000000000199260ff6affffffff000000000000001992169066ffffffffffffff19161716171617161716171617161716171617161716179063ffffffff60d81b1617178155019061ffff6101a0820151169082549165ffffffff00006101c083015160101b169269ffffffff0000000000006101e084015160301b166a01000000000000000000008860901b0361020085015160501b169263ffffffff60901b61022086015160901b169461024063ffffffff60b01b91015160b01b169563ffffffff60b01b199363ffffffff60901b19926a01000000000000000000008c60901b0319918c8060501b03191617161716171617171790550161054e565b807f2431cc0363f2f66b21782c7e3d54dd9085927981a21bd0cc6be45a51b19689e360405180610a76868291909161024063ffffffff8161026084019580511515855261ffff602082015116602086015282604082015116604086015282606082015116606086015282608082015116608086015260ff60a08201511660a086015260ff60c08201511660c086015261ffff60e08201511660e0860152826101008201511661010086015261ffff6101208201511661012086015261ffff610140820151166101408601528260e01b61016082015116610160860152610180810151151561018086015261ffff6101a0820151166101a0860152826101c0820151166101c0860152826101e0820151166101e086015260018060401b03610200820151166102008601528261022082015116610220860152015116910152565b0390a2610700565b63c35aa79d60e01b60005260045260246000fd5b5063ffffffff60e01b61016083015116630a04b54b60e21b8114159081610aea575b81610ad8575b81610ac6575b50610593565b63c4e0595360e01b1415905088610ac0565b632b1dfffb60e21b8114159150610aba565b6307842f7160e21b8114159150610ab4565b5063ffffffff6101e08301511663ffffffff6060840151161061058c565b5063ffffffff6101e08301511615610585565b84828560005b8151811015610bb3576001906001600160a01b03610b518285611400565b5151167fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d86848060401b0381610b878689611400565b510151168360005260088252604060002081878060401b0319825416179055604051908152a201610b33565b83600184610bc083611306565b9060008252600092610e90575b909282935b8251851015610dcf57610be58584611400565b5180516001600160401b0316939083019190855b83518051821015610dbe57610c0f828792611400565b51015184516001600160a01b0390610c28908490611400565b5151169063ffffffff815116908781019163ffffffff8351169081811015610da95750506080810163ffffffff815116898110610d92575090899291838c52600a8a5260408c20600160a01b6001900386168d528a5260408c2092825163ffffffff169380549180518d1b67ffffffff0000000016916040860192835160401b69ffff000000000000000016966060810195865160501b6dffffffff00000000000000000000169063ffffffff60701b895160701b169260a001998b60ff60901b8c51151560901b169560ff60901b199363ffffffff60701b19926dffffffff000000000000000000001991600160501b60019003191617161716171617171790556040519586525163ffffffff168c8601525161ffff1660408501525163ffffffff1660608401525163ffffffff16608083015251151560a082015260c07f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b591a3600101610bf9565b6312766e0160e11b8c52600485905260245260448bfd5b6305a7b3d160e11b8c5260045260245260448afd5b505060019096019593509050610bd2565b9150825b8251811015610e51576001906001600160401b03610df18286611400565b515116828060a01b0384610e058488611400565b5101511690808752600a855260408720848060a01b038316885285528660408120557f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b8780a301610dd3565b604051615d6b908161168182396080518181816105dc0152610c33015260a0518181816106120152610be4015260c05181818161063901526138980152f35b610bcd565b63d794ef9560e01b60005260046000fd5b5063ffffffff8251161561035f565b5080516001600160601b031615610358565b639b15e16f60e01b60005260046000fd5b838203610280811261109a57610260610eef6112e7565b91610ef9876113dd565b8352601f19011261109a576040519161026083016001600160401b0381118482101761109f57604052610f2e602087016113d0565b8352610f3c604087016113f1565b6020840152610f4d6060870161133f565b6040840152610f5e6080870161133f565b6060840152610f6f60a0870161133f565b6080840152610f8060c087016113c2565b60a0840152610f9160e087016113c2565b60c0840152610fa361010087016113f1565b60e0840152610fb5610120870161133f565b610100840152610fc861014087016113f1565b610120840152610fdb61016087016113f1565b610140840152610180860151916001600160e01b03198316830361109a57836020936101606102809601526110136101a089016113d0565b6101808201526110266101c089016113f1565b6101a08201526110396101e0890161133f565b6101c082015261104c610200890161133f565b6101e082015261105f61022089016113dd565b610200820152611072610240890161133f565b610220820152611085610260890161133f565b61024082015283820152815201930192610211565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60408387031261109a5760206040916110cc6112e7565b6110d58661132b565b81526110e28387016113dd565b838201528152019201916101b9565b82516001600160401b03811161109a5782016040818803601f19011261109a576111196112e7565b90611126602082016113dd565b825260408101516001600160401b03811161109a57602091010187601f8201121561109a5780516111596100e882611350565b91602060e08185858152019302820101908a821161109a57602001915b8183106111955750505091816020938480940152815201920191610162565b828b0360e0811261109a5760c06111aa6112e7565b916111b48661132b565b8352601f19011261109a576040519160c08301916001600160401b0383118484101761109f5760e0936020936040526111ee84880161133f565b81526111fc6040880161133f565b8482015261120c606088016113f1565b604082015261121d6080880161133f565b606082015261122e60a0880161133f565b608082015261123f60c088016113d0565b60a082015283820152815201920191611176565b8284036080811261109a5760606112686112e7565b916112728661132b565b8352601f19011261109a5760809160209161128b6112c8565b61129684880161132b565b81526112a4604088016113c2565b848201526112b4606088016113d0565b60408201528382015281520192019161010a565b60405190606082016001600160401b0381118382101761109f57604052565b60408051919082016001600160401b0381118382101761109f57604052565b6040519190601f01601f191682016001600160401b0381118382101761109f57604052565b51906001600160a01b038216820361109a57565b519063ffffffff8216820361109a57565b6001600160401b03811161109f5760051b60200190565b9080601f8301121561109a5781516113816100e882611350565b9260208085848152019260051b82010192831161109a57602001905b8282106113aa5750505090565b602080916113b78461132b565b81520191019061139d565b519060ff8216820361109a57565b5190811515820361109a57565b51906001600160401b038216820361109a57565b519061ffff8216820361109a57565b80518210156114145760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b80548210156114145760005260206000200190600090565b8054801561146b576000190190611459828261142a565b8154906000199060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b6000818152600c6020526040902054801561154257600019810181811161152c57600b5460001981019190821161152c578181036114db575b5050506114c7600b611442565b600052600c60205260006040812055600190565b6115146114ec6114fd93600b61142a565b90549060031b1c928392600b61142a565b819391549060031b91821b91600019901b19161790565b9055600052600c6020526040600020553880806114ba565b634e487b7160e01b600052601160045260246000fd5b5050600090565b8054906801000000000000000082101561109f57816114fd9160016115709401815561142a565b9055565b806000526003602052604060002054156000146115ad57611596816002611549565b600254906000526003602052604060002055600190565b50600090565b80600052600c602052604060002054156000146115ad576115d581600b611549565b600b5490600052600c602052604060002055600190565b600081815260036020526040902054801561154257600019810181811161152c5760025460001981019190821161152c57808203611646575b5050506116326002611442565b600052600360205260006040812055600190565b6116686116576114fd93600261142a565b90549060031b1c928392600261142a565b9055600052600360205260406000205538808061162556fe6080604052600436101561001257600080fd5b60003560e01c806241e5be1461021657806301447eaa1461021157806301ffc9a71461020c578063061877e31461020757806306285c6914610202578063181f5a77146101fd5780632451a627146101f8578063325c868e146101f35780633937306f146101ee5780633a49bb49146101e957806341ed29e7146101e457806345ac924d146101df5780634ab35b0b146101da578063514e8cff146101d55780636def4ce7146101d0578063770e2dc4146101cb57806379ba5097146101c65780637afac322146101c1578063805f2132146101bc57806382b49eb0146101b757806387b8d879146101b25780638da5cb5b146101ad57806391a2749a146101a8578063a69c64c0146101a3578063bf78e03f1461019e578063cdc73d5114610199578063d02641a014610194578063d63d3af21461018f578063d8694ccd1461018a578063f2fde38b14610185578063fbe3f778146101805763ffdb4b371461017b57600080fd5b612642565b612545565b612489565b612056565b61203a565b611ff1565b611f7a565b611ed4565b611e1b565b611d87565b611d60565b611b44565b6119c7565b61172c565b6115f3565b6114db565b6112bc565b61113d565b610f7e565b610f46565b610e7d565b610ce8565b610b72565b610898565b61087c565b6107f9565b610757565b6105a0565b610558565b610464565b6103b0565b61023e565b6001600160a01b0381160361022c57565b600080fd5b359061023c8261021b565b565b3461022c57606060031936011261022c5760206102756004356102608161021b565b602435604435916102708361021b565b6127f2565b604051908152f35b6004359067ffffffffffffffff8216820361022c57565b6024359067ffffffffffffffff8216820361022c57565b359067ffffffffffffffff8216820361022c57565b9181601f8401121561022c5782359167ffffffffffffffff831161022c576020808501948460051b01011161022c57565b919082519283825260005b84811061031d575050601f19601f8460006020809697860101520116010190565b806020809284010151828286010152016102fc565b602081016020825282518091526040820191602060408360051b8301019401926000915b83831061036557505050505090565b90919293946020806103a1837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0866001960301875289516102f1565b97019301930191939290610356565b3461022c57606060031936011261022c576103c961027d565b60243567ffffffffffffffff811161022c576103e99036906004016102c0565b6044929192359167ffffffffffffffff831161022c573660238401121561022c5782600401359167ffffffffffffffff831161022c573660248460061b8601011161022c5761044b94602461043f950192612a00565b60405191829182610332565b0390f35b35906001600160e01b03198216820361022c57565b3461022c57602060031936011261022c576004356001600160e01b03198116810361022c576001600160e01b0319602091167f805f213200000000000000000000000000000000000000000000000000000000811490811561052e575b8115610504575b81156104da575b506040519015158152f35b7f01ffc9a700000000000000000000000000000000000000000000000000000000915014386104cf565b7f181f5a7700000000000000000000000000000000000000000000000000000000811491506104c8565b7fe364892e00000000000000000000000000000000000000000000000000000000811491506104c1565b3461022c57602060031936011261022c576001600160a01b0360043561057d8161021b565b166000526008602052602067ffffffffffffffff60406000205416604051908152f35b3461022c57600060031936011261022c576105b9612c19565b5060606040516105c8816106a0565b63ffffffff6bffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016918281526001600160a01b0360406020830192827f00000000000000000000000000000000000000000000000000000000000000001684520191837f00000000000000000000000000000000000000000000000000000000000000001683526040519485525116602084015251166040820152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176106bc57604052565b610671565b60a0810190811067ffffffffffffffff8211176106bc57604052565b6040810190811067ffffffffffffffff8211176106bc57604052565b60c0810190811067ffffffffffffffff8211176106bc57604052565b90601f601f19910116810190811067ffffffffffffffff8211176106bc57604052565b6040519061023c604083610715565b6040519061023c61026083610715565b3461022c57600060031936011261022c5761044b604080519061077a8183610715565b600f82527f46656551756f74657220312e362e3000000000000000000000000000000000006020830152519182916020835260208301906102f1565b602060408183019282815284518094520192019060005b8181106107da5750505090565b82516001600160a01b03168452602093840193909201916001016107cd565b3461022c57600060031936011261022c5760405180602060025491828152019060026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9060005b8181106108665761044b8561085a81870382610715565b604051918291826107b6565b8254845260209093019260019283019201610843565b3461022c57600060031936011261022c57602060405160248152f35b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c5780600401906040600319823603011261022c576108d6613d82565b6108e08280612c38565b4263ffffffff1692915060005b818110610a39575050602401906109048284612c38565b92905060005b83811061091357005b8061093261092d600193610927868a612c38565b9061289b565b612cd7565b7fdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e67ffffffffffffffff610a15610a0760208501946109f961097b87516001600160e01b031690565b610995610986610738565b6001600160e01b039092168252565b63ffffffff8c1660208201526109d06109b6845167ffffffffffffffff1690565b67ffffffffffffffff166000526005602052604060002090565b815160209092015160e01b6001600160e01b0319166001600160e01b0392909216919091179055565b5167ffffffffffffffff1690565b93516001600160e01b031690565b604080516001600160e01b039290921682524260208301529190931692a20161090a565b80610a52610a4d6001936109278980612c38565b612ca0565b7f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a6001600160a01b03610aeb610a076020850194610ade610a9a87516001600160e01b031690565b610aa5610986610738565b63ffffffff8d1660208201526109d0610ac584516001600160a01b031690565b6001600160a01b03166000526006602052604060002090565b516001600160a01b031690565b604080516001600160e01b039290921682524260208301529190931692a2016108ed565b9181601f8401121561022c5782359167ffffffffffffffff831161022c576020838186019501011161022c57565b92610b6f9492610b61928552151560208501526080604085015260808401906102f1565b9160608184039101526102f1565b90565b3461022c5760a060031936011261022c57610b8b61027d565b60243590610b988261021b565b6044359160643567ffffffffffffffff811161022c57610bbc903690600401610b0f565b93909160843567ffffffffffffffff811161022c57610bdf903690600401610b0f565b9290917f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b0382166001600160a01b03821614600014610cab575050935b6bffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016808611610c7a575091610c6b939161044b9693613dc6565b90939160405194859485610b3d565b857f6a92a4830000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b91610cb5926127f2565b93610c24565b67ffffffffffffffff81116106bc5760051b60200190565b8015150361022c57565b359061023c82610cd3565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c57806004013590610d2582610cbb565b90610d336040519283610715565b828252602460a06020840194028201019036821161022c57602401925b818410610d6257610d6083612cfc565b005b60a08436031261022c5760405190610d79826106c1565b8435610d848161021b565b825260208501357fffffffffffffffffffff000000000000000000000000000000000000000000008116810361022c5760208301526040850135907fffff0000000000000000000000000000000000000000000000000000000000008216820361022c5782602092604060a0950152610dff60608801610231565b6060820152610e1060808801610cdd565b6080820152815201930192610d50565b602060408183019282815284518094520192019060005b818110610e445750505090565b9091926020604082610e72600194885163ffffffff602080926001600160e01b038151168552015116910152565b019401929101610e37565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c57610eae9036906004016102c0565b610eb781610cbb565b91610ec56040519384610715565b818352601f19610ed483610cbb565b0160005b818110610f2f57505060005b82811015610f2157600190610f05610f008260051b85016128b0565b613844565b610f0f82876129ec565b52610f1a81866129ec565b5001610ee4565b6040518061044b8682610e20565b602090610f3a612e5a565b82828801015201610ed8565b3461022c57602060031936011261022c576020610f6d600435610f688161021b565b613b24565b6001600160e01b0360405191168152f35b3461022c57602060031936011261022c5767ffffffffffffffff610fa061027d565b610fa8612e5a565b50166000526005602052604060002060405190610fc4826106dd565b546001600160e01b038116825260e01c6020820152604051809161044b82604081019263ffffffff602080926001600160e01b038151168552015116910152565b61023c909291926102408061026083019561102284825115159052565b60208181015161ffff169085015260408181015163ffffffff169085015260608181015163ffffffff169085015260808181015163ffffffff169085015260a08181015160ff169085015260c08181015160ff169085015260e08181015161ffff16908501526101008181015163ffffffff16908501526101208181015161ffff16908501526101408181015161ffff1690850152610160818101516001600160e01b03191690850152610180818101511515908501526101a08181015161ffff16908501526101c08181015163ffffffff16908501526101e08181015163ffffffff16908501526102008181015167ffffffffffffffff16908501526102208181015163ffffffff1690850152015163ffffffff16910152565b3461022c57602060031936011261022c5761044b6112006111fb61115f61027d565b600061024061116c610747565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e08201528261020082015282610220820152015267ffffffffffffffff166000526009602052604060002090565b612e98565b60405191829182611005565b359063ffffffff8216820361022c57565b359061ffff8216820361022c57565b81601f8201121561022c5780359061124382610cbb565b926112516040519485610715565b82845260208085019360061b8301019181831161022c57602001925b82841061127b575050505090565b60408483031261022c5760206040918251611295816106dd565b61129e876102ab565b8152828701356112ad8161021b565b8382015281520193019261126d565b3461022c57604060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c5780600401356112f881610cbb565b916113066040519384610715565b8183526024602084019260051b8201019036821161022c5760248101925b828410611355576024358567ffffffffffffffff821161022c5761134f610d6092369060040161122c565b90612fee565b833567ffffffffffffffff811161022c57820160407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc823603011261022c57604051906113a1826106dd565b6113ad602482016102ab565b8252604481013567ffffffffffffffff811161022c57602491010136601f8201121561022c5780356113de81610cbb565b916113ec6040519384610715565b818352602060e081850193028201019036821161022c57602001915b8183106114275750505091816020938480940152815201930192611324565b82360360e0811261022c5760c0601f1960405192611444846106dd565b863561144f8161021b565b8452011261022c5760e091602091604051611469816106f9565b61147484880161120c565b81526114826040880161120c565b848201526114926060880161121d565b60408201526114a36080880161120c565b60608201526114b460a0880161120c565b608082015260c08701356114c781610cd3565b60a082015283820152815201920191611408565b3461022c57600060031936011261022c576000546001600160a01b0381163303611562577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154913382841617600155166000556001600160a01b033391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f02b543c60000000000000000000000000000000000000000000000000000000060005260046000fd5b9080601f8301121561022c5781356115a381610cbb565b926115b16040519485610715565b81845260208085019260051b82010192831161022c57602001905b8282106115d95750505090565b6020809183356115e88161021b565b8152019101906115cc565b3461022c57604060031936011261022c5760043567ffffffffffffffff811161022c5761162490369060040161158c565b60243567ffffffffffffffff811161022c5761164490369060040161158c565b9061164d613fd8565b60005b81518110156116bc578061167161166c610ade600194866129ec565b615a24565b61167c575b01611650565b6001600160a01b03611691610ade83866129ec565b167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f91600080a2611676565b8260005b8151811015610d6057806116e16116dc610ade600194866129ec565b615a38565b6116ec575b016116c0565b6001600160a01b03611701610ade83866129ec565b167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba23600080a26116e6565b3461022c57604060031936011261022c5760043567ffffffffffffffff811161022c5761175d903690600401610b0f565b6024359167ffffffffffffffff831161022c576117b66117ae61179461178a6117be963690600401610b0f565b949095369161294b565b90604082015190605e604a84015160601c93015191929190565b91903361416a565b81019061329d565b60005b8151811015610d60576118096118046117eb6117dd84866129ec565b51516001600160a01b031690565b6001600160a01b03166000526007602052604060002090565b61335c565b61181d6118196040830151151590565b1590565b61197e57906118686118356020600194015160ff1690565b611862611856602061184786896129ec565b5101516001600160e01b031690565b6001600160e01b031690565b9061423c565b611883604061187784876129ec565b51015163ffffffff1690565b63ffffffff6118ae6118a561189e610ac56117dd888b6129ec565b5460e01c90565b63ffffffff1690565b911610611978576118fc6118c7604061187785886129ec565b6118ec6118d2610738565b6001600160e01b03851681529163ffffffff166020830152565b6109d0610ac56117dd86896129ec565b7f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a6001600160a01b036119326117dd85886129ec565b61196e6119446040611877888b6129ec565b60405193849316958390929163ffffffff6020916001600160e01b03604085019616845216910152565b0390a25b016117c1565b50611972565b6119c361198e6117dd84866129ec565b7f06439c6b000000000000000000000000000000000000000000000000000000006000526001600160a01b0316600452602490565b6000fd5b3461022c57604060031936011261022c5761044b611a4f6119e661027d565b67ffffffffffffffff602435916119fc8361021b565b600060a0604051611a0c816106f9565b828152826020820152826040820152826060820152826080820152015216600052600a6020526040600020906001600160a01b0316600052602052604060002090565b611acb611ac260405192611a62846106f9565b5463ffffffff8116845263ffffffff8160201c16602085015261ffff8160401c166040850152611aa9611a9c8263ffffffff9060501c1690565b63ffffffff166060860152565b63ffffffff607082901c16608085015260901c60ff1690565b151560a0830152565b6040519182918291909160a08060c083019463ffffffff815116845263ffffffff602082015116602085015261ffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015201511515910152565b60ff81160361022c57565b359061023c82611b2e565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c57806004013590611b8182610cbb565b90611b8f6040519283610715565b82825260246102806020840194028201019036821161022c57602401925b818410611bbd57610d6083613392565b833603610280811261022c57610260601f1960405192611bdc846106dd565b611be5886102ab565b8452011261022c5761028091602091611bfc610747565b611c07848901610cdd565b8152611c156040890161121d565b84820152611c256060890161120c565b6040820152611c366080890161120c565b6060820152611c4760a0890161120c565b6080820152611c5860c08901611b39565b60a0820152611c6960e08901611b39565b60c0820152611c7b610100890161121d565b60e0820152611c8d610120890161120c565b610100820152611ca0610140890161121d565b610120820152611cb3610160890161121d565b610140820152611cc6610180890161044f565b610160820152611cd96101a08901610cdd565b610180820152611cec6101c0890161121d565b6101a0820152611cff6101e0890161120c565b6101c0820152611d12610200890161120c565b6101e0820152611d2561022089016102ab565b610200820152611d38610240890161120c565b610220820152611d4b610260890161120c565b61024082015283820152815201930192611bad565b3461022c57600060031936011261022c5760206001600160a01b0360015416604051908152f35b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c576040600319823603011261022c57604051611dc4816106dd565b816004013567ffffffffffffffff811161022c57611de8906004369185010161158c565b8152602482013567ffffffffffffffff811161022c57610d60926004611e11923692010161158c565b6020820152613616565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c57806004013590611e5882610cbb565b90611e666040519283610715565b8282526024602083019360061b8201019036821161022c57602401925b818410611e9357610d6083613768565b60408436031261022c5760206040918251611ead816106dd565b8635611eb88161021b565b8152611ec58388016102ab565b83820152815201930192611e83565b3461022c57602060031936011261022c576001600160a01b03600435611ef98161021b565b611f01612c19565b5016600052600760205261044b604060002060ff60405191611f22836106a0565b546001600160a01b0381168352818160a01c16602084015260a81c16151560408201526040519182918291909160408060608301946001600160a01b03815116845260ff602082015116602085015201511515910152565b3461022c57600060031936011261022c57604051806020600b54918281520190600b6000527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db99060005b818110611fdb5761044b8561085a81870382610715565b8254845260209093019260019283019201611fc4565b3461022c57602060031936011261022c576040612013600435610f008161021b565b6120388251809263ffffffff602080926001600160e01b038151168552015116910152565bf35b3461022c57600060031936011261022c57602060405160128152f35b3461022c57604060031936011261022c5761206f61027d565b60243567ffffffffffffffff811161022c578060040160a0600319833603011261022c576120b46111fb8467ffffffffffffffff166000526009602052604060002090565b916120c26118198451151590565b61245157606481016120f86118196120d9836128b0565b6001600160a01b03166000526001600b01602052604060002054151590565b6124105783906044830161210c8186612c38565b95905061211a81858a614b75565b92612127610f68826128b0565b97889361214561213f61022084015163ffffffff1690565b8c615075565b9a6000808b156123d75750506121dd6121aa61ffff856121cf996121b69998966121ea9661218e6101c06121826101a06121f09d015161ffff1690565b95015163ffffffff1690565b6121a161219a8b6128b0565b938d612c38565b96909516615166565b989198979098946128b0565b6001600160a01b03166000526008602052604060002090565b5467ffffffffffffffff1690565b67ffffffffffffffff1690565b906127a6565b9560009761ffff61220761014089015161ffff1690565b16612379575b50946121ea6121dd6102006122df61044b9d6dffffffffffffffffffffffffffff6122d76122f79f9e9b6122d26001600160e01b039f9b9c6122ef9f6122d29e63ffffffff6122666122d29f60246122709501906128fa565b929050169061397c565b908b60a0810161229361228d612287835160ff1690565b60ff1690565b856127a6565b9360e08301916122a5835161ffff1690565b9061ffff82168311612307575b50505050608001516122d2916118a59163ffffffff166139ba565b6139ba565b61397c565b9116906127a6565b93015167ffffffffffffffff1690565b9116906127b9565b6040519081529081906020820190565b6118a59496506122d2959361ffff6123686123576122cd9661235161234a61234160809960ff61233b61236f9b5160ff1690565b16613989565b965161ffff1690565b61ffff1690565b90613837565b6121ea61228760c08d015160ff1690565b911661397c565b95938395506122b2565b9095949897508261239f8b989495986dffffffffffffffffffffffffffff9060701c1690565b6dffffffffffffffffffffffffffff16916123bd60248901856128fa565b90506123c9938861536b565b96979394389693929661220d565b959492509550506121ea6121dd6121cf6121b661240a6124056118a56102406121f099015163ffffffff1690565b612731565b946128b0565b61241c6119c3916128b0565b7f2502348c000000000000000000000000000000000000000000000000000000006000526001600160a01b0316600452602490565b7f99ac52f20000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff841660045260246000fd5b3461022c57602060031936011261022c576001600160a01b036004356124ae8161021b565b6124b6613fd8565b1633811461251b57807fffffffffffffffffffffffff000000000000000000000000000000000000000060005416176000556001600160a01b03600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b7fdad89dca0000000000000000000000000000000000000000000000000000000060005260046000fd5b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c5780600401359061258282610cbb565b906125906040519283610715565b8282526024602083019360071b8201019036821161022c57602401925b8184106125bd57610d60836139d4565b8336036080811261022c576060601f19604051926125da846106dd565b87356125e58161021b565b8452011261022c576080916020916040516125ff816106a0565b8388013561260c8161021b565b8152604088013561261c81611b2e565b84820152606088013561262e81610cd3565b6040820152838201528152019301926125ad565b3461022c57604060031936011261022c5760043561265f8161021b565b612667610294565b9067ffffffffffffffff82169182600052600960205260ff60406000205416156126d4576126976126b892613b24565b92600052600960205263ffffffff60016040600020015460901c1690615075565b604080516001600160e01b039384168152919092166020820152f35b827f99ac52f20000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b90662386f26fc10000820291808304662386f26fc10000149015171561275357565b612702565b908160051b918083046020149015171561275357565b9061012c82029180830461012c149015171561275357565b90655af3107a4000820291808304655af3107a4000149015171561275357565b8181029291811591840414171561275357565b81156127c3570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b61281c612816610b6f94936001600160e01b0361280f8195613b24565b16906127a6565b92613b24565b16906127b9565b9061282d82610cbb565b61283a6040519182610715565b828152601f1961284a8294610cbb565b019060005b82811061285b57505050565b80606060208093850101520161284f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b91908110156128ab5760061b0190565b61286c565b35610b6f8161021b565b91908110156128ab5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618136030182121561022c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561022c570180359067ffffffffffffffff821161022c5760200191813603831361022c57565b92919267ffffffffffffffff82116106bc5760405191612975601f8201601f191660200184610715565b82948184528183011161022c578281602093846000960137010152565b9061023c6040516129a2816106f9565b925463ffffffff8082168552602082811c821690860152604082811c61ffff1690860152605082901c81166060860152607082901c16608085015260901c60ff16151560a0840152565b80518210156128ab5760209160051b010190565b909291612a35612a248367ffffffffffffffff166000526009602052604060002090565b5460081b6001600160e01b03191690565b90612a3f81612823565b9560005b828110612a54575050505050505090565b612a67612a6282848961289b565b6128b0565b8388612a81612a778584846128ba565b60408101906128fa565b905060208111612b9e575b508392612ac2612abc612ab5612aab600198612afd97612af8976128ba565b60208101906128fa565b369161294b565b89613b9c565b612ae08967ffffffffffffffff16600052600a602052604060002090565b906001600160a01b0316600052602052604060002090565b612992565b60a081015115612b6257612b46612b1e6060612b3893015163ffffffff1690565b6040805163ffffffff909216602083015290928391820190565b03601f198101835282610715565b612b50828b6129ec565b52612b5b818a6129ec565b5001612a43565b50612b38612b46612b9984612b8b8a67ffffffffffffffff166000526009602052604060002090565b015460101c63ffffffff1690565b612b1e565b915050612bd66118a5612bc984612ae08b67ffffffffffffffff16600052600a602052604060002090565b5460701c63ffffffff1690565b10612be357838838612a8c565b7f36f536ca000000000000000000000000000000000000000000000000000000006000526001600160a01b031660045260246000fd5b60405190612c26826106a0565b60006040838281528260208201520152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561022c570180359067ffffffffffffffff821161022c57602001918160061b3603831361022c57565b35906001600160e01b038216820361022c57565b60408136031261022c57612ccf602060405192612cbc846106dd565b8035612cc78161021b565b845201612c8c565b602082015290565b60408136031261022c57612ccf602060405192612cf3846106dd565b612cc7816102ab565b90612d05613fd8565b60005b8251811015612e555780612d1e600192856129ec565b517f32a4ba3fa3351b11ad555d4c8ec70a744e8705607077a946807030d64b6ab1a360a06001600160a01b038351169260608101936001600160a01b0380865116957fffff000000000000000000000000000000000000000000000000000000000000612dbc60208601947fffffffffffffffffffff00000000000000000000000000000000000000000000865116604088019a848c5116926159a0565b977fffffffffffffffffffff000000000000000000000000000000000000000000006080870195612e28875115158c600052600460205260406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b8560405198511688525116602087015251166040850152511660608301525115156080820152a201612d08565b509050565b60405190612e67826106dd565b60006020838281520152565b90604051612e80816106dd565b91546001600160e01b038116835260e01c6020830152565b9061023c612fe06001612ea9610747565b94612f7f612f758254612ec5612ebf8260ff1690565b15158a52565b61ffff600882901c1660208a015263ffffffff601882901c1660408a015263ffffffff603882901c1660608a015263ffffffff605882901c1660808a015260ff607882901c1660a08a015260ff608082901c1660c08a015261ffff608882901c1660e08a015263ffffffff609882901c166101008a015261ffff60b882901c166101208a015261ffff60c882901c166101408a01526001600160e01b0319600882901b166101608a015260f81c90565b1515610180880152565b015461ffff81166101a086015263ffffffff601082901c166101c086015263ffffffff603082901c166101e086015267ffffffffffffffff605082901c1661020086015263ffffffff609082901c1661022086015260b01c63ffffffff1690565b63ffffffff16610240840152565b90612ff7613fd8565b6000915b80518310156131e95761300e83826129ec565b5190613022825167ffffffffffffffff1690565b946020600093019367ffffffffffffffff8716935b855180518210156131d45761304e826020926129ec565b51015161305f6117dd8389516129ec565b8151602083015163ffffffff90811691168181101561319b575050608082015163ffffffff166020811061315a575090867f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b56001600160a01b03846130e9858f60019998612ae06130e49267ffffffffffffffff16600052600a602052604060002090565b614016565b61315160405192839216958291909160a08060c083019463ffffffff815116845263ffffffff602082015116602085015261ffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015201511515910152565b0390a301613037565b7f24ecdc02000000000000000000000000000000000000000000000000000000006000526001600160a01b0390911660045263ffffffff1660245260446000fd5b7f0b4f67a20000000000000000000000000000000000000000000000000000000060005263ffffffff9081166004521660245260446000fd5b50509550925092600191500191929092612ffb565b50905060005b81518110156132995780613217613208600193856129ec565b515167ffffffffffffffff1690565b67ffffffffffffffff6001600160a01b03613246602061323786896129ec565b5101516001600160a01b031690565b600061326a82612ae08767ffffffffffffffff16600052600a602052604060002090565b551691167f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b600080a3016131ef565b5050565b60208183031261022c5780359067ffffffffffffffff821161022c570181601f8201121561022c578035906132d182610cbb565b926132df6040519485610715565b8284526020606081860194028301019181831161022c57602001925b828410613309575050505090565b60608483031261022c576020606091604051613324816106a0565b863561332f8161021b565b815261333c838801612c8c565b8382015261334c6040880161120c565b60408201528152019301926132fb565b90604051613369816106a0565b604060ff8294546001600160a01b0381168452818160a01c16602085015260a81c161515910152565b9061339b613fd8565b60005b8251811015612e55576133b181846129ec565b5160206133c161320884876129ec565b9101519067ffffffffffffffff8116801580156135f7575b80156135c9575b80156134ea575b6134b25791613478826001959461342861341b612a2461347d9767ffffffffffffffff166000526009602052604060002090565b6001600160e01b03191690565b613483577f71e9302ab4e912a9678ae7f5a8542856706806f2817e1bf2a20b171e265cb4ad6040518061345b8782611005565b0390a267ffffffffffffffff166000526009602052604060002090565b614349565b0161339e565b7f2431cc0363f2f66b21782c7e3d54dd9085927981a21bd0cc6be45a51b19689e36040518061345b8782611005565b7fc35aa79d0000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff821660045260246000fd5b506001600160e01b031961350a6101608501516001600160e01b03191690565b167f2812d52c00000000000000000000000000000000000000000000000000000000811415908161359e575b81613573575b81613548575b506133e7565b7fc4e05953000000000000000000000000000000000000000000000000000000009150141538613542565b7fac77ffec00000000000000000000000000000000000000000000000000000000811415915061353c565b7f1e10bdc4000000000000000000000000000000000000000000000000000000008114159150613536565b506101e083015163ffffffff1663ffffffff6135ef6118a5606087015163ffffffff1690565b9116116133e0565b5063ffffffff61360f6101e085015163ffffffff1690565b16156133d9565b61361e613fd8565b60208101519160005b83518110156136ab5780613640610ade600193876129ec565b61366261365d6001600160a01b0383165b6001600160a01b031690565b615cd3565b61366e575b5001613627565b6040516001600160a01b039190911681527fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda7758090602090a138613667565b5091505160005b8151811015613299576136c8610ade82846129ec565b906001600160a01b0382161561373e577feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef6137358361371a6137156136516001976001600160a01b031690565b615c5a565b506040516001600160a01b0390911681529081906020820190565b0390a1016136b2565b7f8579befe0000000000000000000000000000000000000000000000000000000060005260046000fd5b613770613fd8565b60005b815181101561329957806001600160a01b03613791600193856129ec565b5151167fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d61382e67ffffffffffffffff60206137cd86896129ec565b51015116836000526008602052604060002067ffffffffffffffff82167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008254161790556040519182918291909167ffffffffffffffff6020820193169052565b0390a201613773565b9190820391821161275357565b61384c612e5a565b5061387261386d826001600160a01b03166000526006602052604060002090565b612e73565b602081019161389161388b6118a5855163ffffffff1690565b42613837565b63ffffffff7f00000000000000000000000000000000000000000000000000000000000000001611613939576118046138dd916001600160a01b03166000526007602052604060002090565b6138ed6118196040830151151590565b801561393f575b61393957613901906149ff565b9163ffffffff6139296118a561391e602087015163ffffffff1690565b935163ffffffff1690565b911610613934575090565b905090565b50905090565b506001600160a01b0361395982516001600160a01b031690565b16156138f4565b906002820180921161275357565b906020820180921161275357565b9190820180921161275357565b9061ffff8091169116029061ffff821691820361275357565b63ffffffff60209116019063ffffffff821161275357565b9063ffffffff8091169116019063ffffffff821161275357565b906139dd613fd8565b60005b8251811015612e5557806139f6600192856129ec565b517fe6a7a17d710bf0b2cd05e5397dc6f97a5da4ee79e31e234bf5f965ee2bd9a5bf613b1b60206001600160a01b038451169301518360005260076020526040600020613a7b6001600160a01b0383511682906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b602082015181547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000075ff0000000000000000000000000000000000000000006040870151151560a81b169360a01b169116171790556040519182918291909160408060608301946001600160a01b03815116845260ff602082015116602085015201511515910152565b0390a2016139e0565b613b2d81613844565b9063ffffffff602083015116158015613b8a575b613b535750516001600160e01b031690565b6001600160a01b03907f06439c6b000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b506001600160e01b0382511615613b41565b6001600160e01b03191691907f2812d52c000000000000000000000000000000000000000000000000000000008314613c94577f1e10bdc4000000000000000000000000000000000000000000000000000000008314613c86577fac77ffec0000000000000000000000000000000000000000000000000000000083148015613c5d575b613c5257827f2ee820750000000000000000000000000000000000000000000000000000000060005260045260246000fd5b61023c91925061549e565b507fc4e05953000000000000000000000000000000000000000000000000000000008314613c20565b61023c919250600190615501565b61023c91925061541f565b6001600160e01b03191692917f2812d52c000000000000000000000000000000000000000000000000000000008414613d76577f1e10bdc4000000000000000000000000000000000000000000000000000000008414613d5557507fac77ffec0000000000000000000000000000000000000000000000000000000083148015613c5d57613c5257827f2ee820750000000000000000000000000000000000000000000000000000000060005260045260246000fd5b91925061023c9115613d6d5760ff60015b1690615501565b60ff6000613d66565b5061023c91925061541f565b33600052600360205260406000205415613d9857565b7fd86ad9cf000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b6111fb613ded9196949395929667ffffffffffffffff166000526009602052604060002090565b946101608601946001600160e01b0319613e0f87516001600160e01b03191690565b167f2812d52c000000000000000000000000000000000000000000000000000000008114908115613fae575b8115613f84575b50613f3f5750507f1e10bdc4000000000000000000000000000000000000000000000000000000006001600160e01b0319613e8586516001600160e01b03191690565b1614613ed6576119c3613ea085516001600160e01b03191690565b7f2ee82075000000000000000000000000000000000000000000000000000000006000526001600160e01b031916600452602490565b613f2a9350612ab56060613f148763ffffffff613f0b610180613f0386613f389b9d015163ffffffff1690565b930151151590565b91168587615853565b0151604051958691602083019190602083019252565b03601f198101865285610715565b9160019190565b94509491613f6591613f5f6118a56101e0610b6f96015163ffffffff1690565b916155ec565b93613f7c6020613f74876156f5565b960151151590565b93369161294b565b7fc4e059530000000000000000000000000000000000000000000000000000000091501438613e42565b7fac77ffec0000000000000000000000000000000000000000000000000000000081149150613e3b565b6001600160a01b03600154163303613fec57565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b815181546020808501516040808701517fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000090941663ffffffff958616179190921b67ffffffff00000000161791901b69ffff000000000000000016178255606083015161023c936141269260a0926140c8911685547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff1660509190911b6dffffffff0000000000000000000016178555565b61411f6140dc608083015163ffffffff1690565b85547fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff1660709190911b71ffffffff000000000000000000000000000016178555565b0151151590565b81547fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff1690151560901b72ff00000000000000000000000000000000000016179055565b9192909261417a828286866159a0565b600052600460205260ff60406000205416156141965750505050565b6040517f097e17ff0000000000000000000000000000000000000000000000000000000081526001600160a01b0393841660048201529390921660248401527fffffffffffffffffffff0000000000000000000000000000000000000000000090911660448301527fffff000000000000000000000000000000000000000000000000000000000000166064820152608490fd5b0390fd5b604d811161275357600a0a90565b60ff1660120160ff81116127535760ff169060248211156142d7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82019182116127535761428d6142939261422e565b906127b9565b6001600160e01b0381116142ad576001600160e01b031690565b7f10cb51d10000000000000000000000000000000000000000000000000000000060005260046000fd5b906024039060248211612753576121ea6142f09261422e565b614293565b9060ff80911691160160ff81116127535760ff169060248211156142d7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82019182116127535761428d6142939261422e565b9061494b610240600161023c946143946143638651151590565b829060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6143da6143a6602087015161ffff1690565b82547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff1660089190911b62ffff0016178255565b6144266143ee604087015163ffffffff1690565b82547fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff1660189190911b66ffffffff00000016178255565b61447661443a606087015163ffffffff1690565b82547fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff1660389190911b6affffffff0000000000000016178255565b6144ca61448a608087015163ffffffff1690565b82547fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff1660589190911b6effffffff000000000000000000000016178255565b61451c6144db60a087015160ff1690565b82547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff1660789190911b6fff00000000000000000000000000000016178255565b61456f61452d60c087015160ff1690565b82547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff1660809190911b70ff0000000000000000000000000000000016178255565b6145c561458160e087015161ffff1690565b82547fffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffff1660889190911b72ffff000000000000000000000000000000000016178255565b6146226145da61010087015163ffffffff1690565b82547fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff1660989190911b76ffffffff0000000000000000000000000000000000000016178255565b61467f61463561012087015161ffff1690565b82547fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff1660b89190911b78ffff000000000000000000000000000000000000000000000016178255565b6146de61469261014087015161ffff1690565b82547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1660c89190911b7affff0000000000000000000000000000000000000000000000000016178255565b6147476146f76101608701516001600160e01b03191690565b82547fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff1660089190911c7effffffff00000000000000000000000000000000000000000000000000000016178255565b6147a8614758610180870151151590565b82547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690151560f81b7fff0000000000000000000000000000000000000000000000000000000000000016178255565b01926147ec6147bd6101a083015161ffff1690565b859061ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000825416179055565b6148386148016101c083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff1660109190911b65ffffffff000016178555565b61488861484d6101e083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffff1660309190911b69ffffffff00000000000016178555565b6148e46148a161020083015167ffffffffffffffff1690565b85547fffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffff1660509190911b71ffffffffffffffff0000000000000000000016178555565b6149406148f961022083015163ffffffff1690565b85547fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff1660909190911b75ffffffff00000000000000000000000000000000000016178555565b015163ffffffff1690565b7fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff0000000000000000000000000000000000000000000083549260b01b169116179055565b519069ffffffffffffffffffff8216820361022c57565b908160a091031261022c576149c081614995565b91602082015191604081015191610b6f608060608401519301614995565b6040513d6000823e3d90fd5b9081602091031261022c5751610b6f81611b2e565b614a07612e5a565b50614a1f61365161365183516001600160a01b031690565b90604051907ffeaf968c00000000000000000000000000000000000000000000000000000000825260a082600481865afa928315614b2757600092600094614b2c575b50600083126142ad576020600491604051928380927f313ce5670000000000000000000000000000000000000000000000000000000082525afa928315614b2757610b6f9363ffffffff93614ac893600092614af1575b506020015160ff165b906142f5565b92614ae3614ad4610738565b6001600160e01b039095168552565b1663ffffffff166020830152565b614ac2919250614b18602091823d8411614b20575b614b108183610715565b8101906149ea565b929150614ab9565b503d614b06565b6149de565b909350614b5291925060a03d60a011614b5f575b614b4a8183610715565b8101906149ac565b5093925050919238614a62565b503d614b40565b9081602091031261022c573590565b9190614b8460208301836128fa565b93905060408301614b958185612c38565b90506040840191614bad6118a5845163ffffffff1690565b8088116150435750602085015161ffff1680831161500d5750610160850196614bde88516001600160e01b03191690565b6001600160e01b031981167f2812d52c0000000000000000000000000000000000000000000000000000000081148015614fe4575b8015614fbb575b15614c9557505050505050509181614c8f612ab5614c76614c8896614c456080610b6f9801866128fa565b6101e083015163ffffffff169063ffffffff614c6e610180613f74606088015163ffffffff1690565b941692615a4c565b51958694516001600160e01b03191690565b92806128fa565b90613c9f565b7f1e10bdc400000000000000000000000000000000000000000000000000000000909a99929394969895979a14600014614f845750614d3e614d0e614d31999a614ce260808801886128fa565b63ffffffff614d06610180614cfe606087015163ffffffff1690565b950151151590565b931691615853565b91614d206118a5845163ffffffff1690565b998a91516001600160e01b03191690565b614c8f612ab588806128fa565b6080810151519082614d5b614d5387806128fa565b810190614b66565b614f67575081614f31575b85151580614f25575b614efb5760408211614ec7576020015167ffffffffffffffff9081169081831c16614e8d575050614dad90614da7859493979561276e565b9061397c565b946000935b838510614e0b5750505050506118a5614dcf915163ffffffff1690565b808211614ddb57505090565b7f869337890000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b9091929395600190614e626118a5612bc9614e3a8667ffffffffffffffff16600052600a602052604060002090565b614e4b612a628d6109278b8d612c38565b6001600160a01b0316600052602052604060002090565b8015614e7d57614e719161397c565b965b0193929190614db2565b50614e879061396e565b96614e73565b7fafa933080000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045260245260446000fd5b7f8a0d71f7000000000000000000000000000000000000000000000000000000006000526004829052604060245260446000fd5b7f5bed51920000000000000000000000000000000000000000000000000000000060005260046000fd5b50606081015115614d6f565b6119c3827f8a0d71f700000000000000000000000000000000000000000000000000000000600052906044916004526000602452565b614f7e919350614da7614f7984613960565b612758565b91614d66565b7f2ee82075000000000000000000000000000000000000000000000000000000006000526001600160e01b03191660045260246000fd5b507fc4e05953000000000000000000000000000000000000000000000000000000008114614c1a565b507fac77ffec000000000000000000000000000000000000000000000000000000008114614c13565b7fd88dddd600000000000000000000000000000000000000000000000000000000600052600483905261ffff1660245260446000fd5b7f8693378900000000000000000000000000000000000000000000000000000000600052600452602487905260446000fd5b67ffffffffffffffff811660005260056020526040600020916040519261509b846106dd565b546001600160e01b038116845260e01c9182602085015263ffffffff821692836150d5575b50505050610b6f90516001600160e01b031690565b63ffffffff1642908103939084116127535783116150f357806150c0565b7ff08bcb3e0000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045263ffffffff1660245260445260646000fd5b60408136031261022c5760206040519161514f836106dd565b803561515a8161021b565b83520135602082015290565b9694919695929390956000946000986000986000965b808810615190575050505050505050929190565b9091929394959697999a6151ad6151a88a848b61289b565b615136565b9a6151e4612af88d614e4b6151d68967ffffffffffffffff16600052600a602052604060002090565b91516001600160a01b031690565b916151f561181960a0850151151590565b61533e5760009c604084019061521061234a835161ffff1690565b6152c6575b5050606083015163ffffffff1661522b916139ba565b9c608083015161523e9063ffffffff1690565b615247916139ba565b9b82516152579063ffffffff1690565b63ffffffff1661526690612731565b600193908083106152ba57506124056118a5602061528993015163ffffffff1690565b8082116152a9575061529a9161397c565b985b019695949392919061517c565b90506152b49161397c565b9861529c565b9150506152b49161397c565b906121ea61532f939f61531d6153269460208f8e61234a95506001600160a01b036152f885516001600160a01b031690565b91166001600160a01b03821614615337576153139150613b24565b915b015190615ab4565b925161ffff1690565b620186a0900490565b9b3880615215565b5091615315565b999b506001915061535f8461535961536593614da78b612731565b9b6139ba565b9c6139a2565b9a61529c565b91939093806101e00193846101e011612753576101208102908082046101201490151715612753576101e09101018093116127535761234a610140615401610b6f966dffffffffffffffffffffffffffff6122d76153ec6153d961540b9a63ffffffff6121ea9a169061397c565b6121ea61234a6101208c015161ffff1690565b614da76118a56101008b015163ffffffff1690565b93015161ffff1690565b612786565b9081602091031261022c575190565b60208151036154555761543b6020825183010160208301615410565b6001600160a01b038111908115615492575b506154555750565b61422a906040519182917f8d666f6000000000000000000000000000000000000000000000000000000000835260206004840181815201906102f1565b6104009150103861544d565b60208151036154c457600b6154bc6020835184010160208401615410565b106154c45750565b61422a906040519182917fe0d7fb0200000000000000000000000000000000000000000000000000000000835260206004840181815201906102f1565b9060208251036155275780615514575050565b6154bc6020835184010160208401615410565b6040517fe0d7fb02000000000000000000000000000000000000000000000000000000008152602060048201528061422a60248201856102f1565b919091356001600160e01b03198116926004811061557e575050565b6001600160e01b0319929350829060040360031b1b161690565b909291928360041161022c57831161022c57600401916003190190565b9060041161022c5790600490565b9081604091031261022c576020604051916155dd836106dd565b805183520151612ccf81610cd3565b916155f5612e5a565b5081156156d3575061561e612ab582806156186001600160e01b03199587615562565b95615598565b91167f181dcf1000000000000000000000000000000000000000000000000000000000810361565b575080602080610b6f935183010191016155c3565b7f97a657c900000000000000000000000000000000000000000000000000000000146156ab577f5247fdce0000000000000000000000000000000000000000000000000000000060005260046000fd5b806020806156be93518301019101615410565b6156c6610738565b9081526000602082015290565b91505067ffffffffffffffff6156e7610738565b911681526000602082015290565b6020604051917f181dcf1000000000000000000000000000000000000000000000000000000000828401528051602484015201511515604482015260448152610b6f606482610715565b6040519061574c826106c1565b60606080836000815260006020820152600060408201526000838201520152565b60208183031261022c5780359067ffffffffffffffff821161022c57019060a08282031261022c57604051916157a2836106c1565b6157ab8161120c565b83526157b9602082016102ab565b602084015260408101356157cc81610cd3565b60408401526060810135606084015260808101359067ffffffffffffffff821161022c57019080601f8301121561022c57813561580881610cbb565b926158166040519485610715565b81845260208085019260051b82010192831161022c57602001905b82821061584357505050608082015290565b8135815260209182019101615831565b61585b61573f565b508115615976577f1f3b3aba000000000000000000000000000000000000000000000000000000006001600160e01b031961589f61589985856155b5565b90615562565b160361594c57816158bb926158b392615598565b81019061576d565b9180615936575b61590c5763ffffffff6158d9835163ffffffff1690565b16116158e25790565b7f2e2b0c290000000000000000000000000000000000000000000000000000000060005260046000fd5b7fee433e990000000000000000000000000000000000000000000000000000000060005260046000fd5b506159476118196040840151151590565b6158c2565b7f5247fdce0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fb00b53dc0000000000000000000000000000000000000000000000000000000060005260046000fd5b604080516001600160a01b039283166020820190815292909316908301527fffffffffffffffffffff0000000000000000000000000000000000000000000090921660608201527fffff000000000000000000000000000000000000000000000000000000000000909216608083015290615a1e8160a08101612b38565b51902090565b6001600160a01b03610b6f9116600b615b61565b6001600160a01b03610b6f9116600b615c95565b9063ffffffff615a6993959495615a61612e5a565b5016916155ec565b91825111615a8a5780615a7e575b61590c5790565b50602081015115615a77565b7f4c4fc93a0000000000000000000000000000000000000000000000000000000060005260046000fd5b670de0b6b3a7640000916001600160e01b03615ad092166127a6565b0490565b80548210156128ab5760005260206000200190600090565b91615b06918354906000199060031b92831b921b19161790565b9055565b80548015615b32576000190190615b218282615ad4565b60001982549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6001810191806000528260205260406000205492831515600014615c13576000198401848111612753578354936000198501948511612753576000958583615bc497615bb59503615bca575b505050615b0a565b90600052602052604060002090565b55600190565b615bfa615bf491615beb615be1615c0a9588615ad4565b90549060031b1c90565b92839187615ad4565b90615aec565b8590600052602052604060002090565b55388080615bad565b50505050600090565b805490680100000000000000008210156106bc5781615c43916001615b0694018155615ad4565b81939154906000199060031b92831b921b19161790565b600081815260036020526040902054615c8f57615c78816002615c1c565b600254906000526003602052604060002055600190565b50600090565b6000828152600182016020526040902054615ccc5780615cb783600193615c1c565b80549260005201602052604060002055600190565b5050600090565b600081815260036020526040902054908115615ccc5760001982019082821161275357600254926000198401938411612753578383615bc49460009603615d33575b505050615d226002615b0a565b600390600052602052604060002090565b615d22615bf491615d4b615be1615d55956002615ad4565b9283916002615ad4565b55388080615d1556fea164736f6c634300081a000a00000000000000000000000000000000000000000000000ad78ebc5ac6200000000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b600000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b537b61351c91154bf361a1589bc0480f89d616e000000000000000000000000062f05cd6c835677b05a8658a3519694768613160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ee7d8bcfb72bc1880d0cf19822eb0a2e6577ab62000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b60000000000000000000000000000000000000000000000000c7d713b49da0000000000000000000000000000ee7d8bcfb72bc1880d0cf19822eb0a2e6577ab620000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c806241e5be1461021657806301447eaa1461021157806301ffc9a71461020c578063061877e31461020757806306285c6914610202578063181f5a77146101fd5780632451a627146101f8578063325c868e146101f35780633937306f146101ee5780633a49bb49146101e957806341ed29e7146101e457806345ac924d146101df5780634ab35b0b146101da578063514e8cff146101d55780636def4ce7146101d0578063770e2dc4146101cb57806379ba5097146101c65780637afac322146101c1578063805f2132146101bc57806382b49eb0146101b757806387b8d879146101b25780638da5cb5b146101ad57806391a2749a146101a8578063a69c64c0146101a3578063bf78e03f1461019e578063cdc73d5114610199578063d02641a014610194578063d63d3af21461018f578063d8694ccd1461018a578063f2fde38b14610185578063fbe3f778146101805763ffdb4b371461017b57600080fd5b612642565b612545565b612489565b612056565b61203a565b611ff1565b611f7a565b611ed4565b611e1b565b611d87565b611d60565b611b44565b6119c7565b61172c565b6115f3565b6114db565b6112bc565b61113d565b610f7e565b610f46565b610e7d565b610ce8565b610b72565b610898565b61087c565b6107f9565b610757565b6105a0565b610558565b610464565b6103b0565b61023e565b6001600160a01b0381160361022c57565b600080fd5b359061023c8261021b565b565b3461022c57606060031936011261022c5760206102756004356102608161021b565b602435604435916102708361021b565b6127f2565b604051908152f35b6004359067ffffffffffffffff8216820361022c57565b6024359067ffffffffffffffff8216820361022c57565b359067ffffffffffffffff8216820361022c57565b9181601f8401121561022c5782359167ffffffffffffffff831161022c576020808501948460051b01011161022c57565b919082519283825260005b84811061031d575050601f19601f8460006020809697860101520116010190565b806020809284010151828286010152016102fc565b602081016020825282518091526040820191602060408360051b8301019401926000915b83831061036557505050505090565b90919293946020806103a1837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0866001960301875289516102f1565b97019301930191939290610356565b3461022c57606060031936011261022c576103c961027d565b60243567ffffffffffffffff811161022c576103e99036906004016102c0565b6044929192359167ffffffffffffffff831161022c573660238401121561022c5782600401359167ffffffffffffffff831161022c573660248460061b8601011161022c5761044b94602461043f950192612a00565b60405191829182610332565b0390f35b35906001600160e01b03198216820361022c57565b3461022c57602060031936011261022c576004356001600160e01b03198116810361022c576001600160e01b0319602091167f805f213200000000000000000000000000000000000000000000000000000000811490811561052e575b8115610504575b81156104da575b506040519015158152f35b7f01ffc9a700000000000000000000000000000000000000000000000000000000915014386104cf565b7f181f5a7700000000000000000000000000000000000000000000000000000000811491506104c8565b7fe364892e00000000000000000000000000000000000000000000000000000000811491506104c1565b3461022c57602060031936011261022c576001600160a01b0360043561057d8161021b565b166000526008602052602067ffffffffffffffff60406000205416604051908152f35b3461022c57600060031936011261022c576105b9612c19565b5060606040516105c8816106a0565b63ffffffff6bffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000ad78ebc5ac620000016918281526001600160a01b0360406020830192827f000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b61684520191837f00000000000000000000000000000000000000000000000000000000000151801683526040519485525116602084015251166040820152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176106bc57604052565b610671565b60a0810190811067ffffffffffffffff8211176106bc57604052565b6040810190811067ffffffffffffffff8211176106bc57604052565b60c0810190811067ffffffffffffffff8211176106bc57604052565b90601f601f19910116810190811067ffffffffffffffff8211176106bc57604052565b6040519061023c604083610715565b6040519061023c61026083610715565b3461022c57600060031936011261022c5761044b604080519061077a8183610715565b600f82527f46656551756f74657220312e362e3000000000000000000000000000000000006020830152519182916020835260208301906102f1565b602060408183019282815284518094520192019060005b8181106107da5750505090565b82516001600160a01b03168452602093840193909201916001016107cd565b3461022c57600060031936011261022c5760405180602060025491828152019060026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9060005b8181106108665761044b8561085a81870382610715565b604051918291826107b6565b8254845260209093019260019283019201610843565b3461022c57600060031936011261022c57602060405160248152f35b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c5780600401906040600319823603011261022c576108d6613d82565b6108e08280612c38565b4263ffffffff1692915060005b818110610a39575050602401906109048284612c38565b92905060005b83811061091357005b8061093261092d600193610927868a612c38565b9061289b565b612cd7565b7fdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e67ffffffffffffffff610a15610a0760208501946109f961097b87516001600160e01b031690565b610995610986610738565b6001600160e01b039092168252565b63ffffffff8c1660208201526109d06109b6845167ffffffffffffffff1690565b67ffffffffffffffff166000526005602052604060002090565b815160209092015160e01b6001600160e01b0319166001600160e01b0392909216919091179055565b5167ffffffffffffffff1690565b93516001600160e01b031690565b604080516001600160e01b039290921682524260208301529190931692a20161090a565b80610a52610a4d6001936109278980612c38565b612ca0565b7f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a6001600160a01b03610aeb610a076020850194610ade610a9a87516001600160e01b031690565b610aa5610986610738565b63ffffffff8d1660208201526109d0610ac584516001600160a01b031690565b6001600160a01b03166000526006602052604060002090565b516001600160a01b031690565b604080516001600160e01b039290921682524260208301529190931692a2016108ed565b9181601f8401121561022c5782359167ffffffffffffffff831161022c576020838186019501011161022c57565b92610b6f9492610b61928552151560208501526080604085015260808401906102f1565b9160608184039101526102f1565b90565b3461022c5760a060031936011261022c57610b8b61027d565b60243590610b988261021b565b6044359160643567ffffffffffffffff811161022c57610bbc903690600401610b0f565b93909160843567ffffffffffffffff811161022c57610bdf903690600401610b0f565b9290917f000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b6906001600160a01b0382166001600160a01b03821614600014610cab575050935b6bffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000ad78ebc5ac620000016808611610c7a575091610c6b939161044b9693613dc6565b90939160405194859485610b3d565b857f6a92a4830000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b91610cb5926127f2565b93610c24565b67ffffffffffffffff81116106bc5760051b60200190565b8015150361022c57565b359061023c82610cd3565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c57806004013590610d2582610cbb565b90610d336040519283610715565b828252602460a06020840194028201019036821161022c57602401925b818410610d6257610d6083612cfc565b005b60a08436031261022c5760405190610d79826106c1565b8435610d848161021b565b825260208501357fffffffffffffffffffff000000000000000000000000000000000000000000008116810361022c5760208301526040850135907fffff0000000000000000000000000000000000000000000000000000000000008216820361022c5782602092604060a0950152610dff60608801610231565b6060820152610e1060808801610cdd565b6080820152815201930192610d50565b602060408183019282815284518094520192019060005b818110610e445750505090565b9091926020604082610e72600194885163ffffffff602080926001600160e01b038151168552015116910152565b019401929101610e37565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c57610eae9036906004016102c0565b610eb781610cbb565b91610ec56040519384610715565b818352601f19610ed483610cbb565b0160005b818110610f2f57505060005b82811015610f2157600190610f05610f008260051b85016128b0565b613844565b610f0f82876129ec565b52610f1a81866129ec565b5001610ee4565b6040518061044b8682610e20565b602090610f3a612e5a565b82828801015201610ed8565b3461022c57602060031936011261022c576020610f6d600435610f688161021b565b613b24565b6001600160e01b0360405191168152f35b3461022c57602060031936011261022c5767ffffffffffffffff610fa061027d565b610fa8612e5a565b50166000526005602052604060002060405190610fc4826106dd565b546001600160e01b038116825260e01c6020820152604051809161044b82604081019263ffffffff602080926001600160e01b038151168552015116910152565b61023c909291926102408061026083019561102284825115159052565b60208181015161ffff169085015260408181015163ffffffff169085015260608181015163ffffffff169085015260808181015163ffffffff169085015260a08181015160ff169085015260c08181015160ff169085015260e08181015161ffff16908501526101008181015163ffffffff16908501526101208181015161ffff16908501526101408181015161ffff1690850152610160818101516001600160e01b03191690850152610180818101511515908501526101a08181015161ffff16908501526101c08181015163ffffffff16908501526101e08181015163ffffffff16908501526102008181015167ffffffffffffffff16908501526102208181015163ffffffff1690850152015163ffffffff16910152565b3461022c57602060031936011261022c5761044b6112006111fb61115f61027d565b600061024061116c610747565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e08201528261020082015282610220820152015267ffffffffffffffff166000526009602052604060002090565b612e98565b60405191829182611005565b359063ffffffff8216820361022c57565b359061ffff8216820361022c57565b81601f8201121561022c5780359061124382610cbb565b926112516040519485610715565b82845260208085019360061b8301019181831161022c57602001925b82841061127b575050505090565b60408483031261022c5760206040918251611295816106dd565b61129e876102ab565b8152828701356112ad8161021b565b8382015281520193019261126d565b3461022c57604060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c5780600401356112f881610cbb565b916113066040519384610715565b8183526024602084019260051b8201019036821161022c5760248101925b828410611355576024358567ffffffffffffffff821161022c5761134f610d6092369060040161122c565b90612fee565b833567ffffffffffffffff811161022c57820160407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc823603011261022c57604051906113a1826106dd565b6113ad602482016102ab565b8252604481013567ffffffffffffffff811161022c57602491010136601f8201121561022c5780356113de81610cbb565b916113ec6040519384610715565b818352602060e081850193028201019036821161022c57602001915b8183106114275750505091816020938480940152815201930192611324565b82360360e0811261022c5760c0601f1960405192611444846106dd565b863561144f8161021b565b8452011261022c5760e091602091604051611469816106f9565b61147484880161120c565b81526114826040880161120c565b848201526114926060880161121d565b60408201526114a36080880161120c565b60608201526114b460a0880161120c565b608082015260c08701356114c781610cd3565b60a082015283820152815201920191611408565b3461022c57600060031936011261022c576000546001600160a01b0381163303611562577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154913382841617600155166000556001600160a01b033391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f02b543c60000000000000000000000000000000000000000000000000000000060005260046000fd5b9080601f8301121561022c5781356115a381610cbb565b926115b16040519485610715565b81845260208085019260051b82010192831161022c57602001905b8282106115d95750505090565b6020809183356115e88161021b565b8152019101906115cc565b3461022c57604060031936011261022c5760043567ffffffffffffffff811161022c5761162490369060040161158c565b60243567ffffffffffffffff811161022c5761164490369060040161158c565b9061164d613fd8565b60005b81518110156116bc578061167161166c610ade600194866129ec565b615a24565b61167c575b01611650565b6001600160a01b03611691610ade83866129ec565b167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f91600080a2611676565b8260005b8151811015610d6057806116e16116dc610ade600194866129ec565b615a38565b6116ec575b016116c0565b6001600160a01b03611701610ade83866129ec565b167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba23600080a26116e6565b3461022c57604060031936011261022c5760043567ffffffffffffffff811161022c5761175d903690600401610b0f565b6024359167ffffffffffffffff831161022c576117b66117ae61179461178a6117be963690600401610b0f565b949095369161294b565b90604082015190605e604a84015160601c93015191929190565b91903361416a565b81019061329d565b60005b8151811015610d60576118096118046117eb6117dd84866129ec565b51516001600160a01b031690565b6001600160a01b03166000526007602052604060002090565b61335c565b61181d6118196040830151151590565b1590565b61197e57906118686118356020600194015160ff1690565b611862611856602061184786896129ec565b5101516001600160e01b031690565b6001600160e01b031690565b9061423c565b611883604061187784876129ec565b51015163ffffffff1690565b63ffffffff6118ae6118a561189e610ac56117dd888b6129ec565b5460e01c90565b63ffffffff1690565b911610611978576118fc6118c7604061187785886129ec565b6118ec6118d2610738565b6001600160e01b03851681529163ffffffff166020830152565b6109d0610ac56117dd86896129ec565b7f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a6001600160a01b036119326117dd85886129ec565b61196e6119446040611877888b6129ec565b60405193849316958390929163ffffffff6020916001600160e01b03604085019616845216910152565b0390a25b016117c1565b50611972565b6119c361198e6117dd84866129ec565b7f06439c6b000000000000000000000000000000000000000000000000000000006000526001600160a01b0316600452602490565b6000fd5b3461022c57604060031936011261022c5761044b611a4f6119e661027d565b67ffffffffffffffff602435916119fc8361021b565b600060a0604051611a0c816106f9565b828152826020820152826040820152826060820152826080820152015216600052600a6020526040600020906001600160a01b0316600052602052604060002090565b611acb611ac260405192611a62846106f9565b5463ffffffff8116845263ffffffff8160201c16602085015261ffff8160401c166040850152611aa9611a9c8263ffffffff9060501c1690565b63ffffffff166060860152565b63ffffffff607082901c16608085015260901c60ff1690565b151560a0830152565b6040519182918291909160a08060c083019463ffffffff815116845263ffffffff602082015116602085015261ffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015201511515910152565b60ff81160361022c57565b359061023c82611b2e565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c57806004013590611b8182610cbb565b90611b8f6040519283610715565b82825260246102806020840194028201019036821161022c57602401925b818410611bbd57610d6083613392565b833603610280811261022c57610260601f1960405192611bdc846106dd565b611be5886102ab565b8452011261022c5761028091602091611bfc610747565b611c07848901610cdd565b8152611c156040890161121d565b84820152611c256060890161120c565b6040820152611c366080890161120c565b6060820152611c4760a0890161120c565b6080820152611c5860c08901611b39565b60a0820152611c6960e08901611b39565b60c0820152611c7b610100890161121d565b60e0820152611c8d610120890161120c565b610100820152611ca0610140890161121d565b610120820152611cb3610160890161121d565b610140820152611cc6610180890161044f565b610160820152611cd96101a08901610cdd565b610180820152611cec6101c0890161121d565b6101a0820152611cff6101e0890161120c565b6101c0820152611d12610200890161120c565b6101e0820152611d2561022089016102ab565b610200820152611d38610240890161120c565b610220820152611d4b610260890161120c565b61024082015283820152815201930192611bad565b3461022c57600060031936011261022c5760206001600160a01b0360015416604051908152f35b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c576040600319823603011261022c57604051611dc4816106dd565b816004013567ffffffffffffffff811161022c57611de8906004369185010161158c565b8152602482013567ffffffffffffffff811161022c57610d60926004611e11923692010161158c565b6020820152613616565b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c57806004013590611e5882610cbb565b90611e666040519283610715565b8282526024602083019360061b8201019036821161022c57602401925b818410611e9357610d6083613768565b60408436031261022c5760206040918251611ead816106dd565b8635611eb88161021b565b8152611ec58388016102ab565b83820152815201930192611e83565b3461022c57602060031936011261022c576001600160a01b03600435611ef98161021b565b611f01612c19565b5016600052600760205261044b604060002060ff60405191611f22836106a0565b546001600160a01b0381168352818160a01c16602084015260a81c16151560408201526040519182918291909160408060608301946001600160a01b03815116845260ff602082015116602085015201511515910152565b3461022c57600060031936011261022c57604051806020600b54918281520190600b6000527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db99060005b818110611fdb5761044b8561085a81870382610715565b8254845260209093019260019283019201611fc4565b3461022c57602060031936011261022c576040612013600435610f008161021b565b6120388251809263ffffffff602080926001600160e01b038151168552015116910152565bf35b3461022c57600060031936011261022c57602060405160128152f35b3461022c57604060031936011261022c5761206f61027d565b60243567ffffffffffffffff811161022c578060040160a0600319833603011261022c576120b46111fb8467ffffffffffffffff166000526009602052604060002090565b916120c26118198451151590565b61245157606481016120f86118196120d9836128b0565b6001600160a01b03166000526001600b01602052604060002054151590565b6124105783906044830161210c8186612c38565b95905061211a81858a614b75565b92612127610f68826128b0565b97889361214561213f61022084015163ffffffff1690565b8c615075565b9a6000808b156123d75750506121dd6121aa61ffff856121cf996121b69998966121ea9661218e6101c06121826101a06121f09d015161ffff1690565b95015163ffffffff1690565b6121a161219a8b6128b0565b938d612c38565b96909516615166565b989198979098946128b0565b6001600160a01b03166000526008602052604060002090565b5467ffffffffffffffff1690565b67ffffffffffffffff1690565b906127a6565b9560009761ffff61220761014089015161ffff1690565b16612379575b50946121ea6121dd6102006122df61044b9d6dffffffffffffffffffffffffffff6122d76122f79f9e9b6122d26001600160e01b039f9b9c6122ef9f6122d29e63ffffffff6122666122d29f60246122709501906128fa565b929050169061397c565b908b60a0810161229361228d612287835160ff1690565b60ff1690565b856127a6565b9360e08301916122a5835161ffff1690565b9061ffff82168311612307575b50505050608001516122d2916118a59163ffffffff166139ba565b6139ba565b61397c565b9116906127a6565b93015167ffffffffffffffff1690565b9116906127b9565b6040519081529081906020820190565b6118a59496506122d2959361ffff6123686123576122cd9661235161234a61234160809960ff61233b61236f9b5160ff1690565b16613989565b965161ffff1690565b61ffff1690565b90613837565b6121ea61228760c08d015160ff1690565b911661397c565b95938395506122b2565b9095949897508261239f8b989495986dffffffffffffffffffffffffffff9060701c1690565b6dffffffffffffffffffffffffffff16916123bd60248901856128fa565b90506123c9938861536b565b96979394389693929661220d565b959492509550506121ea6121dd6121cf6121b661240a6124056118a56102406121f099015163ffffffff1690565b612731565b946128b0565b61241c6119c3916128b0565b7f2502348c000000000000000000000000000000000000000000000000000000006000526001600160a01b0316600452602490565b7f99ac52f20000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff841660045260246000fd5b3461022c57602060031936011261022c576001600160a01b036004356124ae8161021b565b6124b6613fd8565b1633811461251b57807fffffffffffffffffffffffff000000000000000000000000000000000000000060005416176000556001600160a01b03600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b7fdad89dca0000000000000000000000000000000000000000000000000000000060005260046000fd5b3461022c57602060031936011261022c5760043567ffffffffffffffff811161022c573660238201121561022c5780600401359061258282610cbb565b906125906040519283610715565b8282526024602083019360071b8201019036821161022c57602401925b8184106125bd57610d60836139d4565b8336036080811261022c576060601f19604051926125da846106dd565b87356125e58161021b565b8452011261022c576080916020916040516125ff816106a0565b8388013561260c8161021b565b8152604088013561261c81611b2e565b84820152606088013561262e81610cd3565b6040820152838201528152019301926125ad565b3461022c57604060031936011261022c5760043561265f8161021b565b612667610294565b9067ffffffffffffffff82169182600052600960205260ff60406000205416156126d4576126976126b892613b24565b92600052600960205263ffffffff60016040600020015460901c1690615075565b604080516001600160e01b039384168152919092166020820152f35b827f99ac52f20000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b90662386f26fc10000820291808304662386f26fc10000149015171561275357565b612702565b908160051b918083046020149015171561275357565b9061012c82029180830461012c149015171561275357565b90655af3107a4000820291808304655af3107a4000149015171561275357565b8181029291811591840414171561275357565b81156127c3570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b61281c612816610b6f94936001600160e01b0361280f8195613b24565b16906127a6565b92613b24565b16906127b9565b9061282d82610cbb565b61283a6040519182610715565b828152601f1961284a8294610cbb565b019060005b82811061285b57505050565b80606060208093850101520161284f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b91908110156128ab5760061b0190565b61286c565b35610b6f8161021b565b91908110156128ab5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618136030182121561022c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561022c570180359067ffffffffffffffff821161022c5760200191813603831361022c57565b92919267ffffffffffffffff82116106bc5760405191612975601f8201601f191660200184610715565b82948184528183011161022c578281602093846000960137010152565b9061023c6040516129a2816106f9565b925463ffffffff8082168552602082811c821690860152604082811c61ffff1690860152605082901c81166060860152607082901c16608085015260901c60ff16151560a0840152565b80518210156128ab5760209160051b010190565b909291612a35612a248367ffffffffffffffff166000526009602052604060002090565b5460081b6001600160e01b03191690565b90612a3f81612823565b9560005b828110612a54575050505050505090565b612a67612a6282848961289b565b6128b0565b8388612a81612a778584846128ba565b60408101906128fa565b905060208111612b9e575b508392612ac2612abc612ab5612aab600198612afd97612af8976128ba565b60208101906128fa565b369161294b565b89613b9c565b612ae08967ffffffffffffffff16600052600a602052604060002090565b906001600160a01b0316600052602052604060002090565b612992565b60a081015115612b6257612b46612b1e6060612b3893015163ffffffff1690565b6040805163ffffffff909216602083015290928391820190565b03601f198101835282610715565b612b50828b6129ec565b52612b5b818a6129ec565b5001612a43565b50612b38612b46612b9984612b8b8a67ffffffffffffffff166000526009602052604060002090565b015460101c63ffffffff1690565b612b1e565b915050612bd66118a5612bc984612ae08b67ffffffffffffffff16600052600a602052604060002090565b5460701c63ffffffff1690565b10612be357838838612a8c565b7f36f536ca000000000000000000000000000000000000000000000000000000006000526001600160a01b031660045260246000fd5b60405190612c26826106a0565b60006040838281528260208201520152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561022c570180359067ffffffffffffffff821161022c57602001918160061b3603831361022c57565b35906001600160e01b038216820361022c57565b60408136031261022c57612ccf602060405192612cbc846106dd565b8035612cc78161021b565b845201612c8c565b602082015290565b60408136031261022c57612ccf602060405192612cf3846106dd565b612cc7816102ab565b90612d05613fd8565b60005b8251811015612e555780612d1e600192856129ec565b517f32a4ba3fa3351b11ad555d4c8ec70a744e8705607077a946807030d64b6ab1a360a06001600160a01b038351169260608101936001600160a01b0380865116957fffff000000000000000000000000000000000000000000000000000000000000612dbc60208601947fffffffffffffffffffff00000000000000000000000000000000000000000000865116604088019a848c5116926159a0565b977fffffffffffffffffffff000000000000000000000000000000000000000000006080870195612e28875115158c600052600460205260406000209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b8560405198511688525116602087015251166040850152511660608301525115156080820152a201612d08565b509050565b60405190612e67826106dd565b60006020838281520152565b90604051612e80816106dd565b91546001600160e01b038116835260e01c6020830152565b9061023c612fe06001612ea9610747565b94612f7f612f758254612ec5612ebf8260ff1690565b15158a52565b61ffff600882901c1660208a015263ffffffff601882901c1660408a015263ffffffff603882901c1660608a015263ffffffff605882901c1660808a015260ff607882901c1660a08a015260ff608082901c1660c08a015261ffff608882901c1660e08a015263ffffffff609882901c166101008a015261ffff60b882901c166101208a015261ffff60c882901c166101408a01526001600160e01b0319600882901b166101608a015260f81c90565b1515610180880152565b015461ffff81166101a086015263ffffffff601082901c166101c086015263ffffffff603082901c166101e086015267ffffffffffffffff605082901c1661020086015263ffffffff609082901c1661022086015260b01c63ffffffff1690565b63ffffffff16610240840152565b90612ff7613fd8565b6000915b80518310156131e95761300e83826129ec565b5190613022825167ffffffffffffffff1690565b946020600093019367ffffffffffffffff8716935b855180518210156131d45761304e826020926129ec565b51015161305f6117dd8389516129ec565b8151602083015163ffffffff90811691168181101561319b575050608082015163ffffffff166020811061315a575090867f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b56001600160a01b03846130e9858f60019998612ae06130e49267ffffffffffffffff16600052600a602052604060002090565b614016565b61315160405192839216958291909160a08060c083019463ffffffff815116845263ffffffff602082015116602085015261ffff604082015116604085015263ffffffff606082015116606085015263ffffffff608082015116608085015201511515910152565b0390a301613037565b7f24ecdc02000000000000000000000000000000000000000000000000000000006000526001600160a01b0390911660045263ffffffff1660245260446000fd5b7f0b4f67a20000000000000000000000000000000000000000000000000000000060005263ffffffff9081166004521660245260446000fd5b50509550925092600191500191929092612ffb565b50905060005b81518110156132995780613217613208600193856129ec565b515167ffffffffffffffff1690565b67ffffffffffffffff6001600160a01b03613246602061323786896129ec565b5101516001600160a01b031690565b600061326a82612ae08767ffffffffffffffff16600052600a602052604060002090565b551691167f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b600080a3016131ef565b5050565b60208183031261022c5780359067ffffffffffffffff821161022c570181601f8201121561022c578035906132d182610cbb565b926132df6040519485610715565b8284526020606081860194028301019181831161022c57602001925b828410613309575050505090565b60608483031261022c576020606091604051613324816106a0565b863561332f8161021b565b815261333c838801612c8c565b8382015261334c6040880161120c565b60408201528152019301926132fb565b90604051613369816106a0565b604060ff8294546001600160a01b0381168452818160a01c16602085015260a81c161515910152565b9061339b613fd8565b60005b8251811015612e55576133b181846129ec565b5160206133c161320884876129ec565b9101519067ffffffffffffffff8116801580156135f7575b80156135c9575b80156134ea575b6134b25791613478826001959461342861341b612a2461347d9767ffffffffffffffff166000526009602052604060002090565b6001600160e01b03191690565b613483577f71e9302ab4e912a9678ae7f5a8542856706806f2817e1bf2a20b171e265cb4ad6040518061345b8782611005565b0390a267ffffffffffffffff166000526009602052604060002090565b614349565b0161339e565b7f2431cc0363f2f66b21782c7e3d54dd9085927981a21bd0cc6be45a51b19689e36040518061345b8782611005565b7fc35aa79d0000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff821660045260246000fd5b506001600160e01b031961350a6101608501516001600160e01b03191690565b167f2812d52c00000000000000000000000000000000000000000000000000000000811415908161359e575b81613573575b81613548575b506133e7565b7fc4e05953000000000000000000000000000000000000000000000000000000009150141538613542565b7fac77ffec00000000000000000000000000000000000000000000000000000000811415915061353c565b7f1e10bdc4000000000000000000000000000000000000000000000000000000008114159150613536565b506101e083015163ffffffff1663ffffffff6135ef6118a5606087015163ffffffff1690565b9116116133e0565b5063ffffffff61360f6101e085015163ffffffff1690565b16156133d9565b61361e613fd8565b60208101519160005b83518110156136ab5780613640610ade600193876129ec565b61366261365d6001600160a01b0383165b6001600160a01b031690565b615cd3565b61366e575b5001613627565b6040516001600160a01b039190911681527fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda7758090602090a138613667565b5091505160005b8151811015613299576136c8610ade82846129ec565b906001600160a01b0382161561373e577feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef6137358361371a6137156136516001976001600160a01b031690565b615c5a565b506040516001600160a01b0390911681529081906020820190565b0390a1016136b2565b7f8579befe0000000000000000000000000000000000000000000000000000000060005260046000fd5b613770613fd8565b60005b815181101561329957806001600160a01b03613791600193856129ec565b5151167fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d61382e67ffffffffffffffff60206137cd86896129ec565b51015116836000526008602052604060002067ffffffffffffffff82167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008254161790556040519182918291909167ffffffffffffffff6020820193169052565b0390a201613773565b9190820391821161275357565b61384c612e5a565b5061387261386d826001600160a01b03166000526006602052604060002090565b612e73565b602081019161389161388b6118a5855163ffffffff1690565b42613837565b63ffffffff7f00000000000000000000000000000000000000000000000000000000000151801611613939576118046138dd916001600160a01b03166000526007602052604060002090565b6138ed6118196040830151151590565b801561393f575b61393957613901906149ff565b9163ffffffff6139296118a561391e602087015163ffffffff1690565b935163ffffffff1690565b911610613934575090565b905090565b50905090565b506001600160a01b0361395982516001600160a01b031690565b16156138f4565b906002820180921161275357565b906020820180921161275357565b9190820180921161275357565b9061ffff8091169116029061ffff821691820361275357565b63ffffffff60209116019063ffffffff821161275357565b9063ffffffff8091169116019063ffffffff821161275357565b906139dd613fd8565b60005b8251811015612e5557806139f6600192856129ec565b517fe6a7a17d710bf0b2cd05e5397dc6f97a5da4ee79e31e234bf5f965ee2bd9a5bf613b1b60206001600160a01b038451169301518360005260076020526040600020613a7b6001600160a01b0383511682906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b602082015181547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000075ff0000000000000000000000000000000000000000006040870151151560a81b169360a01b169116171790556040519182918291909160408060608301946001600160a01b03815116845260ff602082015116602085015201511515910152565b0390a2016139e0565b613b2d81613844565b9063ffffffff602083015116158015613b8a575b613b535750516001600160e01b031690565b6001600160a01b03907f06439c6b000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b506001600160e01b0382511615613b41565b6001600160e01b03191691907f2812d52c000000000000000000000000000000000000000000000000000000008314613c94577f1e10bdc4000000000000000000000000000000000000000000000000000000008314613c86577fac77ffec0000000000000000000000000000000000000000000000000000000083148015613c5d575b613c5257827f2ee820750000000000000000000000000000000000000000000000000000000060005260045260246000fd5b61023c91925061549e565b507fc4e05953000000000000000000000000000000000000000000000000000000008314613c20565b61023c919250600190615501565b61023c91925061541f565b6001600160e01b03191692917f2812d52c000000000000000000000000000000000000000000000000000000008414613d76577f1e10bdc4000000000000000000000000000000000000000000000000000000008414613d5557507fac77ffec0000000000000000000000000000000000000000000000000000000083148015613c5d57613c5257827f2ee820750000000000000000000000000000000000000000000000000000000060005260045260246000fd5b91925061023c9115613d6d5760ff60015b1690615501565b60ff6000613d66565b5061023c91925061541f565b33600052600360205260406000205415613d9857565b7fd86ad9cf000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b6111fb613ded9196949395929667ffffffffffffffff166000526009602052604060002090565b946101608601946001600160e01b0319613e0f87516001600160e01b03191690565b167f2812d52c000000000000000000000000000000000000000000000000000000008114908115613fae575b8115613f84575b50613f3f5750507f1e10bdc4000000000000000000000000000000000000000000000000000000006001600160e01b0319613e8586516001600160e01b03191690565b1614613ed6576119c3613ea085516001600160e01b03191690565b7f2ee82075000000000000000000000000000000000000000000000000000000006000526001600160e01b031916600452602490565b613f2a9350612ab56060613f148763ffffffff613f0b610180613f0386613f389b9d015163ffffffff1690565b930151151590565b91168587615853565b0151604051958691602083019190602083019252565b03601f198101865285610715565b9160019190565b94509491613f6591613f5f6118a56101e0610b6f96015163ffffffff1690565b916155ec565b93613f7c6020613f74876156f5565b960151151590565b93369161294b565b7fc4e059530000000000000000000000000000000000000000000000000000000091501438613e42565b7fac77ffec0000000000000000000000000000000000000000000000000000000081149150613e3b565b6001600160a01b03600154163303613fec57565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b815181546020808501516040808701517fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000090941663ffffffff958616179190921b67ffffffff00000000161791901b69ffff000000000000000016178255606083015161023c936141269260a0926140c8911685547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff1660509190911b6dffffffff0000000000000000000016178555565b61411f6140dc608083015163ffffffff1690565b85547fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff1660709190911b71ffffffff000000000000000000000000000016178555565b0151151590565b81547fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff1690151560901b72ff00000000000000000000000000000000000016179055565b9192909261417a828286866159a0565b600052600460205260ff60406000205416156141965750505050565b6040517f097e17ff0000000000000000000000000000000000000000000000000000000081526001600160a01b0393841660048201529390921660248401527fffffffffffffffffffff0000000000000000000000000000000000000000000090911660448301527fffff000000000000000000000000000000000000000000000000000000000000166064820152608490fd5b0390fd5b604d811161275357600a0a90565b60ff1660120160ff81116127535760ff169060248211156142d7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82019182116127535761428d6142939261422e565b906127b9565b6001600160e01b0381116142ad576001600160e01b031690565b7f10cb51d10000000000000000000000000000000000000000000000000000000060005260046000fd5b906024039060248211612753576121ea6142f09261422e565b614293565b9060ff80911691160160ff81116127535760ff169060248211156142d7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82019182116127535761428d6142939261422e565b9061494b610240600161023c946143946143638651151590565b829060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6143da6143a6602087015161ffff1690565b82547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff1660089190911b62ffff0016178255565b6144266143ee604087015163ffffffff1690565b82547fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff1660189190911b66ffffffff00000016178255565b61447661443a606087015163ffffffff1690565b82547fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff1660389190911b6affffffff0000000000000016178255565b6144ca61448a608087015163ffffffff1690565b82547fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff1660589190911b6effffffff000000000000000000000016178255565b61451c6144db60a087015160ff1690565b82547fffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffff1660789190911b6fff00000000000000000000000000000016178255565b61456f61452d60c087015160ff1690565b82547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff1660809190911b70ff0000000000000000000000000000000016178255565b6145c561458160e087015161ffff1690565b82547fffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffff1660889190911b72ffff000000000000000000000000000000000016178255565b6146226145da61010087015163ffffffff1690565b82547fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff1660989190911b76ffffffff0000000000000000000000000000000000000016178255565b61467f61463561012087015161ffff1690565b82547fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff1660b89190911b78ffff000000000000000000000000000000000000000000000016178255565b6146de61469261014087015161ffff1690565b82547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1660c89190911b7affff0000000000000000000000000000000000000000000000000016178255565b6147476146f76101608701516001600160e01b03191690565b82547fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff1660089190911c7effffffff00000000000000000000000000000000000000000000000000000016178255565b6147a8614758610180870151151590565b82547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690151560f81b7fff0000000000000000000000000000000000000000000000000000000000000016178255565b01926147ec6147bd6101a083015161ffff1690565b859061ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000825416179055565b6148386148016101c083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff1660109190911b65ffffffff000016178555565b61488861484d6101e083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffff1660309190911b69ffffffff00000000000016178555565b6148e46148a161020083015167ffffffffffffffff1690565b85547fffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffff1660509190911b71ffffffffffffffff0000000000000000000016178555565b6149406148f961022083015163ffffffff1690565b85547fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff1660909190911b75ffffffff00000000000000000000000000000000000016178555565b015163ffffffff1690565b7fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff0000000000000000000000000000000000000000000083549260b01b169116179055565b519069ffffffffffffffffffff8216820361022c57565b908160a091031261022c576149c081614995565b91602082015191604081015191610b6f608060608401519301614995565b6040513d6000823e3d90fd5b9081602091031261022c5751610b6f81611b2e565b614a07612e5a565b50614a1f61365161365183516001600160a01b031690565b90604051907ffeaf968c00000000000000000000000000000000000000000000000000000000825260a082600481865afa928315614b2757600092600094614b2c575b50600083126142ad576020600491604051928380927f313ce5670000000000000000000000000000000000000000000000000000000082525afa928315614b2757610b6f9363ffffffff93614ac893600092614af1575b506020015160ff165b906142f5565b92614ae3614ad4610738565b6001600160e01b039095168552565b1663ffffffff166020830152565b614ac2919250614b18602091823d8411614b20575b614b108183610715565b8101906149ea565b929150614ab9565b503d614b06565b6149de565b909350614b5291925060a03d60a011614b5f575b614b4a8183610715565b8101906149ac565b5093925050919238614a62565b503d614b40565b9081602091031261022c573590565b9190614b8460208301836128fa565b93905060408301614b958185612c38565b90506040840191614bad6118a5845163ffffffff1690565b8088116150435750602085015161ffff1680831161500d5750610160850196614bde88516001600160e01b03191690565b6001600160e01b031981167f2812d52c0000000000000000000000000000000000000000000000000000000081148015614fe4575b8015614fbb575b15614c9557505050505050509181614c8f612ab5614c76614c8896614c456080610b6f9801866128fa565b6101e083015163ffffffff169063ffffffff614c6e610180613f74606088015163ffffffff1690565b941692615a4c565b51958694516001600160e01b03191690565b92806128fa565b90613c9f565b7f1e10bdc400000000000000000000000000000000000000000000000000000000909a99929394969895979a14600014614f845750614d3e614d0e614d31999a614ce260808801886128fa565b63ffffffff614d06610180614cfe606087015163ffffffff1690565b950151151590565b931691615853565b91614d206118a5845163ffffffff1690565b998a91516001600160e01b03191690565b614c8f612ab588806128fa565b6080810151519082614d5b614d5387806128fa565b810190614b66565b614f67575081614f31575b85151580614f25575b614efb5760408211614ec7576020015167ffffffffffffffff9081169081831c16614e8d575050614dad90614da7859493979561276e565b9061397c565b946000935b838510614e0b5750505050506118a5614dcf915163ffffffff1690565b808211614ddb57505090565b7f869337890000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b9091929395600190614e626118a5612bc9614e3a8667ffffffffffffffff16600052600a602052604060002090565b614e4b612a628d6109278b8d612c38565b6001600160a01b0316600052602052604060002090565b8015614e7d57614e719161397c565b965b0193929190614db2565b50614e879061396e565b96614e73565b7fafa933080000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045260245260446000fd5b7f8a0d71f7000000000000000000000000000000000000000000000000000000006000526004829052604060245260446000fd5b7f5bed51920000000000000000000000000000000000000000000000000000000060005260046000fd5b50606081015115614d6f565b6119c3827f8a0d71f700000000000000000000000000000000000000000000000000000000600052906044916004526000602452565b614f7e919350614da7614f7984613960565b612758565b91614d66565b7f2ee82075000000000000000000000000000000000000000000000000000000006000526001600160e01b03191660045260246000fd5b507fc4e05953000000000000000000000000000000000000000000000000000000008114614c1a565b507fac77ffec000000000000000000000000000000000000000000000000000000008114614c13565b7fd88dddd600000000000000000000000000000000000000000000000000000000600052600483905261ffff1660245260446000fd5b7f8693378900000000000000000000000000000000000000000000000000000000600052600452602487905260446000fd5b67ffffffffffffffff811660005260056020526040600020916040519261509b846106dd565b546001600160e01b038116845260e01c9182602085015263ffffffff821692836150d5575b50505050610b6f90516001600160e01b031690565b63ffffffff1642908103939084116127535783116150f357806150c0565b7ff08bcb3e0000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045263ffffffff1660245260445260646000fd5b60408136031261022c5760206040519161514f836106dd565b803561515a8161021b565b83520135602082015290565b9694919695929390956000946000986000986000965b808810615190575050505050505050929190565b9091929394959697999a6151ad6151a88a848b61289b565b615136565b9a6151e4612af88d614e4b6151d68967ffffffffffffffff16600052600a602052604060002090565b91516001600160a01b031690565b916151f561181960a0850151151590565b61533e5760009c604084019061521061234a835161ffff1690565b6152c6575b5050606083015163ffffffff1661522b916139ba565b9c608083015161523e9063ffffffff1690565b615247916139ba565b9b82516152579063ffffffff1690565b63ffffffff1661526690612731565b600193908083106152ba57506124056118a5602061528993015163ffffffff1690565b8082116152a9575061529a9161397c565b985b019695949392919061517c565b90506152b49161397c565b9861529c565b9150506152b49161397c565b906121ea61532f939f61531d6153269460208f8e61234a95506001600160a01b036152f885516001600160a01b031690565b91166001600160a01b03821614615337576153139150613b24565b915b015190615ab4565b925161ffff1690565b620186a0900490565b9b3880615215565b5091615315565b999b506001915061535f8461535961536593614da78b612731565b9b6139ba565b9c6139a2565b9a61529c565b91939093806101e00193846101e011612753576101208102908082046101201490151715612753576101e09101018093116127535761234a610140615401610b6f966dffffffffffffffffffffffffffff6122d76153ec6153d961540b9a63ffffffff6121ea9a169061397c565b6121ea61234a6101208c015161ffff1690565b614da76118a56101008b015163ffffffff1690565b93015161ffff1690565b612786565b9081602091031261022c575190565b60208151036154555761543b6020825183010160208301615410565b6001600160a01b038111908115615492575b506154555750565b61422a906040519182917f8d666f6000000000000000000000000000000000000000000000000000000000835260206004840181815201906102f1565b6104009150103861544d565b60208151036154c457600b6154bc6020835184010160208401615410565b106154c45750565b61422a906040519182917fe0d7fb0200000000000000000000000000000000000000000000000000000000835260206004840181815201906102f1565b9060208251036155275780615514575050565b6154bc6020835184010160208401615410565b6040517fe0d7fb02000000000000000000000000000000000000000000000000000000008152602060048201528061422a60248201856102f1565b919091356001600160e01b03198116926004811061557e575050565b6001600160e01b0319929350829060040360031b1b161690565b909291928360041161022c57831161022c57600401916003190190565b9060041161022c5790600490565b9081604091031261022c576020604051916155dd836106dd565b805183520151612ccf81610cd3565b916155f5612e5a565b5081156156d3575061561e612ab582806156186001600160e01b03199587615562565b95615598565b91167f181dcf1000000000000000000000000000000000000000000000000000000000810361565b575080602080610b6f935183010191016155c3565b7f97a657c900000000000000000000000000000000000000000000000000000000146156ab577f5247fdce0000000000000000000000000000000000000000000000000000000060005260046000fd5b806020806156be93518301019101615410565b6156c6610738565b9081526000602082015290565b91505067ffffffffffffffff6156e7610738565b911681526000602082015290565b6020604051917f181dcf1000000000000000000000000000000000000000000000000000000000828401528051602484015201511515604482015260448152610b6f606482610715565b6040519061574c826106c1565b60606080836000815260006020820152600060408201526000838201520152565b60208183031261022c5780359067ffffffffffffffff821161022c57019060a08282031261022c57604051916157a2836106c1565b6157ab8161120c565b83526157b9602082016102ab565b602084015260408101356157cc81610cd3565b60408401526060810135606084015260808101359067ffffffffffffffff821161022c57019080601f8301121561022c57813561580881610cbb565b926158166040519485610715565b81845260208085019260051b82010192831161022c57602001905b82821061584357505050608082015290565b8135815260209182019101615831565b61585b61573f565b508115615976577f1f3b3aba000000000000000000000000000000000000000000000000000000006001600160e01b031961589f61589985856155b5565b90615562565b160361594c57816158bb926158b392615598565b81019061576d565b9180615936575b61590c5763ffffffff6158d9835163ffffffff1690565b16116158e25790565b7f2e2b0c290000000000000000000000000000000000000000000000000000000060005260046000fd5b7fee433e990000000000000000000000000000000000000000000000000000000060005260046000fd5b506159476118196040840151151590565b6158c2565b7f5247fdce0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fb00b53dc0000000000000000000000000000000000000000000000000000000060005260046000fd5b604080516001600160a01b039283166020820190815292909316908301527fffffffffffffffffffff0000000000000000000000000000000000000000000090921660608201527fffff000000000000000000000000000000000000000000000000000000000000909216608083015290615a1e8160a08101612b38565b51902090565b6001600160a01b03610b6f9116600b615b61565b6001600160a01b03610b6f9116600b615c95565b9063ffffffff615a6993959495615a61612e5a565b5016916155ec565b91825111615a8a5780615a7e575b61590c5790565b50602081015115615a77565b7f4c4fc93a0000000000000000000000000000000000000000000000000000000060005260046000fd5b670de0b6b3a7640000916001600160e01b03615ad092166127a6565b0490565b80548210156128ab5760005260206000200190600090565b91615b06918354906000199060031b92831b921b19161790565b9055565b80548015615b32576000190190615b218282615ad4565b60001982549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6001810191806000528260205260406000205492831515600014615c13576000198401848111612753578354936000198501948511612753576000958583615bc497615bb59503615bca575b505050615b0a565b90600052602052604060002090565b55600190565b615bfa615bf491615beb615be1615c0a9588615ad4565b90549060031b1c90565b92839187615ad4565b90615aec565b8590600052602052604060002090565b55388080615bad565b50505050600090565b805490680100000000000000008210156106bc5781615c43916001615b0694018155615ad4565b81939154906000199060031b92831b921b19161790565b600081815260036020526040902054615c8f57615c78816002615c1c565b600254906000526003602052604060002055600190565b50600090565b6000828152600182016020526040902054615ccc5780615cb783600193615c1c565b80549260005201602052604060002055600190565b5050600090565b600081815260036020526040902054908115615ccc5760001982019082821161275357600254926000198401938411612753578383615bc49460009603615d33575b505050615d226002615b0a565b600390600052602052604060002090565b615d22615bf491615d4b615be1615d55956002615ad4565b9283916002615ad4565b55388080615d1556fea164736f6c634300081a000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000ad78ebc5ac6200000000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b600000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b537b61351c91154bf361a1589bc0480f89d616e000000000000000000000000062f05cd6c835677b05a8658a3519694768613160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ee7d8bcfb72bc1880d0cf19822eb0a2e6577ab62000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b60000000000000000000000000000000000000000000000000c7d713b49da0000000000000000000000000000ee7d8bcfb72bc1880d0cf19822eb0a2e6577ab620000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : staticConfig (tuple):
Arg [1] : maxFeeJuelsPerMsg (uint96): 200000000000000000000
Arg [2] : linkToken (address): 0xc2C447b04e0ED3476DdbDae8E9E39bE7159d27b6
Arg [3] : tokenPriceStalenessThreshold (uint32): 86400
Arg [1] : priceUpdaters (address[]): 0xb537B61351c91154BF361a1589bC0480F89d616E,0x062f05CD6c835677B05a8658A351969476861316
Arg [2] : feeTokens (address[]): 0xEE7D8BCFb72bC1880D0Cf19822eB0A2e6577aB62,0xc2C447b04e0ED3476DdbDae8E9E39bE7159d27b6
Arg [3] : tokenPriceFeeds (tuple[]):
Arg [4] : tokenTransferFeeConfigArgs (tuple[]):
Arg [5] : premiumMultiplierWeiPerEthArgs (tuple[]):
Arg [1] : token (address): 0xc2C447b04e0ED3476DdbDae8E9E39bE7159d27b6
Arg [2] : premiumMultiplierWeiPerEth (uint64): 900000000000000000
Arg [1] : token (address): 0xEE7D8BCFb72bC1880D0Cf19822eB0A2e6577aB62
Arg [2] : premiumMultiplierWeiPerEth (uint64): 1000000000000000000
Arg [6] : destChainConfigArgs (tuple[]):
-----Encoded View---------------
23 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000ad78ebc5ac6200000
Arg [1] : 000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b6
Arg [2] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [5] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000220
Arg [8] : 00000000000000000000000000000000000000000000000000000000000002c0
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [10] : 000000000000000000000000b537b61351c91154bf361a1589bc0480f89d616e
Arg [11] : 000000000000000000000000062f05cd6c835677b05a8658a351969476861316
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [13] : 000000000000000000000000ee7d8bcfb72bc1880d0cf19822eb0a2e6577ab62
Arg [14] : 000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b6
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [18] : 000000000000000000000000c2c447b04e0ed3476ddbdae8e9e39be7159d27b6
Arg [19] : 0000000000000000000000000000000000000000000000000c7d713b49da0000
Arg [20] : 000000000000000000000000ee7d8bcfb72bc1880d0cf19822eb0a2e6577ab62
Arg [21] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.