Contract 0x3b39Cd9599137f892Ad57A4f54158198D445D147

Contract Overview

Balance:
0 ETH

Token:
Txn Hash Method
Block
From
To
Value
0xc444993d5881da8ff86652a36723fa06205a482044ba99051286af4516716ad0Set Token Transf...78833762024-03-27 10:24:0019 days 11 hrs ago0xd5ccdabf11e3de8d2f64022e232ac18001b8acac IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000802282510.001000258
0x57e839d4b0817fb92654af4903dfc978d6bdfc78c26559d17bf1fb65c8d0e8f5Accept Ownership73726092024-03-15 14:38:2631 days 6 hrs ago0xd5ccdabf11e3de8d2f64022e232ac18001b8acac IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000018838940.001000258
0xcc84929b34ddb0952aaf7d2cba6bdb25e07603b182ba81ff3609a6603fea9f7eTransfer Ownersh...73699012024-03-15 13:08:1031 days 8 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000148201540.001000254
0x957ee8210e62542fab0be497816be3cca079c00cde2d92b3a70e8cc98e9ac8c3Set Token Transf...67632902024-03-01 12:07:4845 days 9 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000019653320.027991039
0x5ce5c442714eb30f0f901b170bdccebb19c841b1aac32537c369e7857c385468Apply Pool Updat...67632802024-03-01 12:07:2845 days 9 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000053111540.028340336
0xf87cc4c1b56367dcfd17d3361d2dacf3ff0ae69e43b825123ed5f824f575366eSet Token Transf...66965662024-02-28 23:03:4046 days 22 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000009084620.023396486
0x3ab6a151a130c0021e7475598e1993ee361a2c9668fa9d476ccb65dff16cdb34Apply Pool Updat...66965592024-02-28 23:03:2646 days 22 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000013109890.023466258
0x9e66dd41de4243daf1adf63cc8a9b2b835eacecbb41608cc4f9e19e416f5bce4Set Rate Limiter...66422042024-02-27 16:51:3648 days 4 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.000067641930.001000254
0x303feff6c3fde78a102869536ebb063bdceb521e899928249fc0a7321e02d986Set Rate Limiter...66421882024-02-27 16:51:0448 days 4 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0000719387670.001000254
0x79da0347cba41bada086bd23486ad4b79409b3bd300bd9caa661c519a46a29b1Set Rate Limiter...66421652024-02-27 16:50:1848 days 4 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.000073812740.001000254
0x3b87581fad6a8611fad801eeaede913d88e848d30369e8aade9a5202d86fa0cfSet Nops51476442024-01-24 2:32:5682 days 18 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  0x3b39cd9599137f892ad57a4f54158198d445d1470 ETH0.0002763977060.001000254
0x1c6ffbc26db17ec3d5fb523d7e3acc1ad72cfa4390d4ced6326a0e4baa855bec0x6101806051475022024-01-24 2:28:1282 days 18 hrs ago0xda9e8e71bb750a996af33ebb8abb18cd9eb9dc75 IN  Create: EVM2EVMOnRamp0 ETH0.0303071387880.001000254
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EVM2EVMOnRamp

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion
File 1 of 25 : EVM2EVMOnRamp.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IPool} from "../interfaces/pools/IPool.sol";
import {IARM} from "../interfaces/IARM.sol";
import {IPriceRegistry} from "../interfaces/IPriceRegistry.sol";
import {IEVM2AnyOnRamp} from "../interfaces/IEVM2AnyOnRamp.sol";
import {IEVM2AnyOnRampClient} from "../interfaces/IEVM2AnyOnRampClient.sol";
import {ILinkAvailable} from "../interfaces/automation/ILinkAvailable.sol";

import {AggregateRateLimiter} from "../AggregateRateLimiter.sol";
import {Client} from "../libraries/Client.sol";
import {Internal} from "../libraries/Internal.sol";
import {RateLimiter} from "../libraries/RateLimiter.sol";
import {USDPriceWith18Decimals} from "../libraries/USDPriceWith18Decimals.sol";
import {EnumerableMapAddresses} from "../../shared/enumerable/EnumerableMapAddresses.sol";

import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol";
import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol";

/// @notice The onRamp is a contract that handles lane-specific fee logic, NOP payments and
/// bridgeable token support.
/// @dev The EVM2EVMOnRamp, CommitStore and EVM2EVMOffRamp form an xchain upgradeable unit. Any change to one of them
/// results an onchain upgrade of all 3.
contract EVM2EVMOnRamp is IEVM2AnyOnRamp, ILinkAvailable, AggregateRateLimiter, ITypeAndVersion {
  using SafeERC20 for IERC20;
  using EnumerableMap for EnumerableMap.AddressToUintMap;
  using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap;
  using USDPriceWith18Decimals for uint224;

  error InvalidExtraArgsTag();
  error OnlyCallableByOwnerOrAdmin();
  error OnlyCallableByOwnerOrAdminOrNop();
  error InvalidWithdrawParams();
  error NoFeesToPay();
  error NoNopsToPay();
  error InsufficientBalance();
  error TooManyNops();
  error MaxFeeBalanceReached();
  error MessageTooLarge(uint256 maxSize, uint256 actualSize);
  error MessageGasLimitTooHigh();
  error UnsupportedNumberOfTokens();
  error UnsupportedToken(IERC20 token);
  error MustBeCalledByRouter();
  error RouterMustSetOriginalSender();
  error InvalidTokenPoolConfig();
  error PoolAlreadyAdded();
  error PoolDoesNotExist(address token);
  error TokenPoolMismatch();
  error InvalidConfig();
  error InvalidAddress(bytes encodedAddress);
  error BadARMSignal();
  error LinkBalanceNotSettled();
  error InvalidNopAddress(address nop);
  error NotAFeeToken(address token);
  error CannotSendZeroTokens();
  error SourceTokenDataTooLarge(address token);
  error InvalidChainSelector(uint64 chainSelector);

  event ConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig);
  event NopPaid(address indexed nop, uint256 amount);
  event FeeConfigSet(FeeTokenConfigArgs[] feeConfig);
  event TokenTransferFeeConfigSet(TokenTransferFeeConfigArgs[] transferFeeConfig);
  /// RMN depends on this event, if changing, please notify the RMN maintainers.
  event CCIPSendRequested(Internal.EVM2EVMMessage message);
  event NopsSet(uint256 nopWeightsTotal, NopAndWeight[] nopsAndWeights);
  event PoolAdded(address token, address pool);
  event PoolRemoved(address token, address pool);

  /// @dev Struct that contains the static configuration
  /// RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct StaticConfig {
    address linkToken; // ────────╮ Link token address
    uint64 chainSelector; // ─────╯ Source chainSelector
    uint64 destChainSelector; // ─╮ Destination chainSelector
    uint64 defaultTxGasLimit; //  │ Default gas limit for a tx
    uint96 maxNopFeesJuels; // ───╯ Max nop fee balance onramp can have
    address prevOnRamp; //          Address of previous-version OnRamp
    address armProxy; //            Address of ARM proxy
  }

  /// @dev Struct to contains the dynamic configuration
  struct DynamicConfig {
    address router; // ──────────────────────────╮ Router address
    uint16 maxNumberOfTokensPerMsg; //           │ Maximum number of distinct ERC20 token transferred per message
    uint32 destGasOverhead; //                   │ Gas charged on top of the gasLimit to cover destination chain costs
    uint16 destGasPerPayloadByte; //             │ Destination chain gas charged for passing each byte of `data` payload to receiver
    uint32 destDataAvailabilityOverheadGas; // ──╯ Extra data availability gas charged on top of the message, e.g. for OCR
    uint16 destGasPerDataAvailabilityByte; // ───╮ Amount of gas to charge per byte of message data that needs availability
    uint16 destDataAvailabilityMultiplierBps; // │ Multiplier for data availability gas, multiples of bps, or 0.0001
    address priceRegistry; //                    │ Price registry address
    uint32 maxDataBytes; //                      │ Maximum payload data size in bytes
    uint32 maxPerMsgGasLimit; // ────────────────╯ Maximum gas limit for messages targeting EVMs
  }

  /// @dev Struct to hold the execution fee configuration for a fee token
  struct FeeTokenConfig {
    uint32 networkFeeUSDCents; // ─────────╮ Flat network fee to charge for messages,  multiples of 0.01 USD
    uint64 gasMultiplierWeiPerEth; //      │ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost.
    uint64 premiumMultiplierWeiPerEth; //  │ Multiplier for fee-token-specific premiums
    bool enabled; // ──────────────────────╯ Whether this fee token is enabled
  }

  /// @dev Struct to hold the fee configuration for a fee token, same as the FeeTokenConfig but with
  /// token included so that an array of these can be passed in to setFeeTokenConfig to set the mapping
  struct FeeTokenConfigArgs {
    address token; // ─────────────────────╮ Token address
    uint32 networkFeeUSDCents; //          │ Flat network fee to charge for messages,  multiples of 0.01 USD
    uint64 gasMultiplierWeiPerEth; // ─────╯ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost
    uint64 premiumMultiplierWeiPerEth; // ─╮ Multiplier for fee-token-specific premiums, 1e18 based
    bool enabled; // ──────────────────────╯ Whether this fee token is enabled
  }

  /// @dev Struct to hold the 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
    uint32 destBytesOverhead; // ─╯ Extra data availability bytes on top of fixed transfer data, including sourceTokenData and offchainData
  }

  /// @dev Same as TokenTransferFeeConfig
  /// token included so that an array of these can be passed in to setTokenTransferFeeConfig
  struct TokenTransferFeeConfigArgs {
    address token; // ────────────╮ Token address
    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
    uint32 destBytesOverhead; // ─╯ Extra data availability bytes on top of fixed transfer data, including sourceTokenData and offchainData
  }

  /// @dev Nop address and weight, used to set the nops and their weights
  struct NopAndWeight {
    address nop; // ────╮ Address of the node operator
    uint16 weight; // ──╯ Weight for nop rewards
  }

  // STATIC CONFIG
  // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables
  string public constant override typeAndVersion = "EVM2EVMOnRamp 1.2.0";
  /// @dev metadataHash is a lane-specific prefix for a message hash preimage which ensures global uniqueness
  /// Ensures that 2 identical messages sent to 2 different lanes will have a distinct hash.
  /// Must match the metadataHash used in computing leaf hashes offchain for the root committed in
  /// the commitStore and i_metadataHash in the offRamp.
  bytes32 internal immutable i_metadataHash;
  /// @dev Default gas limit for a transactions that did not specify
  /// a gas limit in the extraArgs.
  uint64 internal immutable i_defaultTxGasLimit;
  /// @dev Maximum nop fee that can accumulate in this onramp
  uint96 internal immutable i_maxNopFeesJuels;
  /// @dev The link token address - known to pay nops for their work
  address internal immutable i_linkToken;
  /// @dev The chain ID of the source chain that this contract is deployed to
  uint64 internal immutable i_chainSelector;
  /// @dev The chain ID of the destination chain
  uint64 internal immutable i_destChainSelector;
  /// @dev The address of previous-version OnRamp for this lane
  /// Used to be able to provide sequencing continuity during a zero downtime upgrade.
  address internal immutable i_prevOnRamp;
  /// @dev The address of the arm proxy
  address internal immutable i_armProxy;
  /// @dev the maximum number of nops that can be configured at the same time.
  /// Used to bound gas for loops over nops.
  uint256 private constant MAX_NUMBER_OF_NOPS = 64;

  // DYNAMIC CONFIG
  /// @dev The config for the onRamp
  DynamicConfig internal s_dynamicConfig;
  /// @dev (address nop => uint256 weight)
  EnumerableMap.AddressToUintMap internal s_nops;
  /// @dev source token => token pool
  EnumerableMapAddresses.AddressToAddressMap private s_poolsBySourceToken;

  /// @dev The execution fee token config that can be set by the owner or fee admin
  mapping(address token => FeeTokenConfig feeTokenConfig) internal s_feeTokenConfig;
  /// @dev The token transfer fee config that can be set by the owner or fee admin
  mapping(address token => TokenTransferFeeConfig tranferFeeConfig) internal s_tokenTransferFeeConfig;

  // STATE
  /// @dev The current nonce per sender.
  /// The offramp has a corresponding s_senderNonce mapping to ensure messages
  /// are executed in the same order they are sent.
  mapping(address sender => uint64 nonce) internal s_senderNonce;
  /// @dev The amount of LINK available to pay NOPS
  uint96 internal s_nopFeesJuels;
  /// @dev The combined weight of all NOPs weights
  uint32 internal s_nopWeightsTotal;
  /// @dev The last used sequence number. This is zero in the case where no
  /// messages has been sent yet. 0 is not a valid sequence number for any
  /// real transaction.
  uint64 internal s_sequenceNumber;

  constructor(
    StaticConfig memory staticConfig,
    DynamicConfig memory dynamicConfig,
    Internal.PoolUpdate[] memory tokensAndPools,
    RateLimiter.Config memory rateLimiterConfig,
    FeeTokenConfigArgs[] memory feeTokenConfigs,
    TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs,
    NopAndWeight[] memory nopsAndWeights
  ) AggregateRateLimiter(rateLimiterConfig) {
    if (
      staticConfig.linkToken == address(0) ||
      staticConfig.chainSelector == 0 ||
      staticConfig.destChainSelector == 0 ||
      staticConfig.defaultTxGasLimit == 0 ||
      staticConfig.armProxy == address(0)
    ) revert InvalidConfig();

    i_metadataHash = keccak256(
      abi.encode(
        Internal.EVM_2_EVM_MESSAGE_HASH,
        staticConfig.chainSelector,
        staticConfig.destChainSelector,
        address(this)
      )
    );
    i_linkToken = staticConfig.linkToken;
    i_chainSelector = staticConfig.chainSelector;
    i_destChainSelector = staticConfig.destChainSelector;
    i_defaultTxGasLimit = staticConfig.defaultTxGasLimit;
    i_maxNopFeesJuels = staticConfig.maxNopFeesJuels;
    i_prevOnRamp = staticConfig.prevOnRamp;
    i_armProxy = staticConfig.armProxy;

    _setDynamicConfig(dynamicConfig);
    _setFeeTokenConfig(feeTokenConfigs);
    _setTokenTransferFeeConfig(tokenTransferFeeConfigArgs);
    _setNops(nopsAndWeights);

    // Set new tokens and pools
    _applyPoolUpdates(new Internal.PoolUpdate[](0), tokensAndPools);
  }

  // ================================================================
  // │                          Messaging                           │
  // ================================================================

  /// @inheritdoc IEVM2AnyOnRamp
  function getExpectedNextSequenceNumber() external view returns (uint64) {
    return s_sequenceNumber + 1;
  }

  /// @inheritdoc IEVM2AnyOnRamp
  function getSenderNonce(address sender) external view returns (uint64) {
    uint256 senderNonce = s_senderNonce[sender];

    if (senderNonce == 0 && i_prevOnRamp != address(0)) {
      // If OnRamp was upgraded, check if sender has a nonce from the previous OnRamp.
      return IEVM2AnyOnRamp(i_prevOnRamp).getSenderNonce(sender);
    }
    return uint64(senderNonce);
  }

  /// @inheritdoc IEVM2AnyOnRampClient
  function forwardFromRouter(
    uint64 destChainSelector,
    Client.EVM2AnyMessage calldata message,
    uint256 feeTokenAmount,
    address originalSender
  ) external whenHealthy returns (bytes32) {
    // Validate message sender is set and allowed. Not validated in `getFee` since it is not user-driven.
    if (originalSender == address(0)) revert RouterMustSetOriginalSender();
    // Router address may be zero intentionally to pause.
    if (msg.sender != s_dynamicConfig.router) revert MustBeCalledByRouter();
    if (destChainSelector != i_destChainSelector) revert InvalidChainSelector(destChainSelector);

    // EVM destination addresses should be abi encoded and therefore always 32 bytes long
    // Not duplicately validated in `getFee`. Invalid address is uncommon, gas cost outweighs UX gain.
    if (message.receiver.length != 32) revert InvalidAddress(message.receiver);
    uint256 decodedReceiver = abi.decode(message.receiver, (uint256));
    // We want to disallow sending to address(0) and to precompiles, which exist on address(1) through address(9).
    if (decodedReceiver > type(uint160).max || decodedReceiver < 10) revert InvalidAddress(message.receiver);

    uint256 gasLimit = _fromBytes(message.extraArgs).gasLimit;
    // Validate the message with various checks
    uint256 numberOfTokens = message.tokenAmounts.length;
    _validateMessage(message.data.length, gasLimit, numberOfTokens);

    // Only check token value if there are tokens
    if (numberOfTokens > 0) {
      for (uint256 i = 0; i < numberOfTokens; ++i) {
        if (message.tokenAmounts[i].amount == 0) revert CannotSendZeroTokens();
      }
      // Rate limit on aggregated token value
      _rateLimitValue(message.tokenAmounts, IPriceRegistry(s_dynamicConfig.priceRegistry));
    }

    // Convert feeToken to link if not already in link
    if (message.feeToken == i_linkToken) {
      // Since there is only 1b link this is safe
      s_nopFeesJuels += uint96(feeTokenAmount);
    } else {
      // the cast from uint256 to uint96 is considered safe, uint96 can store more than max supply of link token
      s_nopFeesJuels += uint96(
        IPriceRegistry(s_dynamicConfig.priceRegistry).convertTokenAmount(message.feeToken, feeTokenAmount, i_linkToken)
      );
    }
    if (s_nopFeesJuels > i_maxNopFeesJuels) revert MaxFeeBalanceReached();

    if (s_senderNonce[originalSender] == 0 && i_prevOnRamp != address(0)) {
      // If this is first time send for a sender in new OnRamp, check if they have a nonce
      // from the previous OnRamp and start from there instead of zero.
      s_senderNonce[originalSender] = IEVM2AnyOnRamp(i_prevOnRamp).getSenderNonce(originalSender);
    }

    // We need the next available sequence number so we increment before we use the value
    Internal.EVM2EVMMessage memory newMessage = Internal.EVM2EVMMessage({
      sourceChainSelector: i_chainSelector,
      sender: originalSender,
      receiver: address(uint160(decodedReceiver)),
      sequenceNumber: ++s_sequenceNumber,
      gasLimit: gasLimit,
      strict: false,
      nonce: ++s_senderNonce[originalSender],
      feeToken: message.feeToken,
      feeTokenAmount: feeTokenAmount,
      data: message.data,
      tokenAmounts: message.tokenAmounts,
      sourceTokenData: new bytes[](numberOfTokens), // will be filled in later
      messageId: ""
    });

    // Lock the tokens as last step. TokenPools may not always be trusted.
    // There should be no state changes after external call to TokenPools.
    for (uint256 i = 0; i < numberOfTokens; ++i) {
      Client.EVMTokenAmount memory tokenAndAmount = message.tokenAmounts[i];
      bytes memory tokenData = getPoolBySourceToken(destChainSelector, IERC20(tokenAndAmount.token)).lockOrBurn(
        originalSender,
        message.receiver,
        tokenAndAmount.amount,
        i_destChainSelector,
        bytes("") // any future extraArgs component would be added here
      );

      // Since the DON has to pay for the tokenData to be included on the destination chain, we cap the length of the tokenData.
      // This prevents gas bomb attacks on the NOPs. We use destBytesOverhead as a proxy to cap the number of bytes we accept.
      // As destBytesOverhead accounts for tokenData + offchainData, this caps the worst case abuse to the number of bytes reserved for offchainData.
      // It therefore fully mitigates gas bombs for most tokens, as most tokens don't use offchainData.
      if (tokenData.length > s_tokenTransferFeeConfig[tokenAndAmount.token].destBytesOverhead)
        revert SourceTokenDataTooLarge(tokenAndAmount.token);

      newMessage.sourceTokenData[i] = tokenData;
    }

    // Hash only after the sourceTokenData has been set
    newMessage.messageId = Internal._hash(newMessage, i_metadataHash);

    // Emit message request
    // Note this must happen after pools, some tokens (eg USDC) emit events that we
    // expect to directly precede this event.
    emit CCIPSendRequested(newMessage);
    return newMessage.messageId;
  }

  /// @dev Convert the extra args bytes into a struct
  /// @param extraArgs The extra args bytes
  /// @return The extra args struct
  function _fromBytes(bytes calldata extraArgs) internal view returns (Client.EVMExtraArgsV1 memory) {
    if (extraArgs.length == 0) {
      return Client.EVMExtraArgsV1({gasLimit: i_defaultTxGasLimit});
    }
    if (bytes4(extraArgs) != Client.EVM_EXTRA_ARGS_V1_TAG) revert InvalidExtraArgsTag();
    // EVMExtraArgsV1 originally included a second boolean (strict) field which we have deprecated entirely.
    // Clients may still send that version but it will be ignored.
    return abi.decode(extraArgs[4:], (Client.EVMExtraArgsV1));
  }

  /// @notice Validate the forwarded message with various checks.
  /// @dev This function can be called multiple times during a CCIPSend,
  /// only common user-driven mistakes are validated here to minimize duplicate validation cost.
  /// @param dataLength The length of the data field of the message.
  /// @param gasLimit The gasLimit set in message for destination execution.
  /// @param numberOfTokens The number of tokens to be sent.
  function _validateMessage(uint256 dataLength, uint256 gasLimit, uint256 numberOfTokens) internal view {
    // Check that payload is formed correctly
    uint256 maxDataBytes = uint256(s_dynamicConfig.maxDataBytes);
    if (dataLength > maxDataBytes) revert MessageTooLarge(maxDataBytes, dataLength);
    if (gasLimit > uint256(s_dynamicConfig.maxPerMsgGasLimit)) revert MessageGasLimitTooHigh();
    if (numberOfTokens > uint256(s_dynamicConfig.maxNumberOfTokensPerMsg)) revert UnsupportedNumberOfTokens();
  }

  // ================================================================
  // │                           Config                             │
  // ================================================================

  /// @notice Returns the static onRamp config.
  /// @dev RMN depends on this function, if changing, please notify the RMN maintainers.
  /// @return the configuration.
  function getStaticConfig() external view returns (StaticConfig memory) {
    return
      StaticConfig({
        linkToken: i_linkToken,
        chainSelector: i_chainSelector,
        destChainSelector: i_destChainSelector,
        defaultTxGasLimit: i_defaultTxGasLimit,
        maxNopFeesJuels: i_maxNopFeesJuels,
        prevOnRamp: i_prevOnRamp,
        armProxy: i_armProxy
      });
  }

  /// @notice Returns the dynamic onRamp config.
  /// @return dynamicConfig the configuration.
  function getDynamicConfig() external view returns (DynamicConfig memory dynamicConfig) {
    return s_dynamicConfig;
  }

  /// @notice Sets the dynamic configuration.
  /// @param dynamicConfig The configuration.
  function setDynamicConfig(DynamicConfig memory dynamicConfig) external onlyOwner {
    _setDynamicConfig(dynamicConfig);
  }

  /// @notice Internal version of setDynamicConfig to allow for reuse in the constructor.
  function _setDynamicConfig(DynamicConfig memory dynamicConfig) internal {
    // We permit router to be set to zero as a way to pause the contract.
    if (dynamicConfig.priceRegistry == address(0)) revert InvalidConfig();

    s_dynamicConfig = dynamicConfig;

    emit ConfigSet(
      StaticConfig({
        linkToken: i_linkToken,
        chainSelector: i_chainSelector,
        destChainSelector: i_destChainSelector,
        defaultTxGasLimit: i_defaultTxGasLimit,
        maxNopFeesJuels: i_maxNopFeesJuels,
        prevOnRamp: i_prevOnRamp,
        armProxy: i_armProxy
      }),
      dynamicConfig
    );
  }

  // ================================================================
  // │                      Tokens and pools                        │
  // ================================================================

  /// @inheritdoc IEVM2AnyOnRampClient
  function getSupportedTokens(uint64 /*destChainSelector*/) external view returns (address[] memory) {
    address[] memory sourceTokens = new address[](s_poolsBySourceToken.length());
    for (uint256 i = 0; i < sourceTokens.length; ++i) {
      (sourceTokens[i], ) = s_poolsBySourceToken.at(i);
    }
    return sourceTokens;
  }

  /// @inheritdoc IEVM2AnyOnRampClient
  function getPoolBySourceToken(uint64 /*destChainSelector*/, IERC20 sourceToken) public view returns (IPool) {
    if (!s_poolsBySourceToken.contains(address(sourceToken))) revert UnsupportedToken(sourceToken);
    return IPool(s_poolsBySourceToken.get(address(sourceToken)));
  }

  /// @inheritdoc IEVM2AnyOnRamp
  /// @dev This method can only be called by the owner of the contract.
  function applyPoolUpdates(
    Internal.PoolUpdate[] memory removes,
    Internal.PoolUpdate[] memory adds
  ) external onlyOwner {
    _applyPoolUpdates(removes, adds);
  }

  function _applyPoolUpdates(Internal.PoolUpdate[] memory removes, Internal.PoolUpdate[] memory adds) internal {
    for (uint256 i = 0; i < removes.length; ++i) {
      address token = removes[i].token;
      address pool = removes[i].pool;

      if (!s_poolsBySourceToken.contains(token)) revert PoolDoesNotExist(token);
      if (s_poolsBySourceToken.get(token) != pool) revert TokenPoolMismatch();

      if (s_poolsBySourceToken.remove(token)) {
        emit PoolRemoved(token, pool);
      }
    }

    for (uint256 i = 0; i < adds.length; ++i) {
      address token = adds[i].token;
      address pool = adds[i].pool;

      if (token == address(0) || pool == address(0)) revert InvalidTokenPoolConfig();
      if (token != address(IPool(pool).getToken())) revert TokenPoolMismatch();

      if (s_poolsBySourceToken.set(token, pool)) {
        emit PoolAdded(token, pool);
      } else {
        revert PoolAlreadyAdded();
      }
    }
  }

  // ================================================================
  // │                             Fees                             │
  // ================================================================

  /// @inheritdoc IEVM2AnyOnRampClient
  /// @dev getFee MUST revert if the feeToken is not listed in the fee token config, as the router assumes it does.
  /// @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 getFee(
    uint64 destChainSelector,
    Client.EVM2AnyMessage calldata message
  ) external view returns (uint256 feeTokenAmount) {
    if (destChainSelector != i_destChainSelector) revert InvalidChainSelector(destChainSelector);

    uint256 gasLimit = _fromBytes(message.extraArgs).gasLimit;
    // Validate the message with various checks
    _validateMessage(message.data.length, gasLimit, message.tokenAmounts.length);

    FeeTokenConfig memory feeTokenConfig = s_feeTokenConfig[message.feeToken];
    if (!feeTokenConfig.enabled) revert NotAFeeToken(message.feeToken);

    (uint224 feeTokenPrice, uint224 packedGasPrice) = IPriceRegistry(s_dynamicConfig.priceRegistry)
      .getTokenAndGasPrices(message.feeToken, destChainSelector);
    uint112 executionGasPrice = uint112(packedGasPrice);

    // 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 premiumFee = 0;
    uint32 tokenTransferGas = 0;
    uint32 tokenTransferBytesOverhead = 0;
    if (message.tokenAmounts.length > 0) {
      (premiumFee, tokenTransferGas, tokenTransferBytesOverhead) = _getTokenTransferCost(
        message.feeToken,
        feeTokenPrice,
        message.tokenAmounts
      );
    } else {
      // Convert USD cents with 2 decimals to 18 decimals.
      premiumFee = uint256(feeTokenConfig.networkFeeUSDCents) * 1e16;
    }

    // Apply a feeToken-specific multiplier with 18 decimals, raising the premiumFee to 36 decimals
    premiumFee = premiumFee * feeTokenConfig.premiumMultiplierWeiPerEth;

    // Calculate execution gas fee on destination chain in USD with 36 decimals.
    // We add the message gas limit, the overhead gas, the gas of passing message data to receiver, and token transfer gas together.
    // We then multiply this gas total with the gas multiplier and gas price, converting it into USD with 36 decimals.
    uint256 executionCost = executionGasPrice *
      ((gasLimit +
        s_dynamicConfig.destGasOverhead +
        (message.data.length * s_dynamicConfig.destGasPerPayloadByte) +
        tokenTransferGas) * feeTokenConfig.gasMultiplierWeiPerEth);

    // 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 dataAvailabilityCost = 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 (s_dynamicConfig.destDataAvailabilityMultiplierBps > 0) {
      // Parse the dava availability gas price stored in the higher-order 112 bits of encoded gas price.
      uint112 dataAvailabilityGasPrice = uint112(packedGasPrice >> Internal.GAS_PRICE_BITS);

      dataAvailabilityCost = _getDataAvailabilityCost(
        dataAvailabilityGasPrice,
        message.data.length,
        message.tokenAmounts.length,
        tokenTransferBytesOverhead
      );
    }

    // Calculate number of fee tokens to charge.
    // Total USD fee is in 36 decimals, feeTokenPrice is in 18 decimals USD for 1e18 smallest token denominations.
    // Result of the division is the number of smallest token denominations.
    return (premiumFee + executionCost + dataAvailabilityCost) / feeTokenPrice;
  }

  /// @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 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(
    uint112 dataAvailabilityGasPrice,
    uint256 messageDataLength,
    uint256 numberOfTokens,
    uint32 tokenTransferBytesOverhead
  ) internal view returns (uint256 dataAvailabilityCostUSD36Decimal) {
    // dataAvailabilityLengthBytes sums up byte lengths of fixed message fields and dynamic message fields.
    // Fixed message fields does 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 * s_dynamicConfig.destGasPerDataAvailabilityByte) +
      s_dynamicConfig.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) * s_dynamicConfig.destDataAvailabilityMultiplierBps) * 1e14;
  }

  /// @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 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(
    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[tokenAmount.token];

      // Validate if the token is supported, do not calculate fee for unsupported tokens.
      if (!s_poolsBySourceToken.contains(tokenAmount.token)) revert UnsupportedToken(IERC20(tokenAmount.token));

      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 PriceRegistry cannot return a valid price for the token.
      if (transferFeeConfig.deciBps > 0) {
        uint224 tokenPrice = 0;
        if (tokenAmount.token != feeToken) {
          tokenPrice = IPriceRegistry(s_dynamicConfig.priceRegistry).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;
      }

      tokenTransferFeeUSDWei += bpsFeeUSDWei;
    }

    return (tokenTransferFeeUSDWei, tokenTransferGas, tokenTransferBytesOverhead);
  }

  /// @notice Gets the fee configuration for a token
  /// @param token The token to get the fee configuration for
  /// @return feeTokenConfig FeeTokenConfig struct
  function getFeeTokenConfig(address token) external view returns (FeeTokenConfig memory feeTokenConfig) {
    return s_feeTokenConfig[token];
  }

  /// @notice Sets the fee configuration for a token
  /// @param feeTokenConfigArgs Array of FeeTokenConfigArgs structs.
  function setFeeTokenConfig(FeeTokenConfigArgs[] memory feeTokenConfigArgs) external onlyOwnerOrAdmin {
    _setFeeTokenConfig(feeTokenConfigArgs);
  }

  /// @dev Set the fee config
  /// @param feeTokenConfigArgs The fee token configs.
  function _setFeeTokenConfig(FeeTokenConfigArgs[] memory feeTokenConfigArgs) internal {
    for (uint256 i = 0; i < feeTokenConfigArgs.length; ++i) {
      FeeTokenConfigArgs memory configArg = feeTokenConfigArgs[i];

      s_feeTokenConfig[configArg.token] = FeeTokenConfig({
        networkFeeUSDCents: configArg.networkFeeUSDCents,
        gasMultiplierWeiPerEth: configArg.gasMultiplierWeiPerEth,
        premiumMultiplierWeiPerEth: configArg.premiumMultiplierWeiPerEth,
        enabled: configArg.enabled
      });
    }
    emit FeeConfigSet(feeTokenConfigArgs);
  }

  /// @notice Gets the transfer fee config for a given token.
  function getTokenTransferFeeConfig(
    address token
  ) external view returns (TokenTransferFeeConfig memory tokenTransferFeeConfig) {
    return s_tokenTransferFeeConfig[token];
  }

  /// @notice Sets the transfer fee config.
  /// @dev only callable by the owner or admin.
  function setTokenTransferFeeConfig(
    TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs
  ) external onlyOwnerOrAdmin {
    _setTokenTransferFeeConfig(tokenTransferFeeConfigArgs);
  }

  /// @notice internal helper to set the token transfer fee config.
  function _setTokenTransferFeeConfig(TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs) internal {
    for (uint256 i = 0; i < tokenTransferFeeConfigArgs.length; ++i) {
      TokenTransferFeeConfigArgs memory configArg = tokenTransferFeeConfigArgs[i];

      s_tokenTransferFeeConfig[configArg.token] = TokenTransferFeeConfig({
        minFeeUSDCents: configArg.minFeeUSDCents,
        maxFeeUSDCents: configArg.maxFeeUSDCents,
        deciBps: configArg.deciBps,
        destGasOverhead: configArg.destGasOverhead,
        destBytesOverhead: configArg.destBytesOverhead
      });
    }
    emit TokenTransferFeeConfigSet(tokenTransferFeeConfigArgs);
  }

  // ================================================================
  // │                         NOP payments                         │
  // ================================================================

  /// @notice Get the total amount of fees to be paid to the Nops (in LINK)
  /// @return totalNopFees
  function getNopFeesJuels() external view returns (uint96) {
    return s_nopFeesJuels;
  }

  /// @notice Gets the Nops and their weights
  /// @return nopsAndWeights Array of NopAndWeight structs
  /// @return weightsTotal The sum weight of all Nops
  function getNops() external view returns (NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) {
    uint256 length = s_nops.length();
    nopsAndWeights = new NopAndWeight[](length);
    for (uint256 i = 0; i < length; ++i) {
      (address nopAddress, uint256 nopWeight) = s_nops.at(i);
      nopsAndWeights[i] = NopAndWeight({nop: nopAddress, weight: uint16(nopWeight)});
    }
    weightsTotal = s_nopWeightsTotal;
    return (nopsAndWeights, weightsTotal);
  }

  /// @notice Sets the Nops and their weights
  /// @param nopsAndWeights Array of NopAndWeight structs
  function setNops(NopAndWeight[] calldata nopsAndWeights) external onlyOwnerOrAdmin {
    _setNops(nopsAndWeights);
  }

  /// @param nopsAndWeights New set of nops and weights
  /// @dev Clears existing nops, sets new nops and weights
  /// @dev We permit fees to accrue before nops are configured, in which case
  /// they will go to the first set of configured nops.
  function _setNops(NopAndWeight[] memory nopsAndWeights) internal {
    uint256 numberOfNops = nopsAndWeights.length;
    if (numberOfNops > MAX_NUMBER_OF_NOPS) revert TooManyNops();

    // Make sure all nops have been paid before removing nops
    // We only have to pay when there are nops and there is enough
    // outstanding NOP balance to trigger a payment.
    if (s_nopWeightsTotal > 0 && s_nopFeesJuels >= s_nopWeightsTotal) {
      payNops();
    }

    // Remove all previous nops, move from end to start to avoid shifting
    for (uint256 i = s_nops.length(); i > 0; --i) {
      (address nop, ) = s_nops.at(i - 1);
      s_nops.remove(nop);
    }

    // Add new
    uint32 nopWeightsTotal = 0;
    // nopWeightsTotal is bounded by the MAX_NUMBER_OF_NOPS and the weight of
    // a single nop being of type uint16. This ensures nopWeightsTotal will
    // always fit into the uint32 type.
    for (uint256 i = 0; i < numberOfNops; ++i) {
      // Make sure the LINK token is not a nop because the link token doesn't allow
      // self transfers. If set as nop, payNops would always revert. Since setNops
      // calls payNops, we can never remove the LINK token as a nop.
      address nop = nopsAndWeights[i].nop;
      uint16 weight = nopsAndWeights[i].weight;
      if (nop == i_linkToken || nop == address(0)) revert InvalidNopAddress(nop);
      s_nops.set(nop, weight);
      nopWeightsTotal += weight;
    }
    s_nopWeightsTotal = nopWeightsTotal;
    emit NopsSet(nopWeightsTotal, nopsAndWeights);
  }

  /// @notice Pays the Node Ops their outstanding balances.
  /// @dev some balance can remain after payments are done. This is at most the sum
  /// of the weight of all nops. Since nop weights are uint16s and we can have at
  /// most MAX_NUMBER_OF_NOPS NOPs, the highest possible value is 2**22 or 0.04 gjuels.
  function payNops() public onlyOwnerOrAdminOrNop {
    uint256 weightsTotal = s_nopWeightsTotal;
    if (weightsTotal == 0) revert NoNopsToPay();

    uint96 totalFeesToPay = s_nopFeesJuels;
    if (totalFeesToPay < weightsTotal) revert NoFeesToPay();
    if (_linkLeftAfterNopFees() < 0) revert InsufficientBalance();

    uint96 fundsLeft = totalFeesToPay;
    uint256 numberOfNops = s_nops.length();
    for (uint256 i = 0; i < numberOfNops; ++i) {
      (address nop, uint256 weight) = s_nops.at(i);
      // amount can never be higher than totalFeesToPay so the cast to uint96 is safe
      uint96 amount = uint96((totalFeesToPay * weight) / weightsTotal);
      fundsLeft -= amount;
      IERC20(i_linkToken).safeTransfer(nop, amount);
      emit NopPaid(nop, amount);
    }
    // Some funds can remain, since this is an incredibly small
    // amount we consider this OK.
    s_nopFeesJuels = fundsLeft;
  }

  /// @notice Allows the owner to withdraw any ERC20 token that is not the fee token
  /// @param feeToken The token to withdraw
  /// @param to The address to send the tokens to
  function withdrawNonLinkFees(address feeToken, address to) external onlyOwnerOrAdmin {
    if (feeToken == i_linkToken || to == address(0)) revert InvalidWithdrawParams();

    // We require the link balance to be settled before allowing withdrawal
    // of non-link fees.
    if (_linkLeftAfterNopFees() < 0) revert LinkBalanceNotSettled();

    IERC20(feeToken).safeTransfer(to, IERC20(feeToken).balanceOf(address(this)));
  }

  // ================================================================
  // │                        Link monitoring                       │
  // ================================================================

  /// @notice Calculate remaining LINK balance after paying nops
  /// @return balance if nops were to be paid
  function _linkLeftAfterNopFees() private view returns (int256) {
    // Since LINK caps at uint96, casting to int256 is safe
    return int256(IERC20(i_linkToken).balanceOf(address(this))) - int256(uint256(s_nopFeesJuels));
  }

  /// @notice Allow keeper to monitor funds available for paying nops
  function linkAvailableForPayment() external view returns (int256) {
    return _linkLeftAfterNopFees();
  }

  // ================================================================
  // │                        Access and ARM                        │
  // ================================================================

  /// @dev Require that the sender is the owner or the fee admin or a nop
  modifier onlyOwnerOrAdminOrNop() {
    if (msg.sender != owner() && msg.sender != s_admin && !s_nops.contains(msg.sender))
      revert OnlyCallableByOwnerOrAdminOrNop();
    _;
  }

  /// @dev Require that the sender is the owner or the fee admin
  modifier onlyOwnerOrAdmin() {
    if (msg.sender != owner() && msg.sender != s_admin) revert OnlyCallableByOwnerOrAdmin();
    _;
  }

  /// @notice Ensure that the ARM has not emitted a bad signal, and that the latest heartbeat is not stale.
  modifier whenHealthy() {
    if (IARM(i_armProxy).isCursed()) revert BadARMSignal();
    _;
  }
}

File 2 of 25 : ITypeAndVersion.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITypeAndVersion {
  function typeAndVersion() external pure returns (string memory);
}

File 3 of 25 : IPool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol";

// Shared public interface for multiple pool types.
// Each pool type handles a different child token model (lock/unlock, mint/burn.)
interface IPool {
  /// @notice Lock tokens into the pool or burn the tokens.
  /// @param originalSender Original sender of the tokens.
  /// @param receiver Receiver of the tokens on destination chain.
  /// @param amount Amount to lock or burn.
  /// @param destChainSelector Destination chain Id.
  /// @param extraArgs Additional data passed in by sender for lockOrBurn processing
  /// in custom pools on source chain.
  /// @return retData Optional field that contains bytes. Unused for now but already
  /// implemented to allow future upgrades while preserving the interface.
  function lockOrBurn(
    address originalSender,
    bytes calldata receiver,
    uint256 amount,
    uint64 destChainSelector,
    bytes calldata extraArgs
  ) external returns (bytes memory);

  /// @notice Releases or mints tokens to the receiver address.
  /// @param originalSender Original sender of the tokens.
  /// @param receiver Receiver of the tokens.
  /// @param amount Amount to release or mint.
  /// @param sourceChainSelector Source chain Id.
  /// @param extraData Additional data supplied offchain for releaseOrMint processing in
  /// custom pools on dest chain. This could be an attestation that was retrieved through a
  /// third party API.
  /// @dev offchainData can come from any untrusted source.
  function releaseOrMint(
    bytes memory originalSender,
    address receiver,
    uint256 amount,
    uint64 sourceChainSelector,
    bytes memory extraData
  ) external;

  /// @notice Gets the IERC20 token that this pool can lock or burn.
  /// @return token The IERC20 token representation.
  function getToken() external view returns (IERC20 token);
}

File 4 of 25 : IARM.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice This interface contains the only ARM-related functions that might be used on-chain by other CCIP contracts.
interface IARM {
  /// @notice A Merkle root tagged with the address of the commit store contract it is destined for.
  struct TaggedRoot {
    address commitStore;
    bytes32 root;
  }

  /// @notice Callers MUST NOT cache the return value as a blessed tagged root could become unblessed.
  function isBlessed(TaggedRoot calldata taggedRoot) external view returns (bool);

  /// @notice When the ARM is "cursed", CCIP pauses until the curse is lifted.
  function isCursed() external view returns (bool);
}

File 5 of 25 : IPriceRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

File 6 of 25 : IEVM2AnyOnRamp.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IEVM2AnyOnRampClient} from "./IEVM2AnyOnRampClient.sol";

import {Internal} from "../libraries/Internal.sol";

interface IEVM2AnyOnRamp is IEVM2AnyOnRampClient {
  /// @notice Gets the next sequence number to be used in the onRamp
  /// @return the next sequence number to be used
  function getExpectedNextSequenceNumber() external view returns (uint64);

  /// @notice Get the next nonce for a given sender
  /// @param sender The sender to get the nonce for
  /// @return nonce The next nonce for the sender
  function getSenderNonce(address sender) external view returns (uint64 nonce);

  /// @notice Adds and removed token pools.
  /// @param removes The tokens and pools to be removed
  /// @param adds The tokens and pools to be added.
  function applyPoolUpdates(Internal.PoolUpdate[] memory removes, Internal.PoolUpdate[] memory adds) external;
}

File 7 of 25 : IEVM2AnyOnRampClient.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPool} from "./pools/IPool.sol";

import {Client} from "../libraries/Client.sol";

import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol";

interface IEVM2AnyOnRampClient {
  /// @notice Get the fee for a given ccip message
  /// @param destChainSelector The destination chain selector
  /// @param message The message to calculate the cost for
  /// @return fee The calculated fee
  function getFee(uint64 destChainSelector, Client.EVM2AnyMessage calldata message) external view returns (uint256 fee);

  /// @notice Get the pool for a specific token
  /// @param destChainSelector The destination chain selector
  /// @param sourceToken The source chain token to get the pool for
  /// @return pool Token pool
  function getPoolBySourceToken(uint64 destChainSelector, IERC20 sourceToken) external view returns (IPool);

  /// @notice Gets a list of all supported source chain tokens.
  /// @param destChainSelector The destination chain selector
  /// @return tokens The addresses of all tokens that this onRamp supports the given destination chain
  function getSupportedTokens(uint64 destChainSelector) external view returns (address[] memory tokens);

  /// @notice Send a message to the remote chain
  /// @dev only callable by the Router
  /// @dev approve() must have already been called on the token using the this ramp address as the spender.
  /// @dev if the contract is paused, this function will revert.
  /// @param destChainSelector The destination chain selector
  /// @param message Message struct to send
  /// @param feeTokenAmount Amount of fee tokens for payment
  /// @param originalSender The original initiator of the CCIP request
  function forwardFromRouter(
    uint64 destChainSelector,
    Client.EVM2AnyMessage memory message,
    uint256 feeTokenAmount,
    address originalSender
  ) external returns (bytes32);
}

File 8 of 25 : ILinkAvailable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Implement this contract so that a keeper-compatible contract can monitor
/// and fund the implementation contract with LINK if it falls below a defined threshold.
interface ILinkAvailable {
  function linkAvailableForPayment() external view returns (int256 availableBalance);
}

File 9 of 25 : AggregateRateLimiter.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {IPriceRegistry} from "./interfaces/IPriceRegistry.sol";

import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol";
import {Client} from "./libraries/Client.sol";
import {RateLimiter} from "./libraries/RateLimiter.sol";
import {USDPriceWith18Decimals} from "./libraries/USDPriceWith18Decimals.sol";

/// @notice The aggregate rate limiter is a wrapper of the token bucket rate limiter
/// which permits rate limiting based on the aggregate value of a group of
/// token transfers, using a price registry to convert to a numeraire asset (e.g. USD).
contract AggregateRateLimiter is OwnerIsCreator {
  using RateLimiter for RateLimiter.TokenBucket;
  using USDPriceWith18Decimals for uint224;

  error PriceNotFoundForToken(address token);

  event AdminSet(address newAdmin);

  // The address of the token limit admin that has the same permissions as the owner.
  address internal s_admin;

  // The token bucket object that contains the bucket state.
  RateLimiter.TokenBucket private s_rateLimiter;

  /// @param config The RateLimiter.Config containing the capacity and refill rate
  /// of the bucket, plus the admin address.
  constructor(RateLimiter.Config memory config) {
    s_rateLimiter = RateLimiter.TokenBucket({
      rate: config.rate,
      capacity: config.capacity,
      tokens: config.capacity,
      lastUpdated: uint32(block.timestamp),
      isEnabled: config.isEnabled
    });
  }

  /// @notice Consumes value from the rate limiter bucket based on the
  /// token value given. First, calculate the prices
  function _rateLimitValue(Client.EVMTokenAmount[] memory tokenAmounts, IPriceRegistry priceRegistry) internal {
    uint256 numberOfTokens = tokenAmounts.length;

    uint256 value = 0;
    for (uint256 i = 0; i < numberOfTokens; ++i) {
      // not fetching validated price, as price staleness is not important for value-based rate limiting
      // we only need to verify price is not 0
      uint224 pricePerToken = priceRegistry.getTokenPrice(tokenAmounts[i].token).value;
      if (pricePerToken == 0) revert PriceNotFoundForToken(tokenAmounts[i].token);
      value += pricePerToken._calcUSDValueFromTokenAmount(tokenAmounts[i].amount);
    }

    s_rateLimiter._consume(value, address(0));
  }

  /// @notice Gets the token bucket with its values for the block it was requested at.
  /// @return The token bucket.
  function currentRateLimiterState() external view returns (RateLimiter.TokenBucket memory) {
    return s_rateLimiter._currentTokenBucketState();
  }

  /// @notice Sets the rate limited config.
  /// @param config The new rate limiter config.
  /// @dev should only be callable by the owner or token limit admin.
  function setRateLimiterConfig(RateLimiter.Config memory config) external onlyAdminOrOwner {
    s_rateLimiter._setTokenBucketConfig(config);
  }

  // ================================================================
  // │                           Access                             │
  // ================================================================

  /// @notice Gets the token limit admin address.
  /// @return the token limit admin address.
  function getTokenLimitAdmin() external view returns (address) {
    return s_admin;
  }

  /// @notice Sets the token limit admin address.
  /// @param newAdmin the address of the new admin.
  /// @dev setting this to address(0) indicates there is no active admin.
  function setAdmin(address newAdmin) external onlyAdminOrOwner {
    s_admin = newAdmin;
    emit AdminSet(newAdmin);
  }

  /// @notice a modifier that allows the owner or the s_tokenLimitAdmin call the functions
  /// it is applied to.
  modifier onlyAdminOrOwner() {
    if (msg.sender != owner() && msg.sender != s_admin) revert RateLimiter.OnlyCallableByAdminOrOwner();
    _;
  }
}

File 10 of 25 : Client.sol
// 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(EVMExtraArgsV1)
  }

  // bytes4(keccak256("CCIP EVMExtraArgsV1"));
  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);
  }
}

File 11 of 25 : Internal.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Client} from "./Client.sol";
import {MerkleMultiProof} from "../libraries/MerkleMultiProof.sol";

// Library for CCIP internal definitions common to multiple contracts.
library Internal {
  /// @dev The minimum amount of gas to perform the call with exact gas.
  /// We include this in the offramp so that we can redeploy to adjust it
  /// should a hardfork change the gas costs of relevant opcodes in callWithExactGas.
  uint16 internal constant GAS_FOR_CALL_EXACT_CHECK = 5_000;
  // @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;

  /// @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 smallest unit of token
  }

  /// @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 PoolUpdate {
    address token; // The IERC20 token address
    address pool; // The token pool address
  }

  /// @notice Report that is submitted by the execution DON at the execution phase.
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct ExecutionReport {
    EVM2EVMMessage[] messages;
    // Contains a bytes array for each message, each inner bytes array contains bytes per transferred token
    bytes[][] offchainTokenData;
    bytes32[] proofs;
    uint256 proofFlagBits;
  }

  /// @notice The cross chain message that gets committed to EVM chains.
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct EVM2EVMMessage {
    uint64 sourceChainSelector; // ─────────╮ the chain selector of the source chain, note: not chainId
    address sender; // ─────────────────────╯ sender address on the source chain
    address receiver; // ───────────────────╮ receiver address on the destination chain
    uint64 sequenceNumber; // ──────────────╯ sequence number, not unique across lanes
    uint256 gasLimit; //                      user supplied maximum gas amount available for dest chain execution
    bool strict; // ────────────────────────╮ DEPRECATED
    uint64 nonce; //                        │ nonce for this lane for this sender, not unique across senders/lanes
    address feeToken; // ───────────────────╯ fee token
    uint256 feeTokenAmount; //                fee token amount
    bytes data; //                            arbitrary data payload supplied by the message sender
    Client.EVMTokenAmount[] tokenAmounts; //  array of tokens and amounts to transfer
    bytes[] sourceTokenData; //               array of token pool return values, one per token
    bytes32 messageId; //                     a hash of the message data
  }

  /// @dev EVM2EVMMessage struct has 13 fields, including 3 variable arrays.
  /// Each variable array takes 1 more slot to store its length.
  /// When abi encoded, excluding array contents,
  /// EVM2EVMMessage takes up a fixed number of 16 lots, 32 bytes each.
  /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 17.
  uint256 public constant MESSAGE_FIXED_BYTES = 32 * 17;

  /// @dev Each token transfer adds 1 EVMTokenAmount and 1 bytes.
  /// When abiEncoded, each EVMTokenAmount takes 2 slots, each bytes takes 2 slots, excl bytes contents
  uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * 4;

  function _toAny2EVMMessage(
    EVM2EVMMessage memory original,
    Client.EVMTokenAmount[] memory destTokenAmounts
  ) internal pure returns (Client.Any2EVMMessage memory message) {
    message = Client.Any2EVMMessage({
      messageId: original.messageId,
      sourceChainSelector: original.sourceChainSelector,
      sender: abi.encode(original.sender),
      data: original.data,
      destTokenAmounts: destTokenAmounts
    });
  }

  bytes32 internal constant EVM_2_EVM_MESSAGE_HASH = keccak256("EVM2EVMMessageHashV2");

  function _hash(EVM2EVMMessage 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.receiver,
              original.sequenceNumber,
              original.gasLimit,
              original.strict,
              original.nonce,
              original.feeToken,
              original.feeTokenAmount
            )
          ),
          keccak256(original.data),
          keccak256(abi.encode(original.tokenAmounts)),
          keccak256(abi.encode(original.sourceTokenData))
        )
      );
  }

  /// @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
  }
}

File 12 of 25 : RateLimiter.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/// @notice Implements Token Bucket rate limiting.
/// @dev uint128 is safe for rate limiter state.
/// For USD value rate limiting, it can adequately store USD value in 18 decimals.
/// For ERC20 token amount rate limiting, all tokens that will be listed will have at most
/// a supply of uint128.max tokens, and it will therefore not overflow the bucket.
/// In exceptional scenarios where tokens consumed may be larger than uint128,
/// e.g. compromised issuer, an enabled RateLimiter will check and revert.
library RateLimiter {
  error BucketOverfilled();
  error OnlyCallableByAdminOrOwner();
  error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress);
  error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress);
  error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested);
  error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available);

  event TokensConsumed(uint256 tokens);
  event ConfigChanged(Config config);

  struct TokenBucket {
    uint128 tokens; // ──────╮ Current number of tokens that are in the bucket.
    uint32 lastUpdated; //   │ Timestamp in seconds of the last token refill, good for 100+ years.
    bool isEnabled; // ──────╯ Indication whether the rate limiting is enabled or not
    uint128 capacity; // ────╮ Maximum number of tokens that can be in the bucket.
    uint128 rate; // ────────╯ Number of tokens per second that the bucket is refilled.
  }

  struct Config {
    bool isEnabled; // Indication whether the rate limiting should be enabled
    uint128 capacity; // ────╮ Specifies the capacity of the rate limiter
    uint128 rate; //  ───────╯ Specifies the rate of the rate limiter
  }

  /// @notice _consume removes the given tokens from the pool, lowering the
  /// rate tokens allowed to be consumed for subsequent calls.
  /// @param requestTokens The total tokens to be consumed from the bucket.
  /// @param tokenAddress The token to consume capacity for, use 0x0 to indicate aggregate value capacity.
  /// @dev Reverts when requestTokens exceeds bucket capacity or available tokens in the bucket
  /// @dev emits removal of requestTokens if requestTokens is > 0
  function _consume(TokenBucket storage s_bucket, uint256 requestTokens, address tokenAddress) internal {
    // If there is no value to remove or rate limiting is turned off, skip this step to reduce gas usage
    if (!s_bucket.isEnabled || requestTokens == 0) {
      return;
    }

    uint256 tokens = s_bucket.tokens;
    uint256 capacity = s_bucket.capacity;
    uint256 timeDiff = block.timestamp - s_bucket.lastUpdated;

    if (timeDiff != 0) {
      if (tokens > capacity) revert BucketOverfilled();

      // Refill tokens when arriving at a new block time
      tokens = _calculateRefill(capacity, tokens, timeDiff, s_bucket.rate);

      s_bucket.lastUpdated = uint32(block.timestamp);
    }

    if (capacity < requestTokens) {
      // Token address 0 indicates consuming aggregate value rate limit capacity.
      if (tokenAddress == address(0)) revert AggregateValueMaxCapacityExceeded(capacity, requestTokens);
      revert TokenMaxCapacityExceeded(capacity, requestTokens, tokenAddress);
    }
    if (tokens < requestTokens) {
      uint256 rate = s_bucket.rate;
      // Wait required until the bucket is refilled enough to accept this value, round up to next higher second
      // Consume is not guaranteed to succeed after wait time passes if there is competing traffic.
      // This acts as a lower bound of wait time.
      uint256 minWaitInSeconds = ((requestTokens - tokens) + (rate - 1)) / rate;

      if (tokenAddress == address(0)) revert AggregateValueRateLimitReached(minWaitInSeconds, tokens);
      revert TokenRateLimitReached(minWaitInSeconds, tokens, tokenAddress);
    }
    tokens -= requestTokens;

    // Downcast is safe here, as tokens is not larger than capacity
    s_bucket.tokens = uint128(tokens);
    emit TokensConsumed(requestTokens);
  }

  /// @notice Gets the token bucket with its values for the block it was requested at.
  /// @return The token bucket.
  function _currentTokenBucketState(TokenBucket memory bucket) internal view returns (TokenBucket memory) {
    // We update the bucket to reflect the status at the exact time of the
    // call. This means we might need to refill a part of the bucket based
    // on the time that has passed since the last update.
    bucket.tokens = uint128(
      _calculateRefill(bucket.capacity, bucket.tokens, block.timestamp - bucket.lastUpdated, bucket.rate)
    );
    bucket.lastUpdated = uint32(block.timestamp);
    return bucket;
  }

  /// @notice Sets the rate limited config.
  /// @param s_bucket The token bucket
  /// @param config The new config
  function _setTokenBucketConfig(TokenBucket storage s_bucket, Config memory config) internal {
    // First update the bucket to make sure the proper rate is used for all the time
    // up until the config change.
    uint256 timeDiff = block.timestamp - s_bucket.lastUpdated;
    if (timeDiff != 0) {
      s_bucket.tokens = uint128(_calculateRefill(s_bucket.capacity, s_bucket.tokens, timeDiff, s_bucket.rate));

      s_bucket.lastUpdated = uint32(block.timestamp);
    }

    s_bucket.tokens = uint128(_min(config.capacity, s_bucket.tokens));
    s_bucket.isEnabled = config.isEnabled;
    s_bucket.capacity = config.capacity;
    s_bucket.rate = config.rate;

    emit ConfigChanged(config);
  }

  /// @notice Calculate refilled tokens
  /// @param capacity bucket capacity
  /// @param tokens current bucket tokens
  /// @param timeDiff block time difference since last refill
  /// @param rate bucket refill rate
  /// @return the value of tokens after refill
  function _calculateRefill(
    uint256 capacity,
    uint256 tokens,
    uint256 timeDiff,
    uint256 rate
  ) private pure returns (uint256) {
    return _min(capacity, tokens + timeDiff * rate);
  }

  /// @notice Return the smallest of two integers
  /// @param a first int
  /// @param b second int
  /// @return smallest
  function _min(uint256 a, uint256 b) internal pure returns (uint256) {
    return a < b ? a : b;
  }
}

File 13 of 25 : USDPriceWith18Decimals.sol
// 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;
  }
}

File 14 of 25 : EnumerableMapAddresses.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol";

library EnumerableMapAddresses {
  using EnumerableMap for EnumerableMap.UintToAddressMap;

  struct AddressToAddressMap {
    EnumerableMap.UintToAddressMap _inner;
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
    return map._inner.set(uint256(uint160(key)), value);
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function remove(AddressToAddressMap storage map, address key) internal returns (bool) {
    return map._inner.remove(uint256(uint160(key)));
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function contains(AddressToAddressMap storage map, address key) internal view returns (bool) {
    return map._inner.contains(uint256(uint160(key)));
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function length(AddressToAddressMap storage map) internal view returns (uint256) {
    return map._inner.length();
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function at(AddressToAddressMap storage map, uint256 index) internal view returns (address, address) {
    (uint256 key, address value) = map._inner.at(index);
    return (address(uint160(key)), value);
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool, address) {
    return map._inner.tryGet(uint256(uint160(key)));
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function get(AddressToAddressMap storage map, address key) internal view returns (address) {
    return map._inner.get(uint256(uint160(key)));
  }

  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function get(
    AddressToAddressMap storage map,
    address key,
    string memory errorMessage
  ) internal view returns (address) {
    return map._inner.get(uint256(uint160(key)), errorMessage);
  }
}

File 15 of 25 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
  using Address for address;

  function safeTransfer(IERC20 token, address to, uint256 value) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
  }

  function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
  }

  /**
   * @dev Deprecated. This function has issues similar to the ones found in
   * {IERC20-approve}, and its usage is discouraged.
   *
   * Whenever possible, use {safeIncreaseAllowance} and
   * {safeDecreaseAllowance} instead.
   */
  function safeApprove(IERC20 token, address spender, uint256 value) internal {
    // safeApprove should only be called when setting an initial allowance,
    // or when resetting it to zero. To increase and decrease it, use
    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
    require(
      (value == 0) || (token.allowance(address(this), spender) == 0),
      "SafeERC20: approve from non-zero to non-zero allowance"
    );
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
  }

  function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
    uint256 newAllowance = token.allowance(address(this), spender) + value;
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  }

  function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
    unchecked {
      uint256 oldAllowance = token.allowance(address(this), spender);
      require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
      uint256 newAllowance = oldAllowance - value;
      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }
  }

  function safePermit(
    IERC20Permit token,
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal {
    uint256 nonceBefore = token.nonces(owner);
    token.permit(owner, spender, value, deadline, v, r, s);
    uint256 nonceAfter = token.nonces(owner);
    require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
  }

  /**
   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
   * on the return value: the return value is optional (but if data is returned, it must not be false).
   * @param token The token targeted by the call.
   * @param data The call data (encoded using abi.encode or one of its variants).
   */
  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
    // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
    // the target address contains contract code and also asserts for success in the low-level call.

    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
    if (returndata.length > 0) {
      // Return data is optional
      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }
  }
}

File 16 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
  /**
   * @dev Emitted when `value` tokens are moved from one account (`from`) to
   * another (`to`).
   *
   * Note that `value` may be zero.
   */
  event Transfer(address indexed from, address indexed to, uint256 value);

  /**
   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
   * a call to {approve}. `value` is the new allowance.
   */
  event Approval(address indexed owner, address indexed spender, uint256 value);

  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @dev Returns the amount of tokens owned by `account`.
   */
  function balanceOf(address account) external view returns (uint256);

  /**
   * @dev Moves `amount` tokens from the caller's account to `to`.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transfer(address to, uint256 amount) external returns (bool);

  /**
   * @dev Returns the remaining number of tokens that `spender` will be
   * allowed to spend on behalf of `owner` through {transferFrom}. This is
   * zero by default.
   *
   * This value changes when {approve} or {transferFrom} are called.
   */
  function allowance(address owner, address spender) external view returns (uint256);

  /**
   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * IMPORTANT: Beware that changing an allowance with this method brings the risk
   * that someone may use both the old and the new allowance by unfortunate
   * transaction ordering. One possible solution to mitigate this race
   * condition is to first reduce the spender's allowance to 0 and set the
   * desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   *
   * Emits an {Approval} event.
   */
  function approve(address spender, uint256 amount) external returns (bool);

  /**
   * @dev Moves `amount` tokens from `from` to `to` using the
   * allowance mechanism. `amount` is then deducted from the caller's
   * allowance.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 17 of 25 : EnumerableMap.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.

pragma solidity ^0.8.0;

import "./EnumerableSet.sol";

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * The following map types are supported:
 *
 * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
 *
 * [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 EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableMap.
 * ====
 */
library EnumerableMap {
  using EnumerableSet for EnumerableSet.Bytes32Set;

  // To implement this library for multiple types with as little code
  // repetition as possible, we write it in terms of a generic Map type with
  // bytes32 keys and values.
  // The Map implementation uses private functions, and user-facing
  // implementations (such as Uint256ToAddressMap) are just wrappers around
  // the underlying Map.
  // This means that we can only create new EnumerableMaps for types that fit
  // in bytes32.

  struct Bytes32ToBytes32Map {
    // Storage of keys
    EnumerableSet.Bytes32Set _keys;
    mapping(bytes32 => bytes32) _values;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
   * key. O(1).
   *
   * Returns true if the key was added to the map, that is if it was not
   * already present.
   */
  function set(
    Bytes32ToBytes32Map storage map,
    bytes32 key,
    bytes32 value
  ) internal returns (bool) {
    map._values[key] = value;
    return map._keys.add(key);
  }

  /**
   * @dev Removes a key-value pair from a map. O(1).
   *
   * Returns true if the key was removed from the map, that is if it was present.
   */
  function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
    delete map._values[key];
    return map._keys.remove(key);
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
   */
  function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
    return map._keys.contains(key);
  }

  /**
   * @dev Returns the number of key-value pairs in the map. O(1).
   */
  function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
    return map._keys.length();
  }

  /**
   * @dev Returns the key-value pair stored at position `index` in the map. O(1).
   *
   * Note that there are no guarantees on the ordering of entries inside the
   * array, and it may change when more entries are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
    bytes32 key = map._keys.at(index);
    return (key, map._values[key]);
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
   * Does not revert if `key` is not in the map.
   */
  function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
    bytes32 value = map._values[key];
    if (value == bytes32(0)) {
      return (contains(map, key), bytes32(0));
    } else {
      return (true, value);
    }
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
   *
   * Requirements:
   *
   * - `key` must be in the map.
   */
  function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
    bytes32 value = map._values[key];
    require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
    return value;
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
   *
   * CAUTION: This function is deprecated because it requires allocating memory for the error
   * message unnecessarily. For custom revert reasons use {tryGet}.
   */
  function get(
    Bytes32ToBytes32Map storage map,
    bytes32 key,
    string memory errorMessage
  ) internal view returns (bytes32) {
    bytes32 value = map._values[key];
    require(value != 0 || contains(map, key), errorMessage);
    return value;
  }

  // UintToUintMap

  struct UintToUintMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
   * key. O(1).
   *
   * Returns true if the key was added to the map, that is if it was not
   * already present.
   */
  function set(
    UintToUintMap storage map,
    uint256 key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, bytes32(key), bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the key was removed from the map, that is if it was present.
   */
  function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
    return remove(map._inner, bytes32(key));
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
   */
  function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
    return contains(map._inner, bytes32(key));
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
   */
  function length(UintToUintMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element 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(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (uint256(key), uint256(value));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
   * Does not revert if `key` is not in the map.
   */
  function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
    return (success, uint256(value));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
   *
   * Requirements:
   *
   * - `key` must be in the map.
   */
  function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(key)));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
   *
   * CAUTION: This function is deprecated because it requires allocating memory for the error
   * message unnecessarily. For custom revert reasons use {tryGet}.
   */
  function get(
    UintToUintMap storage map,
    uint256 key,
    string memory errorMessage
  ) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(key), errorMessage));
  }

  // UintToAddressMap

  struct UintToAddressMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
   * key. O(1).
   *
   * Returns true if the key was added to the map, that is if it was not
   * already present.
   */
  function set(
    UintToAddressMap storage map,
    uint256 key,
    address value
  ) internal returns (bool) {
    return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the key was removed from the map, that is if it was present.
   */
  function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
    return remove(map._inner, bytes32(key));
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
   */
  function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
    return contains(map._inner, bytes32(key));
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
   */
  function length(UintToAddressMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element 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(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (uint256(key), address(uint160(uint256(value))));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
   * Does not revert if `key` is not in the map.
   */
  function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
    return (success, address(uint160(uint256(value))));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
   *
   * Requirements:
   *
   * - `key` must be in the map.
   */
  function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
    return address(uint160(uint256(get(map._inner, bytes32(key)))));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
   *
   * CAUTION: This function is deprecated because it requires allocating memory for the error
   * message unnecessarily. For custom revert reasons use {tryGet}.
   */
  function get(
    UintToAddressMap storage map,
    uint256 key,
    string memory errorMessage
  ) internal view returns (address) {
    return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
  }

  // AddressToUintMap

  struct AddressToUintMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
   * key. O(1).
   *
   * Returns true if the key was added to the map, that is if it was not
   * already present.
   */
  function set(
    AddressToUintMap storage map,
    address key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the key was removed from the map, that is if it was present.
   */
  function remove(AddressToUintMap storage map, address key) internal returns (bool) {
    return remove(map._inner, bytes32(uint256(uint160(key))));
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
   */
  function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
    return contains(map._inner, bytes32(uint256(uint160(key))));
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
   */
  function length(AddressToUintMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element 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(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (address(uint160(uint256(key))), uint256(value));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
   * Does not revert if `key` is not in the map.
   */
  function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
    (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
    return (success, uint256(value));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
   *
   * Requirements:
   *
   * - `key` must be in the map.
   */
  function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
   *
   * CAUTION: This function is deprecated because it requires allocating memory for the error
   * message unnecessarily. For custom revert reasons use {tryGet}.
   */
  function get(
    AddressToUintMap storage map,
    address key,
    string memory errorMessage
  ) internal view returns (uint256) {
    return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
  }

  // Bytes32ToUintMap

  struct Bytes32ToUintMap {
    Bytes32ToBytes32Map _inner;
  }

  /**
   * @dev Adds a key-value pair to a map, or updates the value for an existing
   * key. O(1).
   *
   * Returns true if the key was added to the map, that is if it was not
   * already present.
   */
  function set(
    Bytes32ToUintMap storage map,
    bytes32 key,
    uint256 value
  ) internal returns (bool) {
    return set(map._inner, key, bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the key was removed from the map, that is if it was present.
   */
  function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
    return remove(map._inner, key);
  }

  /**
   * @dev Returns true if the key is in the map. O(1).
   */
  function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
    return contains(map._inner, key);
  }

  /**
   * @dev Returns the number of elements in the map. O(1).
   */
  function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
    return length(map._inner);
  }

  /**
   * @dev Returns the element 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(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
    (bytes32 key, bytes32 value) = at(map._inner, index);
    return (key, uint256(value));
  }

  /**
   * @dev Tries to returns the value associated with `key`. O(1).
   * Does not revert if `key` is not in the map.
   */
  function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
    (bool success, bytes32 value) = tryGet(map._inner, key);
    return (success, uint256(value));
  }

  /**
   * @dev Returns the value associated with `key`. O(1).
   *
   * Requirements:
   *
   * - `key` must be in the map.
   */
  function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
    return uint256(get(map._inner, key));
  }

  /**
   * @dev Same as {get}, with a custom error message when `key` is not in the map.
   *
   * CAUTION: This function is deprecated because it requires allocating memory for the error
   * message unnecessarily. For custom revert reasons use {tryGet}.
   */
  function get(
    Bytes32ToUintMap storage map,
    bytes32 key,
    string memory errorMessage
  ) internal view returns (uint256) {
    return uint256(get(map._inner, key, errorMessage));
  }
}

File 18 of 25 : OwnerIsCreator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ConfirmedOwner} from "./ConfirmedOwner.sol";

/// @title The OwnerIsCreator contract
/// @notice A contract with helpers for basic contract ownership.
contract OwnerIsCreator is ConfirmedOwner {
  constructor() ConfirmedOwner(msg.sender) {}
}

File 19 of 25 : MerkleMultiProof.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

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 preiimage.
  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 The 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.
  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  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);
  }
}

File 20 of 25 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
  /**
   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
   * given ``owner``'s signed approval.
   *
   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
   * ordering also apply here.
   *
   * Emits an {Approval} event.
   *
   * Requirements:
   *
   * - `spender` cannot be the zero address.
   * - `deadline` must be a timestamp in the future.
   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
   * over the EIP712-formatted function arguments.
   * - the signature must use ``owner``'s current nonce (see {nonces}).
   *
   * For more information on the signature format, see the
   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
   * section].
   */
  function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @dev Returns the current nonce for `owner`. This value must be
   * included whenever a signature is generated for {permit}.
   *
   * Every successful call to {permit} increases ``owner``'s nonce by one. This
   * prevents a signature from being used multiple times.
   */
  function nonces(address owner) external view returns (uint256);

  /**
   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
   */
  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 21 of 25 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   *
   * [IMPORTANT]
   * ====
   * You shouldn't rely on `isContract` to protect against flash loan attacks!
   *
   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
   * constructor.
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // This method relies on extcodesize/address.code.length, which returns 0
    // for contracts in construction, since the code is only stored at the end
    // of the constructor execution.

    return account.code.length > 0;
  }

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

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

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

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

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

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

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

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal view returns (bytes memory) {
    (bool success, bytes memory returndata) = target.staticcall(data);
    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
  }

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

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a delegate call.
   *
   * _Available since v3.4._
   */
  function functionDelegateCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    (bool success, bytes memory returndata) = target.delegatecall(data);
    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
  }

  /**
   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
   *
   * _Available since v4.8._
   */
  function verifyCallResultFromTarget(
    address target,
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) internal view returns (bytes memory) {
    if (success) {
      if (returndata.length == 0) {
        // only check isContract if the call was successful and the return data is empty
        // otherwise we already know that it was a contract
        require(isContract(target), "Address: call to non-contract");
      }
      return returndata;
    } else {
      _revert(returndata, errorMessage);
    }
  }

  /**
   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
   * revert reason or using the provided one.
   *
   * _Available since v4.3._
   */
  function verifyCallResult(
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) internal pure returns (bytes memory) {
    if (success) {
      return returndata;
    } else {
      _revert(returndata, errorMessage);
    }
  }

  function _revert(bytes memory returndata, string memory errorMessage) private pure {
    // Look for revert reason and bubble it up if present
    if (returndata.length > 0) {
      // The easiest way to bubble the revert reason is using memory via assembly
      /// @solidity memory-safe-assembly
      assembly {
        let returndata_size := mload(returndata)
        revert(add(32, returndata), returndata_size)
      }
    } else {
      revert(errorMessage);
    }
  }
}

File 22 of 25 : EnumerableSet.sol
// 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;
  }
}

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

import "./ConfirmedOwnerWithProposal.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
  constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}

File 24 of 25 : ConfirmedOwnerWithProposal.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../interfaces/IOwnable.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwnerWithProposal is IOwnable {
  address private s_owner;
  address private s_pendingOwner;

  event OwnershipTransferRequested(address indexed from, address indexed to);
  event OwnershipTransferred(address indexed from, address indexed to);

  constructor(address newOwner, address pendingOwner) {
    require(newOwner != address(0), "Cannot set owner to zero");

    s_owner = newOwner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /**
   * @notice Allows an owner to begin transferring ownership to a new address,
   * pending.
   */
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /**
   * @notice Allows an ownership transfer to be completed by the recipient.
   */
  function acceptOwnership() external override {
    require(msg.sender == s_pendingOwner, "Must be proposed owner");

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /**
   * @notice Get the current owner
   */
  function owner() public view override returns (address) {
    return s_owner;
  }

  /**
   * @notice validate, transfer ownership, and emit relevant events
   */
  function _transferOwnership(address to) private {
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /**
   * @notice validate access
   */
  function _validateOwnership() internal view {
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /**
   * @notice Reverts if called by anyone other than the contract owner.
   */
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}

File 25 of 25 : IOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IOwnable {
  function owner() external returns (address);

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

Settings
{
  "remappings": [
    "ds-test/=foundry-lib/forge-std/lib/ds-test/src/",
    "forge-std/=foundry-lib/forge-std/src/",
    "openzeppelin-contracts/=foundry-lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "hardhat/=node_modules/hardhat/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "erc4626-tests/=foundry-lib/openzeppelin-contracts/lib/erc4626-tests/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract ABI

[{"inputs":[{"components":[{"internalType":"address","name":"linkToken","type":"address"},{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"uint64","name":"defaultTxGasLimit","type":"uint64"},{"internalType":"uint96","name":"maxNopFeesJuels","type":"uint96"},{"internalType":"address","name":"prevOnRamp","type":"address"},{"internalType":"address","name":"armProxy","type":"address"}],"internalType":"struct EVM2EVMOnRamp.StaticConfig","name":"staticConfig","type":"tuple"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint16","name":"destGasPerPayloadByte","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"address","name":"priceRegistry","type":"address"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"}],"internalType":"struct EVM2EVMOnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pool","type":"address"}],"internalType":"struct Internal.PoolUpdate[]","name":"tokensAndPools","type":"tuple[]"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"rateLimiterConfig","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"},{"internalType":"bool","name":"enabled","type":"bool"}],"internalType":"struct EVM2EVMOnRamp.FeeTokenConfigArgs[]","name":"feeTokenConfigs","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"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":"struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]","name":"tokenTransferFeeConfigArgs","type":"tuple[]"},{"components":[{"internalType":"address","name":"nop","type":"address"},{"internalType":"uint16","name":"weight","type":"uint16"}],"internalType":"struct EVM2EVMOnRamp.NopAndWeight[]","name":"nopsAndWeights","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"AggregateValueMaxCapacityExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"minWaitInSeconds","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"AggregateValueRateLimitReached","type":"error"},{"inputs":[],"name":"BadARMSignal","type":"error"},{"inputs":[],"name":"BucketOverfilled","type":"error"},{"inputs":[],"name":"CannotSendZeroTokens","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"bytes","name":"encodedAddress","type":"bytes"}],"name":"InvalidAddress","type":"error"},{"inputs":[{"internalType":"uint64","name":"chainSelector","type":"uint64"}],"name":"InvalidChainSelector","type":"error"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[],"name":"InvalidExtraArgsTag","type":"error"},{"inputs":[{"internalType":"address","name":"nop","type":"address"}],"name":"InvalidNopAddress","type":"error"},{"inputs":[],"name":"InvalidTokenPoolConfig","type":"error"},{"inputs":[],"name":"InvalidWithdrawParams","type":"error"},{"inputs":[],"name":"LinkBalanceNotSettled","type":"error"},{"inputs":[],"name":"MaxFeeBalanceReached","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":"MustBeCalledByRouter","type":"error"},{"inputs":[],"name":"NoFeesToPay","type":"error"},{"inputs":[],"name":"NoNopsToPay","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"NotAFeeToken","type":"error"},{"inputs":[],"name":"OnlyCallableByAdminOrOwner","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdminOrNop","type":"error"},{"inputs":[],"name":"PoolAlreadyAdded","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"PoolDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"PriceNotFoundForToken","type":"error"},{"inputs":[],"name":"RouterMustSetOriginalSender","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SourceTokenDataTooLarge","type":"error"},{"inputs":[{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenMaxCapacityExceeded","type":"error"},{"inputs":[],"name":"TokenPoolMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"minWaitInSeconds","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenRateLimitReached","type":"error"},{"inputs":[],"name":"TooManyNops","type":"error"},{"inputs":[],"name":"UnsupportedNumberOfTokens","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"UnsupportedToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminSet","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bool","name":"strict","type":"bool"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"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":"bytes[]","name":"sourceTokenData","type":"bytes[]"},{"internalType":"bytes32","name":"messageId","type":"bytes32"}],"indexed":false,"internalType":"struct Internal.EVM2EVMMessage","name":"message","type":"tuple"}],"name":"CCIPSendRequested","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"linkToken","type":"address"},{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"uint64","name":"defaultTxGasLimit","type":"uint64"},{"internalType":"uint96","name":"maxNopFeesJuels","type":"uint96"},{"internalType":"address","name":"prevOnRamp","type":"address"},{"internalType":"address","name":"armProxy","type":"address"}],"indexed":false,"internalType":"struct EVM2EVMOnRamp.StaticConfig","name":"staticConfig","type":"tuple"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint16","name":"destGasPerPayloadByte","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"address","name":"priceRegistry","type":"address"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"}],"indexed":false,"internalType":"struct EVM2EVMOnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"},{"internalType":"bool","name":"enabled","type":"bool"}],"indexed":false,"internalType":"struct EVM2EVMOnRamp.FeeTokenConfigArgs[]","name":"feeConfig","type":"tuple[]"}],"name":"FeeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nop","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NopPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nopWeightsTotal","type":"uint256"},{"components":[{"internalType":"address","name":"nop","type":"address"},{"internalType":"uint16","name":"weight","type":"uint16"}],"indexed":false,"internalType":"struct EVM2EVMOnRamp.NopAndWeight[]","name":"nopsAndWeights","type":"tuple[]"}],"name":"NopsSet","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":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"}],"name":"PoolRemoved","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"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"}],"indexed":false,"internalType":"struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]","name":"transferFeeConfig","type":"tuple[]"}],"name":"TokenTransferFeeConfigSet","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pool","type":"address"}],"internalType":"struct Internal.PoolUpdate[]","name":"removes","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pool","type":"address"}],"internalType":"struct Internal.PoolUpdate[]","name":"adds","type":"tuple[]"}],"name":"applyPoolUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentRateLimiterState","outputs":[{"components":[{"internalType":"uint128","name":"tokens","type":"uint128"},{"internalType":"uint32","name":"lastUpdated","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.TokenBucket","name":"","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"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"internalType":"address","name":"originalSender","type":"address"}],"name":"forwardFromRouter","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDynamicConfig","outputs":[{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint16","name":"destGasPerPayloadByte","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"address","name":"priceRegistry","type":"address"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"}],"internalType":"struct EVM2EVMOnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExpectedNextSequenceNumber","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"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":"getFee","outputs":[{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getFeeTokenConfig","outputs":[{"components":[{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"},{"internalType":"bool","name":"enabled","type":"bool"}],"internalType":"struct EVM2EVMOnRamp.FeeTokenConfig","name":"feeTokenConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNopFeesJuels","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNops","outputs":[{"components":[{"internalType":"address","name":"nop","type":"address"},{"internalType":"uint16","name":"weight","type":"uint16"}],"internalType":"struct EVM2EVMOnRamp.NopAndWeight[]","name":"nopsAndWeights","type":"tuple[]"},{"internalType":"uint256","name":"weightsTotal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"contract IERC20","name":"sourceToken","type":"address"}],"name":"getPoolBySourceToken","outputs":[{"internalType":"contract IPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"getSenderNonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticConfig","outputs":[{"components":[{"internalType":"address","name":"linkToken","type":"address"},{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"uint64","name":"defaultTxGasLimit","type":"uint64"},{"internalType":"uint96","name":"maxNopFeesJuels","type":"uint96"},{"internalType":"address","name":"prevOnRamp","type":"address"},{"internalType":"address","name":"armProxy","type":"address"}],"internalType":"struct EVM2EVMOnRamp.StaticConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"getSupportedTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenLimitAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"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":"struct EVM2EVMOnRamp.TokenTransferFeeConfig","name":"tokenTransferFeeConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkAvailableForPayment","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payNops","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"uint16","name":"maxNumberOfTokensPerMsg","type":"uint16"},{"internalType":"uint32","name":"destGasOverhead","type":"uint32"},{"internalType":"uint16","name":"destGasPerPayloadByte","type":"uint16"},{"internalType":"uint32","name":"destDataAvailabilityOverheadGas","type":"uint32"},{"internalType":"uint16","name":"destGasPerDataAvailabilityByte","type":"uint16"},{"internalType":"uint16","name":"destDataAvailabilityMultiplierBps","type":"uint16"},{"internalType":"address","name":"priceRegistry","type":"address"},{"internalType":"uint32","name":"maxDataBytes","type":"uint32"},{"internalType":"uint32","name":"maxPerMsgGasLimit","type":"uint32"}],"internalType":"struct EVM2EVMOnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"name":"setDynamicConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"networkFeeUSDCents","type":"uint32"},{"internalType":"uint64","name":"gasMultiplierWeiPerEth","type":"uint64"},{"internalType":"uint64","name":"premiumMultiplierWeiPerEth","type":"uint64"},{"internalType":"bool","name":"enabled","type":"bool"}],"internalType":"struct EVM2EVMOnRamp.FeeTokenConfigArgs[]","name":"feeTokenConfigArgs","type":"tuple[]"}],"name":"setFeeTokenConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"nop","type":"address"},{"internalType":"uint16","name":"weight","type":"uint16"}],"internalType":"struct EVM2EVMOnRamp.NopAndWeight[]","name":"nopsAndWeights","type":"tuple[]"}],"name":"setNops","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"config","type":"tuple"}],"name":"setRateLimiterConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"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":"struct EVM2EVMOnRamp.TokenTransferFeeConfigArgs[]","name":"tokenTransferFeeConfigArgs","type":"tuple[]"}],"name":"setTokenTransferFeeConfig","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawNonLinkFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101806040523480156200001257600080fd5b506040516200849a3803806200849a833981016040819052620000359162001e40565b8333806000816200008d5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c057620000c08162000323565b50506040805160a081018252602084810180516001600160801b039081168085524263ffffffff169385018490528751151585870181905292518216606086018190529790950151166080909301839052600380546001600160a01b031916909417600160801b9283021760ff60a01b1916600160a01b90910217909255029091176004555086516001600160a01b0316158062000169575060208701516001600160401b0316155b8062000180575060408701516001600160401b0316155b8062000197575060608701516001600160401b0316155b80620001ae575060c08701516001600160a01b0316155b15620001cd576040516306b7c75960e31b815260040160405180910390fd5b6020808801516040808a015181517f8acd72527118c8324937b1a42e02cd246697c3b633f1742f3cae11de233722b3948101949094526001600160401b039283169184019190915216606082015230608082015260a00160408051601f198184030181529181528151602092830120608090815289516001600160a01b0390811660e052928a01516001600160401b0390811661010052918a015182166101205260608a015190911660a0908152908901516001600160601b031660c0908152908901518216610140528801511661016052620002aa86620003ce565b620002b58362000645565b620002c0826200077f565b620002cb81620008e5565b6040805160008082526020820190925262000316916200030e565b6040805180820190915260008082526020820152815260200190600190039081620002e65790505b508662000b0f565b505050505050506200241a565b336001600160a01b038216036200037d5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000084565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60e08101516001600160a01b0316620003fa576040516306b7c75960e31b815260040160405180910390fd5b80600560008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548161ffff021916908361ffff16021790555060408201518160000160166101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001601a6101000a81548161ffff021916908361ffff160217905550608082015181600001601c6101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160006101000a81548161ffff021916908361ffff16021790555060c08201518160010160026101000a81548161ffff021916908361ffff16021790555060e08201518160010160046101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555061012082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055509050507f2a57f7c2027cf032c78b77d4d8d2fbd20ad22e5d5e5b5fb23ac7d7820d44adc66040518060e0016040528060e0516001600160a01b03168152602001610100516001600160401b03168152602001610120516001600160401b0316815260200160a0516001600160401b0316815260200160c0516001600160601b03168152602001610140516001600160a01b03168152602001610160516001600160a01b0316815250826040516200063a92919062002015565b60405180910390a150565b60005b81518110156200074d57600082828151811062000669576200066962002095565b60209081029190910181015160408051608080820183528385015163ffffffff9081168352838501516001600160401b03908116848801908152606080880151831686880190815294880151151590860190815296516001600160a01b03166000908152600d90985294909620925183549451925195511515600160a01b0260ff60a01b199688166c010000000000000000000000000296909616600160601b600160a81b031993909716640100000000026001600160601b0319909516911617929092179190911692909217179055506200074581620020c1565b905062000648565b507f067924bf9277d905a9a4631a06d959bc032ace86b3caa835ae7e403d4f39010e816040516200063a9190620020dd565b60005b8151811015620008b3576000828281518110620007a357620007a362002095565b6020908102919091018101516040805160a080820183528385015163ffffffff908116835283850151811683870190815260608087015161ffff9081168688019081526080808a0151861693880193845295890151851695870195865297516001600160a01b03166000908152600e909952959097209351845491519651975193518316600160701b0263ffffffff60701b199484166a01000000000000000000000263ffffffff60501b199990971668010000000000000000029890981665ffffffffffff60401b19978416640100000000026001600160401b03199093169190931617179490941693909317919091179190911691909117905550620008ab81620020c1565b905062000782565b507f555c74101f7a15746d31c6731170310e667bcc607996b2fc0b981a7b26a416e9816040516200063a91906200216c565b805160408111156200090a57604051635ad0867d60e11b815260040160405180910390fd5b6010546c01000000000000000000000000900463ffffffff161580159062000954575060105463ffffffff6c010000000000000000000000008204166001600160601b0390911610155b1562000964576200096462000e12565b600062000972600762001012565b90505b8015620009be576000620009986200098f600184620021f3565b60079062001025565b509050620009a860078262001043565b505080620009b69062002209565b905062000975565b506000805b8281101562000aa6576000848281518110620009e357620009e362002095565b6020026020010151600001519050600085838151811062000a085762000a0862002095565b602002602001015160200151905060e0516001600160a01b0316826001600160a01b0316148062000a4057506001600160a01b038216155b1562000a6b57604051634de938d160e01b81526001600160a01b038316600482015260240162000084565b62000a7d60078361ffff841662001061565b5062000a8e61ffff82168562002223565b935050508062000a9e90620020c1565b9050620009c3565b506010805463ffffffff60601b19166c0100000000000000000000000063ffffffff8416021790556040517f8c337bff38141c507abd25c547606bdde78fe8c12e941ab613f3a565fea6cd249062000b02908390869062002243565b60405180910390a1505050565b60005b825181101562000c4b57600083828151811062000b335762000b3362002095565b6020026020010151600001519050600084838151811062000b585762000b5862002095565b6020908102919091018101510151905062000b75600a8362001081565b62000b9f576040516373913ebd60e01b81526001600160a01b038316600482015260240162000084565b6001600160a01b03811662000bb6600a8462001098565b6001600160a01b03161462000bde57604051630d98f73360e31b815260040160405180910390fd5b62000beb600a83620010af565b1562000c3557604080516001600160a01b038085168252831660208201527f987eb3c2f78454541205f72f34839b434c306c9eaf4922efd7c0c3060fdb2e4c910160405180910390a15b50508062000c4390620020c1565b905062000b12565b5060005b815181101562000e0d57600082828151811062000c705762000c7062002095565b6020026020010151600001519050600083838151811062000c955762000c9562002095565b602002602001015160200151905060006001600160a01b0316826001600160a01b0316148062000ccc57506001600160a01b038116155b1562000cea5760405162d8548360e71b815260040160405180910390fd5b806001600160a01b03166321df0da76040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000d29573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d4f9190620022b0565b6001600160a01b0316826001600160a01b03161462000d8157604051630d98f73360e31b815260040160405180910390fd5b62000d8f600a8383620010c6565b1562000dde57604080516001600160a01b038085168252831660208201527f95f865c2808f8b2a85eea2611db7843150ee7835ef1403f9755918a97d76933c910160405180910390a162000df7565b604051633caf458560e01b815260040160405180910390fd5b50508062000e0590620020c1565b905062000c4f565b505050565b6000546001600160a01b0316331480159062000e3957506002546001600160a01b03163314155b801562000e50575062000e4e600733620010de565b155b1562000e6f5760405163032bb72b60e31b815260040160405180910390fd5b6010546c01000000000000000000000000900463ffffffff16600081900362000eab5760405163990e30bf60e01b815260040160405180910390fd5b6010546001600160601b03168181101562000ed9576040516311a1ee3b60e31b815260040160405180910390fd5b600062000ee5620010f5565b121562000f0557604051631e9acf1760e31b815260040160405180910390fd5b80600062000f14600762001012565b905060005b8181101562000fec5760008062000f3260078462001025565b909250905060008762000f4f836001600160601b038a16620022d0565b62000f5b9190620022ea565b905062000f6981876200230d565b60e05190965062000f8e906001600160a01b0316846001600160601b03841662001183565b6040516001600160601b03821681526001600160a01b038416907f55fdec2aab60a41fa5abb106670eb1006f5aeaee1ba7afea2bc89b5b3ec7678f9060200160405180910390a25050508062000fe490620020c1565b905062000f19565b5050601080546001600160601b0319166001600160601b03929092169190911790555050565b60006200101f82620011db565b92915050565b6000808080620010368686620011e8565b9097909650945050505050565b60006200105a836001600160a01b03841662001215565b9392505050565b600062001079846001600160a01b0385168462001234565b949350505050565b60006200105a836001600160a01b03841662001253565b60006200105a836001600160a01b03841662001261565b60006200105a836001600160a01b0384166200126f565b600062001079846001600160a01b038516846200127d565b60006200105a836001600160a01b03841662001295565b60105460e0516040516370a0823160e01b81523060048201526000926001600160601b0316916001600160a01b0316906370a0823190602401602060405180830381865afa1580156200114c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001172919062002330565b6200117e91906200234a565b905090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663a9059cbb60e01b1790915262000e0d918591620012a316565b60006200101f8262001374565b60008080620011f885856200137f565b600081815260029690960160205260409095205494959350505050565b600081815260028301602052604081208190556200105a83836200138d565b600082815260028401602052604081208290556200107984846200139b565b60006200105a838362001295565b60006200105a8383620013a9565b60006200105a838362001215565b60006200107984846001600160a01b03851662001234565b60006200105a83836200141e565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656490820152600090620012f2906001600160a01b03851690849062001437565b80519091501562000e0d57808060200190518101906200131391906200236d565b62000e0d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000084565b60006200101f825490565b60006200105a838362001448565b60006200105a838362001475565b60006200105a838362001580565b600081815260028301602052604081205480151580620013d05750620013d0848462001295565b6200105a5760405162461bcd60e51b815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b65790000604482015260640162000084565b600081815260018301602052604081205415156200105a565b6060620010798484600085620015d2565b600082600001828154811062001462576200146262002095565b9060005260206000200154905092915050565b600081815260018301602052604081205480156200156e5760006200149c600183620021f3565b8554909150600090620014b290600190620021f3565b90508181146200151e576000866000018281548110620014d657620014d662002095565b9060005260206000200154905080876000018481548110620014fc57620014fc62002095565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806200153257620015326200238b565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506200101f565b60009150506200101f565b5092915050565b6000818152600183016020526040812054620015c9575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200101f565b5060006200101f565b606082471015620016355760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000084565b600080866001600160a01b03168587604051620016539190620023c7565b60006040518083038185875af1925050503d806000811462001692576040519150601f19603f3d011682016040523d82523d6000602084013e62001697565b606091505b509092509050620016ab87838387620016b6565b979650505050505050565b606083156200172a57825160000362001722576001600160a01b0385163b620017225760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000084565b508162001079565b620010798383815115620017415781518083602001fd5b8060405162461bcd60e51b8152600401620000849190620023e5565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b03811182821017156200179857620017986200175d565b60405290565b60405161014081016001600160401b03811182821017156200179857620017986200175d565b604080519081016001600160401b03811182821017156200179857620017986200175d565b60405160a081016001600160401b03811182821017156200179857620017986200175d565b60405160c081016001600160401b03811182821017156200179857620017986200175d565b604051601f8201601f191681016001600160401b03811182821017156200185e576200185e6200175d565b604052919050565b6001600160a01b03811681146200187c57600080fd5b50565b80516200188c8162001866565b919050565b80516001600160401b03811681146200188c57600080fd5b600060e08284031215620018bc57600080fd5b620018c662001773565b90508151620018d58162001866565b8152620018e56020830162001891565b6020820152620018f86040830162001891565b60408201526200190b6060830162001891565b606082015260808201516001600160601b03811681146200192b57600080fd5b60808201526200193e60a083016200187f565b60a08201526200195160c083016200187f565b60c082015292915050565b805161ffff811681146200188c57600080fd5b805163ffffffff811681146200188c57600080fd5b600061014082840312156200199857600080fd5b620019a26200179e565b9050620019af826200187f565b8152620019bf602083016200195c565b6020820152620019d2604083016200196f565b6040820152620019e5606083016200195c565b6060820152620019f8608083016200196f565b608082015262001a0b60a083016200195c565b60a082015262001a1e60c083016200195c565b60c082015262001a3160e083016200187f565b60e082015261010062001a468184016200196f565b9082015261012062001a5a8382016200196f565b9082015292915050565b60006001600160401b0382111562001a805762001a806200175d565b5060051b60200190565b600082601f83011262001a9c57600080fd5b8151602062001ab562001aaf8362001a64565b62001833565b82815260069290921b8401810191818101908684111562001ad557600080fd5b8286015b8481101562001b2f576040818903121562001af45760008081fd5b62001afe620017c4565b815162001b0b8162001866565b81528185015162001b1c8162001866565b8186015283529183019160400162001ad9565b509695505050505050565b805180151581146200188c57600080fd5b80516001600160801b03811681146200188c57600080fd5b60006060828403121562001b7657600080fd5b604051606081016001600160401b038111828210171562001b9b5762001b9b6200175d565b60405290508062001bac8362001b3a565b815262001bbc6020840162001b4b565b602082015262001bcf6040840162001b4b565b60408201525092915050565b600082601f83011262001bed57600080fd5b8151602062001c0062001aaf8362001a64565b82815260a0928302850182019282820191908785111562001c2057600080fd5b8387015b8581101562001caf5781818a03121562001c3e5760008081fd5b62001c48620017e9565b815162001c558162001866565b815262001c648287016200196f565b86820152604062001c7781840162001891565b90820152606062001c8a83820162001891565b90820152608062001c9d83820162001b3a565b90820152845292840192810162001c24565b5090979650505050505050565b600082601f83011262001cce57600080fd5b8151602062001ce162001aaf8362001a64565b82815260c0928302850182019282820191908785111562001d0157600080fd5b8387015b8581101562001caf5781818a03121562001d1f5760008081fd5b62001d296200180e565b815162001d368162001866565b815262001d458287016200196f565b86820152604062001d588184016200196f565b90820152606062001d6b8382016200195c565b90820152608062001d7e8382016200196f565b9082015260a062001d918382016200196f565b90820152845292840192810162001d05565b600082601f83011262001db557600080fd5b8151602062001dc862001aaf8362001a64565b82815260069290921b8401810191818101908684111562001de857600080fd5b8286015b8481101562001b2f576040818903121562001e075760008081fd5b62001e11620017c4565b815162001e1e8162001866565b815262001e2d8286016200195c565b8186015283529183019160400162001dec565b6000806000806000806000610300888a03121562001e5d57600080fd5b62001e698989620018a9565b965062001e7a8960e08a0162001984565b6102208901519096506001600160401b038082111562001e9957600080fd5b62001ea78b838c0162001a8a565b965062001eb98b6102408c0162001b63565b95506102a08a015191508082111562001ed157600080fd5b62001edf8b838c0162001bdb565b94506102c08a015191508082111562001ef757600080fd5b62001f058b838c0162001cbc565b93506102e08a015191508082111562001f1d57600080fd5b5062001f2c8a828b0162001da3565b91505092959891949750929550565b80516001600160a01b03168252602081015162001f5e602084018261ffff169052565b50604081015162001f77604084018263ffffffff169052565b50606081015162001f8e606084018261ffff169052565b50608081015162001fa7608084018263ffffffff169052565b5060a081015162001fbe60a084018261ffff169052565b5060c081015162001fd560c084018261ffff169052565b5060e081015162001ff160e08401826001600160a01b03169052565b506101008181015163ffffffff908116918401919091526101209182015116910152565b82516001600160a01b0390811682526020808501516001600160401b0390811691840191909152604080860151821690840152606080860151909116908301526080808501516001600160601b03169083015260a08085015182169083015260c0808501519091169082015261022081016200105a60e083018462001f3b565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201620020d657620020d6620020ab565b5060010190565b602080825282518282018190526000919060409081850190868401855b828110156200215f57815180516001600160a01b031685528681015163ffffffff1687860152858101516001600160401b03908116878701526060808301519091169086015260809081015115159085015260a09093019290850190600101620020fa565b5091979650505050505050565b602080825282518282018190526000919060409081850190868401855b828110156200215f57815180516001600160a01b031685528681015163ffffffff908116888701528682015181168787015260608083015161ffff169087015260808083015182169087015260a091820151169085015260c0909301929085019060010162002189565b818103818111156200101f576200101f620020ab565b6000816200221b576200221b620020ab565b506000190190565b63ffffffff818116838216019080821115620015795762001579620020ab565b6000604080830163ffffffff8616845260208281860152818651808452606087019150828801935060005b81811015620022a257845180516001600160a01b0316845284015161ffff168484015293830193918501916001016200226e565b509098975050505050505050565b600060208284031215620022c357600080fd5b81516200105a8162001866565b80820281158282048414176200101f576200101f620020ab565b6000826200230857634e487b7160e01b600052601260045260246000fd5b500490565b6001600160601b03828116828216039080821115620015795762001579620020ab565b6000602082840312156200234357600080fd5b5051919050565b8181036000831280158383131683831282161715620015795762001579620020ab565b6000602082840312156200238057600080fd5b6200105a8262001b3a565b634e487b7160e01b600052603160045260246000fd5b60005b83811015620023be578181015183820152602001620023a4565b50506000910152565b60008251620023db818460208701620023a1565b9190910192915050565b602081526000825180602084015262002406816040850160208701620023a1565b601f01601f19169190910160400192915050565b60805160a05160c05160e05161010051610120516101405161016051615f726200252860003960008181610334015281816116530152613dfb015260008181610305015281816114060152818161146e01528181611c3501528181611c9d0152613dcc0152600081816102710152818161097f01528181611790015281816120350152613d3801526000818161024101528181611d680152613d0801526000818161021201528181610fb4015281816119e001528181611ae1015281816123a301528181613090015281816132c60152613cd90152600081816102d101528181611bad0152613d980152600081816102a1015281816125d30152613d680152600061215e0152615f726000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c806379ba5097116100f9578063d09dc33911610097578063eff7cc4811610071578063eff7cc481461092d578063f25561fd14610935578063f2fde38b14610948578063fbca3b741461095b57600080fd5b8063d09dc339146108ff578063df0aa9e914610907578063e687b40a1461091a57600080fd5b80638da5cb5b116100d35780638da5cb5b146107c05780639a113c36146107d1578063b06d41bc146108d6578063c92b2832146108ec57600080fd5b806379ba5097146107925780637ec757511461079a578063856c8247146107ad57600080fd5b8063546719cd11610166578063599f643111610140578063599f6431146105ec578063704b6c02146105fd5780637437ff9f1461061057806376f6ae761461077f57600080fd5b8063546719cd14610555578063549e946f146105b957806354b71468146105cc57600080fd5b806320487ded116101a257806320487ded146104d35780633a87ac53146104f45780634120fccd1461050957806348a98aa41461052a57600080fd5b806306285c69146101c95780631772047e1461037a578063181f5a771461048a575b600080fd5b6103646040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091526040518060e001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516103719190614ac2565b60405180910390f35b61043c610388366004614b63565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506001600160a01b03166000908152600e6020908152604091829020825160a081018452905463ffffffff8082168352640100000000820481169383019390935261ffff68010000000000000000820416938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000090920416608082015290565b6040516103719190815163ffffffff908116825260208084015182169083015260408084015161ffff1690830152606080840151821690830152608092830151169181019190915260a00190565b6104c66040518060400160405280601381526020017f45564d3245564d4f6e52616d7020312e322e300000000000000000000000000081525081565b6040516103719190614bd0565b6104e66104e1366004614c11565b61097b565b604051908152602001610371565b610507610502366004614e19565b610dfb565b005b610511610e11565b60405167ffffffffffffffff9091168152602001610371565b61053d610538366004614e73565b610e45565b6040516001600160a01b039091168152602001610371565b61055d610ea5565b604051610371919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b6105076105c7366004614eac565b610f55565b6010546040516bffffffffffffffffffffffff9091168152602001610371565b6002546001600160a01b031661053d565b61050761060b366004614b63565b61110a565b6107726040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101919091525060408051610140810182526005546001600160a01b03808216835261ffff7401000000000000000000000000000000000000000083048116602085015263ffffffff76010000000000000000000000000000000000000000000084048116958501959095527a0100000000000000000000000000000000000000000000000000008304811660608501527c0100000000000000000000000000000000000000000000000000000000928390048516608085015260065480821660a086015262010000810490911660c0850152640100000000810490911660e08401527801000000000000000000000000000000000000000000000000810484166101008401520490911661012082015290565b6040516103719190614fa5565b61050761078d366004614fb4565b6111d4565b61050761128c565b6105076107a8366004615058565b61136f565b6105116107bb366004614b63565b6113d8565b6000546001600160a01b031661053d565b61088c6107df366004614b63565b604080516080810182526000808252602082018190529181018290526060810191909152506001600160a01b03166000908152600d60209081526040918290208251608081018452905463ffffffff8116825267ffffffffffffffff64010000000082048116938301939093526c0100000000000000000000000081049092169281019290925260ff74010000000000000000000000000000000000000000909104161515606082015290565b60408051825163ffffffff16815260208084015167ffffffffffffffff90811691830191909152838301511691810191909152606091820151151591810191909152608001610371565b6108de6114d9565b6040516103719291906151bb565b6105076108fa36600461520b565b6115dd565b6104e6611645565b6104e6610915366004615279565b61164f565b6105076109283660046152e5565b6121d3565b6105076121e4565b6105076109433660046153ad565b61247b565b610507610956366004614b63565b6124e1565b61096e6109693660046154a0565b6124f2565b60405161037191906154bd565b60007f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168367ffffffffffffffff16146109fb576040517fd9a9cd6800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b6000610a12610a0d608085018561550a565b6125a4565b519050610a3c610a25602085018561550a565b905082610a35604087018761556f565b9050612698565b6000600d81610a516080870160608801614b63565b6001600160a01b0316815260208082019290925260409081016000208151608081018352905463ffffffff81168252640100000000810467ffffffffffffffff908116948301949094526c010000000000000000000000008104909316918101919091527401000000000000000000000000000000000000000090910460ff16151560608201819052909150610b2f57610af16080850160608601614b63565b6040517fa7499d200000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b600654600090819064010000000090046001600160a01b031663ffdb4b37610b5d6080890160608a01614b63565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015267ffffffffffffffff8a1660248201526044016040805180830381865afa158015610bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bec9190615603565b9092509050806000808080610c0460408c018c61556f565b90501115610c3e57610c32610c1f60808c0160608d01614b63565b87610c2d60408e018e61556f565b6127c1565b91945092509050610c5a565b8651610c579063ffffffff16662386f26fc10000615665565b92505b6040870151610c739067ffffffffffffffff1684615665565b60208881015160055492955060009267ffffffffffffffff9091169163ffffffff8616917a010000000000000000000000000000000000000000000000000000900461ffff1690610cc6908f018f61550a565b610cd1929150615665565b600554610cfe90760100000000000000000000000000000000000000000000900463ffffffff168d61567c565b610d08919061567c565b610d12919061567c565b610d1c9190615665565b610d36906dffffffffffffffffffffffffffff8716615665565b60065490915060009062010000900461ffff1615610daa576000607060ff16887bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16901c9050610da6818e8060200190610d8c919061550a565b90508f8060400190610d9e919061556f565b905087612acb565b9150505b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff881681610dd4848861567c565b610dde919061567c565b610de8919061568f565b9a50505050505050505050505b92915050565b610e03612b9b565b610e0d8282612c11565b5050565b601054600090610e4090700100000000000000000000000000000000900467ffffffffffffffff1660016156ca565b905090565b6000610e52600a83612f71565b610e93576040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024016109f2565b610e9e600a83612f86565b9392505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040805160a0810182526003546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff1660208501527401000000000000000000000000000000000000000090920460ff161515938301939093526004548084166060840152049091166080820152610e4090612f9b565b6000546001600160a01b03163314801590610f7b57506002546001600160a01b03163314155b15610fb2576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161480610ff957506001600160a01b038116155b15611030576040517f232cb97f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061103a61304d565b1215611072576040517f02075e0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610e0d9082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f991906156eb565b6001600160a01b038516919061310d565b6000546001600160a01b0316331480159061113057506002546001600160a01b03163314155b15611167576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c906020015b60405180910390a150565b6000546001600160a01b031633148015906111fa57506002546001600160a01b03163314155b15611231576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e0d8282808060200260200160405190810160405280939291908181526020016000905b828210156112825761127360408302860136819003810190615704565b81526020019060010190611256565b505050505061318d565b6001546001600160a01b03163314611300576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016109f2565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000546001600160a01b0316331480159061139557506002546001600160a01b03163314155b156113cc576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d581613400565b50565b6001600160a01b0381166000908152600f602052604081205467ffffffffffffffff168015801561143157507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615155b15610df5576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063856c824790602401602060405180830381865afa1580156114b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9e9190615743565b60606000806114e860076135c6565b90508067ffffffffffffffff81111561150357611503614c61565b60405190808252806020026020018201604052801561154857816020015b60408051808201909152600080825260208201528152602001906001900390816115215790505b50925060005b818110156115ba576000806115646007846135d1565b915091506040518060400160405280836001600160a01b031681526020018261ffff1681525086848151811061159c5761159c615760565b60200260200101819052505050806115b39061578f565b905061154e565b505060105491926c0100000000000000000000000090920463ffffffff16919050565b6000546001600160a01b0316331480159061160357506002546001600160a01b03163314155b1561163a576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d56003826135ef565b6000610e4061304d565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663397796f76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d391906157c7565b1561170a576040517fc148371500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821661174a576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546001600160a01b0316331461178e576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168567ffffffffffffffff1614611807576040517fd9a9cd6800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016109f2565b611811848061550a565b905060201461185857611824848061550a565b6040517f370d875f0000000000000000000000000000000000000000000000000000000081526004016109f292919061580f565b6000611864858061550a565b8101906118719190615823565b90506001600160a01b038111806118885750600a81105b1561189757611824858061550a565b60006118a9610a0d608088018861550a565b51905060006118bb604088018861556f565b91506118d890506118cf602089018961550a565b90508383612698565b80156119d65760005b81811015611959576118f6604089018961556f565b8281811061190657611906615760565b90506040020160200135600003611949576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119528161578f565b90506118e1565b506119d661196a604089018961556f565b808060200260200160405190810160405280939291908181526020016000905b828210156119b6576119a76040830286013681900381019061583c565b8152602001906001019061198a565b505060065464010000000090046001600160a01b031692506137c7915050565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016611a106080890160608a01614b63565b6001600160a01b031603611a745760108054879190600090611a419084906bffffffffffffffffffffffff16615876565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550611b9b565b60065464010000000090046001600160a01b03166241e5be611a9c60808a0160608b01614b63565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b039182166004820152602481018a90527f00000000000000000000000000000000000000000000000000000000000000009091166044820152606401602060405180830381865afa158015611b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b4c91906156eb565b60108054600090611b6c9084906bffffffffffffffffffffffff16615876565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505b6010546bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811691161115611c08576040517fe5c7a49100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0385166000908152600f602052604090205467ffffffffffffffff16158015611c6057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615155b15611d58576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063856c824790602401602060405180830381865afa158015611ce4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d089190615743565b6001600160a01b0386166000908152600f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff929092169190911790555b6000604051806101a001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168152602001876001600160a01b03168152602001856001600160a01b0316815260200160108081819054906101000a900467ffffffffffffffff16611dd59061589b565b825467ffffffffffffffff9182166101009390930a838102908302199091161790925582526020808301879052600060408085018290526001600160a01b038c168252600f90925290812080546060909401939092611e34911661589b565b825467ffffffffffffffff9182166101009390930a83810292021916179091558152602001611e6960808b0160608c01614b63565b6001600160a01b03168152602001888152602001898060200190611e8d919061550a565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001611ed460408b018b61556f565b808060200260200160405190810160405280939291908181526020016000905b82821015611f2057611f116040830286013681900381019061583c565b81526020019060010190611ef4565b505050505081526020018367ffffffffffffffff811115611f4357611f43614c61565b604051908082528060200260200182016040528015611f7657816020015b6060815260200190600190039081611f615790505b508152600060209091018190529091505b82811015612157576000611f9e60408b018b61556f565b83818110611fae57611fae615760565b905060400201803603810190611fc4919061583c565b90506000611fd68c8360000151610e45565b6001600160a01b031663968754458a611fef8e8061550a565b60208088015160408051928301815260008352517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815261205d959493927f0000000000000000000000000000000000000000000000000000000000000000916004016158c2565b6000604051808303816000875af115801561207c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120a4919081019061591a565b82516001600160a01b03166000908152600e602052604090205481519192506e010000000000000000000000000000900463ffffffff1610156121215781516040517f36f536ca0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b80846101600151848151811061213957612139615760565b60200260200101819052505050806121509061578f565b9050611f87565b50612182817f0000000000000000000000000000000000000000000000000000000000000000613982565b6101808201526040517fd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd906121b8908390615a47565b60405180910390a1610180015193505050505b949350505050565b6121db612b9b565b6113d581613add565b6000546001600160a01b0316331480159061220a57506002546001600160a01b03163314155b801561221e575061221c600733613e35565b155b15612255576040517f195db95800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6010546c01000000000000000000000000900463ffffffff1660008190036122a9576040517f990e30bf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6010546bffffffffffffffffffffffff16818110156122f4576040517f8d0f71d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006122fe61304d565b1215612336576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600061234360076135c6565b905060005b818110156124385760008061235e6007846135d1565b909250905060008761237e836bffffffffffffffffffffffff8a16615665565b612388919061568f565b90506123948187615b7c565b95506123d86001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016846bffffffffffffffffffffffff841661310d565b6040516bffffffffffffffffffffffff821681526001600160a01b038416907f55fdec2aab60a41fa5abb106670eb1006f5aeaee1ba7afea2bc89b5b3ec7678f9060200160405180910390a2505050806124319061578f565b9050612348565b5050601080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff929092169190911790555050565b6000546001600160a01b031633148015906124a157506002546001600160a01b03163314155b156124d8576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d581613e4a565b6124e9612b9b565b6113d581613fd5565b60606000612500600a6140b0565b67ffffffffffffffff81111561251857612518614c61565b604051908082528060200260200182016040528015612541578160200160208202803683370190505b50905060005b815181101561259d5761255b600a826140bb565b5082828151811061256e5761256e615760565b60200260200101816001600160a01b03166001600160a01b031681525050806125969061578f565b9050612547565b5092915050565b60408051602081019091526000815260008290036125fa5750604080516020810190915267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152610df5565b7f97a657c9000000000000000000000000000000000000000000000000000000006126258385615ba1565b7fffffffff00000000000000000000000000000000000000000000000000000000161461267e576040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61268b8260048186615be9565b810190610e9e9190615c13565b6006547801000000000000000000000000000000000000000000000000900463ffffffff1680841115612701576040517f8693378900000000000000000000000000000000000000000000000000000000815260048101829052602481018590526044016109f2565b6006547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16831115612763576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055474010000000000000000000000000000000000000000900461ffff168211156127bb576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6000808083815b81811015612abf5760008787838181106127e4576127e4615760565b9050604002018036038101906127fa919061583c565b80516001600160a01b03166000908152600e6020908152604091829020825160a081018452905463ffffffff8082168352640100000000820481169383019390935261ffff68010000000000000000820416938201939093526a01000000000000000000008304821660608201526e010000000000000000000000000000909204811660808301528251929350909161289791600a9190612f7116565b6128db5781516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b604081015160009061ffff16156129ff5760008c6001600160a01b031684600001516001600160a01b0316146129a25760065484516040517f4ab35b0b0000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201526401000000009092041690634ab35b0b90602401602060405180830381865afa158015612977573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299b9190615c55565b90506129a5565b508a5b620186a0836040015161ffff166129e78660200151847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166140ca90919063ffffffff16565b6129f19190615665565b6129fb919061568f565b9150505b6060820151612a0e9088615c70565b9650816080015186612a209190615c70565b8251909650600090612a3f9063ffffffff16662386f26fc10000615665565b905080821015612a5e57612a53818a61567c565b985050505050612aaf565b6000836020015163ffffffff16662386f26fc10000612a7d9190615665565b905080831115612a9d57612a91818b61567c565b99505050505050612aaf565b612aa7838b61567c565b995050505050505b612ab88161578f565b90506127c8565b50509450945094915050565b60008063ffffffff8316612ae0608086615665565b612aec8761022061567c565b612af6919061567c565b612b00919061567c565b6005546006549192506000917c010000000000000000000000000000000000000000000000000000000090910463ffffffff1690612b429061ffff1684615665565b612b4c919061567c565b60065490915062010000900461ffff16612b766dffffffffffffffffffffffffffff891683615665565b612b809190615665565b612b9090655af3107a4000615665565b979650505050505050565b6000546001600160a01b03163314612c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016109f2565b565b60005b8251811015612d72576000838281518110612c3157612c31615760565b60200260200101516000015190506000848381518110612c5357612c53615760565b6020026020010151602001519050612c7582600a612f7190919063ffffffff16565b612cb6576040517f73913ebd0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024016109f2565b6001600160a01b038116612ccb600a84612f86565b6001600160a01b031614612d0b576040517f6cc7b99800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d16600a83614107565b15612d5f57604080516001600160a01b038085168252831660208201527f987eb3c2f78454541205f72f34839b434c306c9eaf4922efd7c0c3060fdb2e4c910160405180910390a15b505080612d6b9061578f565b9050612c14565b5060005b8151811015612f6c576000828281518110612d9357612d93615760565b60200260200101516000015190506000838381518110612db557612db5615760565b602002602001015160200151905060006001600160a01b0316826001600160a01b03161480612deb57506001600160a01b038116155b15612e22576040517f6c2a418000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03166321df0da76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e849190615c8d565b6001600160a01b0316826001600160a01b031614612ece576040517f6cc7b99800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612eda600a838361411c565b15612f2757604080516001600160a01b038085168252831660208201527f95f865c2808f8b2a85eea2611db7843150ee7835ef1403f9755918a97d76933c910160405180910390a1612f59565b6040517f3caf458500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505080612f659061578f565b9050612d76565b505050565b6000610e9e836001600160a01b038416614132565b6000610e9e836001600160a01b03841661413e565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915261302982606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff164261300d9190615caa565b85608001516fffffffffffffffffffffffffffffffff1661414a565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b6010546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000916bffffffffffffffffffffffff16907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156130df573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061310391906156eb565b610e409190615cbd565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052612f6c908490614172565b805160408111156131ca576040517fb5a10cfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6010546c01000000000000000000000000900463ffffffff1615801590613218575060105463ffffffff6c010000000000000000000000008204166bffffffffffffffffffffffff90911610155b15613225576132256121e4565b600061323160076135c6565b90505b801561327357600061325261324a600184615caa565b6007906135d1565b509050613260600782614271565b50508061326c90615cdd565b9050613234565b506000805b8281101561338157600084828151811061329457613294615760565b602002602001015160000151905060008583815181106132b6576132b6615760565b60200260200101516020015190507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316148061330b57506001600160a01b038216155b1561334d576040517f4de938d10000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024016109f2565b61335d60078361ffff8416614286565b5061336c61ffff821685615c70565b935050508061337a9061578f565b9050613278565b50601080547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff8416021790556040517f8c337bff38141c507abd25c547606bdde78fe8c12e941ab613f3a565fea6cd24906133f39083908690615d12565b60405180910390a1505050565b60005b815181101561359657600082828151811061342057613420615760565b6020908102919091018101516040805160a080820183528385015163ffffffff908116835283850151811683870190815260608087015161ffff9081168688019081526080808a0151861693880193845295890151851695870195865297516001600160a01b03166000908152600e9099529590972093518454915196519751935183166e010000000000000000000000000000027fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff9484166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff999097166801000000000000000002989098167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff978416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909316919093161717949094169390931791909117919091169190911790555061358f8161578f565b9050613403565b507f555c74101f7a15746d31c6731170310e667bcc607996b2fc0b981a7b26a416e9816040516111c99190615d31565b6000610df58261429c565b60008080806135e086866142a7565b909450925050505b9250929050565b815460009061361890700100000000000000000000000000000000900463ffffffff1642615caa565b905080156136ba5760018301548354613660916fffffffffffffffffffffffffffffffff8082169281169185917001000000000000000000000000000000009091041661414a565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b602082015183546136e0916fffffffffffffffffffffffffffffffff90811691166142d2565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19906133f39084908151151581526020808301516fffffffffffffffffffffffffffffffff90811691830191909152604092830151169181019190915260600190565b81516000805b82811015613974576000846001600160a01b031663d02641a08784815181106137f8576137f8615760565b6020908102919091010151516040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0390911660048201526024016040805180830381865afa15801561385f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138839190615db6565b5190507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116600003613908578582815181106138be576138be615760565b6020908102919091010151516040517f9a655f7b0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b61395686838151811061391d5761391d615760565b602002602001015160200151827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166140ca90919063ffffffff16565b613960908461567c565b9250508061396d9061578f565b90506137cd565b506127bb60038260006142e8565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b6101000151604051602001613a189897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b6040516020818303038152906040528051906020012085610120015180519060200120866101400151604051602001613a519190615de9565b60405160208183030381529060405280519060200120876101600151604051602001613a7d9190615dfc565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b60e08101516001600160a01b0316613b21576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600560008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548161ffff021916908361ffff16021790555060408201518160000160166101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001601a6101000a81548161ffff021916908361ffff160217905550608082015181600001601c6101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160006101000a81548161ffff021916908361ffff16021790555060c08201518160010160026101000a81548161ffff021916908361ffff16021790555060e08201518160010160046101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555061012082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055509050507f2a57f7c2027cf032c78b77d4d8d2fbd20ad22e5d5e5b5fb23ac7d7820d44adc66040518060e001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250826040516111c9929190615e0f565b6000610e9e836001600160a01b038416614637565b60005b8151811015613fa5576000828281518110613e6a57613e6a615760565b60209081029190910181015160408051608080820183528385015163ffffffff90811683528385015167ffffffffffffffff908116848801908152606080880151831686880190815294880151151590860190815296516001600160a01b03166000908152600d9098529490962092518354945192519551151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9688166c0100000000000000000000000002969096167fffffffffffffffffffffff000000000000000000ffffffffffffffffffffffff93909716640100000000027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090951691161792909217919091169290921717905550613f9e8161578f565b9050613e4d565b507f067924bf9277d905a9a4631a06d959bc032ace86b3caa835ae7e403d4f39010e816040516111c99190615e99565b336001600160a01b03821603614047576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016109f2565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000610df5826135c6565b60008080806135e086866135d1565b6000670de0b6b3a76400006140fd837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8616615665565b610e9e919061568f565b6000610e9e836001600160a01b038416614643565b60006121cb846001600160a01b0385168461464f565b6000610e9e8383614637565b6000610e9e8383614665565b60006141698561415a8486615665565b614164908761567c565b6142d2565b95945050505050565b60006141c7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166146ef9092919063ffffffff16565b805190915015612f6c57808060200190518101906141e591906157c7565b612f6c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016109f2565b6000610e9e836001600160a01b0384166146fe565b60006121cb846001600160a01b0385168461471b565b6000610df582614738565b600080806142b58585614742565b600081815260029690960160205260409095205494959350505050565b60008183106142e15781610e9e565b5090919050565b825474010000000000000000000000000000000000000000900460ff16158061430f575081155b1561431957505050565b825460018401546fffffffffffffffffffffffffffffffff8083169291169060009061435f90700100000000000000000000000000000000900463ffffffff1642615caa565b9050801561441f57818311156143a1576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546143db9083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661414a565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156144bc576001600160a01b038416614471576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016109f2565b6040517f1a76572a00000000000000000000000000000000000000000000000000000000815260048101839052602481018690526001600160a01b03851660448201526064016109f2565b848310156145b55760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169060009082906145009082615caa565b61450a878a615caa565b614514919061567c565b61451e919061568f565b90506001600160a01b03861661456a576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016109f2565b6040517fd0c8d23a00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526001600160a01b03871660448201526064016109f2565b6145bf8584615caa565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b6000610e9e838361474e565b6000610e9e83836146fe565b60006121cb84846001600160a01b03851661471b565b60008181526002830160205260408120548015158061468957506146898484614637565b610e9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b6579000060448201526064016109f2565b60606121cb8484600085614766565b60008181526002830160205260408120819055610e9e8383614867565b600082815260028401602052604081208290556121cb8484614873565b6000610df5825490565b6000610e9e838361487f565b60008181526001830160205260408120541515610e9e565b6060824710156147f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016109f2565b600080866001600160a01b031685876040516148149190615f1a565b60006040518083038185875af1925050503d8060008114614851576040519150601f19603f3d011682016040523d82523d6000602084013e614856565b606091505b5091509150612b90878383876148a9565b6000610e9e838361493c565b6000610e9e8383614a2f565b600082600001828154811061489657614896615760565b9060005260206000200154905092915050565b6060831561493257825160000361492b576001600160a01b0385163b61492b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016109f2565b50816121cb565b6121cb8383614a7e565b60008181526001830160205260408120548015614a25576000614960600183615caa565b855490915060009061497490600190615caa565b90508181146149d957600086600001828154811061499457614994615760565b90600052602060002001549050808760000184815481106149b7576149b7615760565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806149ea576149ea615f36565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610df5565b6000915050610df5565b6000818152600183016020526040812054614a7657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610df5565b506000610df5565b815115614a8e5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f29190614bd0565b60e08101610df582846001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015280606085015116606086015250506bffffffffffffffffffffffff60808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b6001600160a01b03811681146113d557600080fd5b8035614b5e81614b3e565b919050565b600060208284031215614b7557600080fd5b8135610e9e81614b3e565b60005b83811015614b9b578181015183820152602001614b83565b50506000910152565b60008151808452614bbc816020860160208601614b80565b601f01601f19169290920160200192915050565b602081526000610e9e6020830184614ba4565b67ffffffffffffffff811681146113d557600080fd5b600060a08284031215614c0b57600080fd5b50919050565b60008060408385031215614c2457600080fd5b8235614c2f81614be3565b9150602083013567ffffffffffffffff811115614c4b57600080fd5b614c5785828601614bf9565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614cb357614cb3614c61565b60405290565b60405160c0810167ffffffffffffffff81118282101715614cb357614cb3614c61565b604051610140810167ffffffffffffffff81118282101715614cb357614cb3614c61565b60405160a0810167ffffffffffffffff81118282101715614cb357614cb3614c61565b604051601f8201601f1916810167ffffffffffffffff81118282101715614d4c57614d4c614c61565b604052919050565b600067ffffffffffffffff821115614d6e57614d6e614c61565b5060051b60200190565b600082601f830112614d8957600080fd5b81356020614d9e614d9983614d54565b614d23565b82815260069290921b84018101918181019086841115614dbd57600080fd5b8286015b84811015614e0e5760408189031215614dda5760008081fd5b614de2614c90565b8135614ded81614b3e565b815281850135614dfc81614b3e565b81860152835291830191604001614dc1565b509695505050505050565b60008060408385031215614e2c57600080fd5b823567ffffffffffffffff80821115614e4457600080fd5b614e5086838701614d78565b93506020850135915080821115614e6657600080fd5b50614c5785828601614d78565b60008060408385031215614e8657600080fd5b8235614e9181614be3565b91506020830135614ea181614b3e565b809150509250929050565b60008060408385031215614ebf57600080fd5b8235614e9181614b3e565b80516001600160a01b031682526020810151614eec602084018261ffff169052565b506040810151614f04604084018263ffffffff169052565b506060810151614f1a606084018261ffff169052565b506080810151614f32608084018263ffffffff169052565b5060a0810151614f4860a084018261ffff169052565b5060c0810151614f5e60c084018261ffff169052565b5060e0810151614f7960e08401826001600160a01b03169052565b506101008181015163ffffffff81168483015250506101208181015163ffffffff8116848301526127bb565b6101408101610df58284614eca565b60008060208385031215614fc757600080fd5b823567ffffffffffffffff80821115614fdf57600080fd5b818501915085601f830112614ff357600080fd5b81358181111561500257600080fd5b8660208260061b850101111561501757600080fd5b60209290920196919550909350505050565b63ffffffff811681146113d557600080fd5b8035614b5e81615029565b803561ffff81168114614b5e57600080fd5b6000602080838503121561506b57600080fd5b823567ffffffffffffffff81111561508257600080fd5b8301601f8101851361509357600080fd5b80356150a1614d9982614d54565b81815260c091820283018401918482019190888411156150c057600080fd5b938501935b8385101561515c5780858a0312156150dd5760008081fd5b6150e5614cb9565b85356150f081614b3e565b8152858701356150ff81615029565b8188015260408681013561511281615029565b908201526060615123878201615046565b9082015260808681013561513681615029565b9082015260a08681013561514981615029565b90820152835293840193918501916150c5565b50979650505050505050565b600081518084526020808501945080840160005b838110156151b057815180516001600160a01b0316885283015161ffff16838801526040909601959082019060010161517c565b509495945050505050565b6040815260006151ce6040830185615168565b90508260208301529392505050565b80151581146113d557600080fd5b80356fffffffffffffffffffffffffffffffff81168114614b5e57600080fd5b60006060828403121561521d57600080fd5b6040516060810181811067ffffffffffffffff8211171561524057615240614c61565b604052823561524e816151dd565b815261525c602084016151eb565b602082015261526d604084016151eb565b60408201529392505050565b6000806000806080858703121561528f57600080fd5b843561529a81614be3565b9350602085013567ffffffffffffffff8111156152b657600080fd5b6152c287828801614bf9565b9350506040850135915060608501356152da81614b3e565b939692955090935050565b600061014082840312156152f857600080fd5b615300614cdc565b61530983614b53565b815261531760208401615046565b60208201526153286040840161503b565b604082015261533960608401615046565b606082015261534a6080840161503b565b608082015261535b60a08401615046565b60a082015261536c60c08401615046565b60c082015261537d60e08401614b53565b60e082015261010061539081850161503b565b908201526101206153a284820161503b565b908201529392505050565b600060208083850312156153c057600080fd5b823567ffffffffffffffff8111156153d757600080fd5b8301601f810185136153e857600080fd5b80356153f6614d9982614d54565b81815260a0918202830184019184820191908884111561541557600080fd5b938501935b8385101561515c5780858a0312156154325760008081fd5b61543a614d00565b853561544581614b3e565b81528587013561545481615029565b8188015260408681013561546781614be3565b9082015260608681013561547a81614be3565b9082015260808681013561548d816151dd565b908201528352938401939185019161541a565b6000602082840312156154b257600080fd5b8135610e9e81614be3565b6020808252825182820181905260009190848201906040850190845b818110156154fe5783516001600160a01b0316835292840192918401916001016154d9565b50909695505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261553f57600080fd5b83018035915067ffffffffffffffff82111561555a57600080fd5b6020019150368190038213156135e857600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126155a457600080fd5b83018035915067ffffffffffffffff8211156155bf57600080fd5b6020019150600681901b36038213156135e857600080fd5b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81168114614b5e57600080fd5b6000806040838503121561561657600080fd5b61561f836155d7565b915061562d602084016155d7565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610df557610df5615636565b80820180821115610df557610df5615636565b6000826156c5577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b67ffffffffffffffff81811683821601908082111561259d5761259d615636565b6000602082840312156156fd57600080fd5b5051919050565b60006040828403121561571657600080fd5b61571e614c90565b823561572981614b3e565b815261573760208401615046565b60208201529392505050565b60006020828403121561575557600080fd5b8151610e9e81614be3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036157c0576157c0615636565b5060010190565b6000602082840312156157d957600080fd5b8151610e9e816151dd565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6020815260006121cb6020830184866157e4565b60006020828403121561583557600080fd5b5035919050565b60006040828403121561584e57600080fd5b615856614c90565b823561586181614b3e565b81526020928301359281019290925250919050565b6bffffffffffffffffffffffff81811683821601908082111561259d5761259d615636565b600067ffffffffffffffff8083168181036158b8576158b8615636565b6001019392505050565b6001600160a01b038716815260a0602082015260006158e560a0830187896157e4565b85604084015267ffffffffffffffff85166060840152828103608084015261590d8185614ba4565b9998505050505050505050565b60006020828403121561592c57600080fd5b815167ffffffffffffffff8082111561594457600080fd5b818401915084601f83011261595857600080fd5b81518181111561596a5761596a614c61565b61597d6020601f19601f84011601614d23565b915080825285602082850101111561599457600080fd5b6159a5816020840160208601614b80565b50949350505050565b600081518084526020808501945080840160005b838110156151b057815180516001600160a01b0316885283015183880152604090960195908201906001016159c2565b600081518084526020808501808196508360051b8101915082860160005b85811015615a3a578284038952615a28848351614ba4565b98850198935090840190600101615a10565b5091979650505050505050565b60208152615a6260208201835167ffffffffffffffff169052565b60006020830151615a7e60408401826001600160a01b03169052565b5060408301516001600160a01b038116606084015250606083015167ffffffffffffffff8116608084015250608083015160a083015260a0830151615ac760c084018215159052565b5060c083015167ffffffffffffffff811660e08401525060e0830151610100615afa818501836001600160a01b03169052565b840151610120848101919091528401516101a061014080860182905291925090615b286101c0860184614ba4565b9250808601519050601f19610160818786030181880152615b4985846159ae565b945080880151925050610180818786030181880152615b6885846159f2565b970151959092019490945250929392505050565b6bffffffffffffffffffffffff82811682821603908082111561259d5761259d615636565b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015615be15780818660040360031b1b83161692505b505092915050565b60008085851115615bf957600080fd5b83861115615c0657600080fd5b5050820193919092039150565b600060208284031215615c2557600080fd5b6040516020810181811067ffffffffffffffff82111715615c4857615c48614c61565b6040529135825250919050565b600060208284031215615c6757600080fd5b610e9e826155d7565b63ffffffff81811683821601908082111561259d5761259d615636565b600060208284031215615c9f57600080fd5b8151610e9e81614b3e565b81810381811115610df557610df5615636565b818103600083128015838313168383128216171561259d5761259d615636565b600081615cec57615cec615636565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b63ffffffff831681526040602082015260006121cb6040830184615168565b602080825282518282018190526000919060409081850190868401855b82811015615a3a57815180516001600160a01b031685528681015163ffffffff908116888701528682015181168787015260608083015161ffff169087015260808083015182169087015260a091820151169085015260c09093019290850190600101615d4e565b600060408284031215615dc857600080fd5b615dd0614c90565b615dd9836155d7565b8152602083015161573781615029565b602081526000610e9e60208301846159ae565b602081526000610e9e60208301846159f2565b6102208101615e8c82856001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015280606085015116606086015250506bffffffffffffffffffffffff60808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b610e9e60e0830184614eca565b602080825282518282018190526000919060409081850190868401855b82811015615a3a57815180516001600160a01b031685528681015163ffffffff16878601528581015167ffffffffffffffff908116878701526060808301519091169086015260809081015115159085015260a09093019290850190600101615eb6565b60008251615f2c818460208701614b80565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000813000a000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324100000000000000000000000000000000000000000000000008f90b8876dee653800000000000000000000000000000000000000000000000048810ec3e431431f0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000052b7d2dcc80cd2e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099360767a4705f68cccb9533195b761648d6d807000000000000000000000000d3b06cebf099ce7da4accf578aaebfdbd6e88a930000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000005573000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d20536e60832be579cd38e89dc03d11e1741fba000000000000000000000000000000000000000000000000000000000000753000000000000000000000000000000000000000000000000000000000002dc6c00000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000152d02c7e14af68000000000000000000000000000000000000000000000000000090d972f32323c00000000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a3241000000000000000000000000099685281ec520a003f1a726a5a8078c2124c1477000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000004d57c6d8037c65fa66d6231844785a428310a73500000000000000000000000088a2d74f47a237a62e7a51cdda67270ce381555e000000000000000000000000477099a1e7812c09d4df5a9158f97fb7b7105dc5000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c000000000000000000000000b2958d1bd07448865e555feeff32b58d254ffb4c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000c7d713b49da000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000071480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000001d4c0000000000000000000000000000000000000000000000000000000000000028000000000000000000000000088a2d74f47a237a62e7a51cdda67270ce381555e000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000071480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000714800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101c45760003560e01c806379ba5097116100f9578063d09dc33911610097578063eff7cc4811610071578063eff7cc481461092d578063f25561fd14610935578063f2fde38b14610948578063fbca3b741461095b57600080fd5b8063d09dc339146108ff578063df0aa9e914610907578063e687b40a1461091a57600080fd5b80638da5cb5b116100d35780638da5cb5b146107c05780639a113c36146107d1578063b06d41bc146108d6578063c92b2832146108ec57600080fd5b806379ba5097146107925780637ec757511461079a578063856c8247146107ad57600080fd5b8063546719cd11610166578063599f643111610140578063599f6431146105ec578063704b6c02146105fd5780637437ff9f1461061057806376f6ae761461077f57600080fd5b8063546719cd14610555578063549e946f146105b957806354b71468146105cc57600080fd5b806320487ded116101a257806320487ded146104d35780633a87ac53146104f45780634120fccd1461050957806348a98aa41461052a57600080fd5b806306285c69146101c95780631772047e1461037a578063181f5a771461048a575b600080fd5b6103646040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091526040518060e001604052807f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324106001600160a01b031681526020017f0000000000000000000000000000000000000000000000008f90b8876dee653867ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000048810ec3e431431f67ffffffffffffffff1681526020017f0000000000000000000000000000000000000000000000000000000000030d4067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000052b7d2dcc80cd2e40000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000099360767a4705f68cccb9533195b761648d6d8076001600160a01b0316815250905090565b6040516103719190614ac2565b60405180910390f35b61043c610388366004614b63565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506001600160a01b03166000908152600e6020908152604091829020825160a081018452905463ffffffff8082168352640100000000820481169383019390935261ffff68010000000000000000820416938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000090920416608082015290565b6040516103719190815163ffffffff908116825260208084015182169083015260408084015161ffff1690830152606080840151821690830152608092830151169181019190915260a00190565b6104c66040518060400160405280601381526020017f45564d3245564d4f6e52616d7020312e322e300000000000000000000000000081525081565b6040516103719190614bd0565b6104e66104e1366004614c11565b61097b565b604051908152602001610371565b610507610502366004614e19565b610dfb565b005b610511610e11565b60405167ffffffffffffffff9091168152602001610371565b61053d610538366004614e73565b610e45565b6040516001600160a01b039091168152602001610371565b61055d610ea5565b604051610371919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b6105076105c7366004614eac565b610f55565b6010546040516bffffffffffffffffffffffff9091168152602001610371565b6002546001600160a01b031661053d565b61050761060b366004614b63565b61110a565b6107726040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101919091525060408051610140810182526005546001600160a01b03808216835261ffff7401000000000000000000000000000000000000000083048116602085015263ffffffff76010000000000000000000000000000000000000000000084048116958501959095527a0100000000000000000000000000000000000000000000000000008304811660608501527c0100000000000000000000000000000000000000000000000000000000928390048516608085015260065480821660a086015262010000810490911660c0850152640100000000810490911660e08401527801000000000000000000000000000000000000000000000000810484166101008401520490911661012082015290565b6040516103719190614fa5565b61050761078d366004614fb4565b6111d4565b61050761128c565b6105076107a8366004615058565b61136f565b6105116107bb366004614b63565b6113d8565b6000546001600160a01b031661053d565b61088c6107df366004614b63565b604080516080810182526000808252602082018190529181018290526060810191909152506001600160a01b03166000908152600d60209081526040918290208251608081018452905463ffffffff8116825267ffffffffffffffff64010000000082048116938301939093526c0100000000000000000000000081049092169281019290925260ff74010000000000000000000000000000000000000000909104161515606082015290565b60408051825163ffffffff16815260208084015167ffffffffffffffff90811691830191909152838301511691810191909152606091820151151591810191909152608001610371565b6108de6114d9565b6040516103719291906151bb565b6105076108fa36600461520b565b6115dd565b6104e6611645565b6104e6610915366004615279565b61164f565b6105076109283660046152e5565b6121d3565b6105076121e4565b6105076109433660046153ad565b61247b565b610507610956366004614b63565b6124e1565b61096e6109693660046154a0565b6124f2565b60405161037191906154bd565b60007f00000000000000000000000000000000000000000000000048810ec3e431431f67ffffffffffffffff168367ffffffffffffffff16146109fb576040517fd9a9cd6800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b6000610a12610a0d608085018561550a565b6125a4565b519050610a3c610a25602085018561550a565b905082610a35604087018761556f565b9050612698565b6000600d81610a516080870160608801614b63565b6001600160a01b0316815260208082019290925260409081016000208151608081018352905463ffffffff81168252640100000000810467ffffffffffffffff908116948301949094526c010000000000000000000000008104909316918101919091527401000000000000000000000000000000000000000090910460ff16151560608201819052909150610b2f57610af16080850160608601614b63565b6040517fa7499d200000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b600654600090819064010000000090046001600160a01b031663ffdb4b37610b5d6080890160608a01614b63565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015267ffffffffffffffff8a1660248201526044016040805180830381865afa158015610bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bec9190615603565b9092509050806000808080610c0460408c018c61556f565b90501115610c3e57610c32610c1f60808c0160608d01614b63565b87610c2d60408e018e61556f565b6127c1565b91945092509050610c5a565b8651610c579063ffffffff16662386f26fc10000615665565b92505b6040870151610c739067ffffffffffffffff1684615665565b60208881015160055492955060009267ffffffffffffffff9091169163ffffffff8616917a010000000000000000000000000000000000000000000000000000900461ffff1690610cc6908f018f61550a565b610cd1929150615665565b600554610cfe90760100000000000000000000000000000000000000000000900463ffffffff168d61567c565b610d08919061567c565b610d12919061567c565b610d1c9190615665565b610d36906dffffffffffffffffffffffffffff8716615665565b60065490915060009062010000900461ffff1615610daa576000607060ff16887bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16901c9050610da6818e8060200190610d8c919061550a565b90508f8060400190610d9e919061556f565b905087612acb565b9150505b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff881681610dd4848861567c565b610dde919061567c565b610de8919061568f565b9a50505050505050505050505b92915050565b610e03612b9b565b610e0d8282612c11565b5050565b601054600090610e4090700100000000000000000000000000000000900467ffffffffffffffff1660016156ca565b905090565b6000610e52600a83612f71565b610e93576040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024016109f2565b610e9e600a83612f86565b9392505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040805160a0810182526003546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff1660208501527401000000000000000000000000000000000000000090920460ff161515938301939093526004548084166060840152049091166080820152610e4090612f9b565b6000546001600160a01b03163314801590610f7b57506002546001600160a01b03163314155b15610fb2576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324106001600160a01b0316826001600160a01b03161480610ff957506001600160a01b038116155b15611030576040517f232cb97f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061103a61304d565b1215611072576040517f02075e0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610e0d9082906001600160a01b038516906370a0823190602401602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f991906156eb565b6001600160a01b038516919061310d565b6000546001600160a01b0316331480159061113057506002546001600160a01b03163314155b15611167576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c906020015b60405180910390a150565b6000546001600160a01b031633148015906111fa57506002546001600160a01b03163314155b15611231576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e0d8282808060200260200160405190810160405280939291908181526020016000905b828210156112825761127360408302860136819003810190615704565b81526020019060010190611256565b505050505061318d565b6001546001600160a01b03163314611300576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016109f2565b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000546001600160a01b0316331480159061139557506002546001600160a01b03163314155b156113cc576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d581613400565b50565b6001600160a01b0381166000908152600f602052604081205467ffffffffffffffff168015801561143157507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615155b15610df5576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063856c824790602401602060405180830381865afa1580156114b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9e9190615743565b60606000806114e860076135c6565b90508067ffffffffffffffff81111561150357611503614c61565b60405190808252806020026020018201604052801561154857816020015b60408051808201909152600080825260208201528152602001906001900390816115215790505b50925060005b818110156115ba576000806115646007846135d1565b915091506040518060400160405280836001600160a01b031681526020018261ffff1681525086848151811061159c5761159c615760565b60200260200101819052505050806115b39061578f565b905061154e565b505060105491926c0100000000000000000000000090920463ffffffff16919050565b6000546001600160a01b0316331480159061160357506002546001600160a01b03163314155b1561163a576040517ff6cd562000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d56003826135ef565b6000610e4061304d565b60007f00000000000000000000000099360767a4705f68cccb9533195b761648d6d8076001600160a01b031663397796f76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d391906157c7565b1561170a576040517fc148371500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821661174a576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546001600160a01b0316331461178e576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000048810ec3e431431f67ffffffffffffffff168567ffffffffffffffff1614611807576040517fd9a9cd6800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016109f2565b611811848061550a565b905060201461185857611824848061550a565b6040517f370d875f0000000000000000000000000000000000000000000000000000000081526004016109f292919061580f565b6000611864858061550a565b8101906118719190615823565b90506001600160a01b038111806118885750600a81105b1561189757611824858061550a565b60006118a9610a0d608088018861550a565b51905060006118bb604088018861556f565b91506118d890506118cf602089018961550a565b90508383612698565b80156119d65760005b81811015611959576118f6604089018961556f565b8281811061190657611906615760565b90506040020160200135600003611949576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119528161578f565b90506118e1565b506119d661196a604089018961556f565b808060200260200160405190810160405280939291908181526020016000905b828210156119b6576119a76040830286013681900381019061583c565b8152602001906001019061198a565b505060065464010000000090046001600160a01b031692506137c7915050565b6001600160a01b037f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a3241016611a106080890160608a01614b63565b6001600160a01b031603611a745760108054879190600090611a419084906bffffffffffffffffffffffff16615876565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550611b9b565b60065464010000000090046001600160a01b03166241e5be611a9c60808a0160608b01614b63565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b039182166004820152602481018a90527f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324109091166044820152606401602060405180830381865afa158015611b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b4c91906156eb565b60108054600090611b6c9084906bffffffffffffffffffffffff16615876565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505b6010546bffffffffffffffffffffffff7f00000000000000000000000000000000000000000052b7d2dcc80cd2e4000000811691161115611c08576040517fe5c7a49100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0385166000908152600f602052604090205467ffffffffffffffff16158015611c6057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615155b15611d58576040517f856c82470000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063856c824790602401602060405180830381865afa158015611ce4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d089190615743565b6001600160a01b0386166000908152600f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff929092169190911790555b6000604051806101a001604052807f0000000000000000000000000000000000000000000000008f90b8876dee653867ffffffffffffffff168152602001876001600160a01b03168152602001856001600160a01b0316815260200160108081819054906101000a900467ffffffffffffffff16611dd59061589b565b825467ffffffffffffffff9182166101009390930a838102908302199091161790925582526020808301879052600060408085018290526001600160a01b038c168252600f90925290812080546060909401939092611e34911661589b565b825467ffffffffffffffff9182166101009390930a83810292021916179091558152602001611e6960808b0160608c01614b63565b6001600160a01b03168152602001888152602001898060200190611e8d919061550a565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001611ed460408b018b61556f565b808060200260200160405190810160405280939291908181526020016000905b82821015611f2057611f116040830286013681900381019061583c565b81526020019060010190611ef4565b505050505081526020018367ffffffffffffffff811115611f4357611f43614c61565b604051908082528060200260200182016040528015611f7657816020015b6060815260200190600190039081611f615790505b508152600060209091018190529091505b82811015612157576000611f9e60408b018b61556f565b83818110611fae57611fae615760565b905060400201803603810190611fc4919061583c565b90506000611fd68c8360000151610e45565b6001600160a01b031663968754458a611fef8e8061550a565b60208088015160408051928301815260008352517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815261205d959493927f00000000000000000000000000000000000000000000000048810ec3e431431f916004016158c2565b6000604051808303816000875af115801561207c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120a4919081019061591a565b82516001600160a01b03166000908152600e602052604090205481519192506e010000000000000000000000000000900463ffffffff1610156121215781516040517f36f536ca0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b80846101600151848151811061213957612139615760565b60200260200101819052505050806121509061578f565b9050611f87565b50612182817f771009c2ef94d6067e16ddda3d51ac246756ab9142343c08919e2bd21ae5e162613982565b6101808201526040517fd0c3c799bf9e2639de44391e7f524d229b2b55f5b1ea94b2bf7da42f7243dddd906121b8908390615a47565b60405180910390a1610180015193505050505b949350505050565b6121db612b9b565b6113d581613add565b6000546001600160a01b0316331480159061220a57506002546001600160a01b03163314155b801561221e575061221c600733613e35565b155b15612255576040517f195db95800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6010546c01000000000000000000000000900463ffffffff1660008190036122a9576040517f990e30bf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6010546bffffffffffffffffffffffff16818110156122f4576040517f8d0f71d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006122fe61304d565b1215612336576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600061234360076135c6565b905060005b818110156124385760008061235e6007846135d1565b909250905060008761237e836bffffffffffffffffffffffff8a16615665565b612388919061568f565b90506123948187615b7c565b95506123d86001600160a01b037f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a3241016846bffffffffffffffffffffffff841661310d565b6040516bffffffffffffffffffffffff821681526001600160a01b038416907f55fdec2aab60a41fa5abb106670eb1006f5aeaee1ba7afea2bc89b5b3ec7678f9060200160405180910390a2505050806124319061578f565b9050612348565b5050601080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff929092169190911790555050565b6000546001600160a01b031633148015906124a157506002546001600160a01b03163314155b156124d8576040517ffbdb8e5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d581613e4a565b6124e9612b9b565b6113d581613fd5565b60606000612500600a6140b0565b67ffffffffffffffff81111561251857612518614c61565b604051908082528060200260200182016040528015612541578160200160208202803683370190505b50905060005b815181101561259d5761255b600a826140bb565b5082828151811061256e5761256e615760565b60200260200101816001600160a01b03166001600160a01b031681525050806125969061578f565b9050612547565b5092915050565b60408051602081019091526000815260008290036125fa5750604080516020810190915267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000030d40168152610df5565b7f97a657c9000000000000000000000000000000000000000000000000000000006126258385615ba1565b7fffffffff00000000000000000000000000000000000000000000000000000000161461267e576040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61268b8260048186615be9565b810190610e9e9190615c13565b6006547801000000000000000000000000000000000000000000000000900463ffffffff1680841115612701576040517f8693378900000000000000000000000000000000000000000000000000000000815260048101829052602481018590526044016109f2565b6006547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16831115612763576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055474010000000000000000000000000000000000000000900461ffff168211156127bb576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6000808083815b81811015612abf5760008787838181106127e4576127e4615760565b9050604002018036038101906127fa919061583c565b80516001600160a01b03166000908152600e6020908152604091829020825160a081018452905463ffffffff8082168352640100000000820481169383019390935261ffff68010000000000000000820416938201939093526a01000000000000000000008304821660608201526e010000000000000000000000000000909204811660808301528251929350909161289791600a9190612f7116565b6128db5781516040517fbf16aab60000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b604081015160009061ffff16156129ff5760008c6001600160a01b031684600001516001600160a01b0316146129a25760065484516040517f4ab35b0b0000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201526401000000009092041690634ab35b0b90602401602060405180830381865afa158015612977573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299b9190615c55565b90506129a5565b508a5b620186a0836040015161ffff166129e78660200151847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166140ca90919063ffffffff16565b6129f19190615665565b6129fb919061568f565b9150505b6060820151612a0e9088615c70565b9650816080015186612a209190615c70565b8251909650600090612a3f9063ffffffff16662386f26fc10000615665565b905080821015612a5e57612a53818a61567c565b985050505050612aaf565b6000836020015163ffffffff16662386f26fc10000612a7d9190615665565b905080831115612a9d57612a91818b61567c565b99505050505050612aaf565b612aa7838b61567c565b995050505050505b612ab88161578f565b90506127c8565b50509450945094915050565b60008063ffffffff8316612ae0608086615665565b612aec8761022061567c565b612af6919061567c565b612b00919061567c565b6005546006549192506000917c010000000000000000000000000000000000000000000000000000000090910463ffffffff1690612b429061ffff1684615665565b612b4c919061567c565b60065490915062010000900461ffff16612b766dffffffffffffffffffffffffffff891683615665565b612b809190615665565b612b9090655af3107a4000615665565b979650505050505050565b6000546001600160a01b03163314612c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016109f2565b565b60005b8251811015612d72576000838281518110612c3157612c31615760565b60200260200101516000015190506000848381518110612c5357612c53615760565b6020026020010151602001519050612c7582600a612f7190919063ffffffff16565b612cb6576040517f73913ebd0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024016109f2565b6001600160a01b038116612ccb600a84612f86565b6001600160a01b031614612d0b576040517f6cc7b99800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d16600a83614107565b15612d5f57604080516001600160a01b038085168252831660208201527f987eb3c2f78454541205f72f34839b434c306c9eaf4922efd7c0c3060fdb2e4c910160405180910390a15b505080612d6b9061578f565b9050612c14565b5060005b8151811015612f6c576000828281518110612d9357612d93615760565b60200260200101516000015190506000838381518110612db557612db5615760565b602002602001015160200151905060006001600160a01b0316826001600160a01b03161480612deb57506001600160a01b038116155b15612e22576040517f6c2a418000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03166321df0da76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e849190615c8d565b6001600160a01b0316826001600160a01b031614612ece576040517f6cc7b99800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612eda600a838361411c565b15612f2757604080516001600160a01b038085168252831660208201527f95f865c2808f8b2a85eea2611db7843150ee7835ef1403f9755918a97d76933c910160405180910390a1612f59565b6040517f3caf458500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505080612f659061578f565b9050612d76565b505050565b6000610e9e836001600160a01b038416614132565b6000610e9e836001600160a01b03841661413e565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915261302982606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff164261300d9190615caa565b85608001516fffffffffffffffffffffffffffffffff1661414a565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b6010546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000916bffffffffffffffffffffffff16907f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324106001600160a01b0316906370a0823190602401602060405180830381865afa1580156130df573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061310391906156eb565b610e409190615cbd565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052612f6c908490614172565b805160408111156131ca576040517fb5a10cfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6010546c01000000000000000000000000900463ffffffff1615801590613218575060105463ffffffff6c010000000000000000000000008204166bffffffffffffffffffffffff90911610155b15613225576132256121e4565b600061323160076135c6565b90505b801561327357600061325261324a600184615caa565b6007906135d1565b509050613260600782614271565b50508061326c90615cdd565b9050613234565b506000805b8281101561338157600084828151811061329457613294615760565b602002602001015160000151905060008583815181106132b6576132b6615760565b60200260200101516020015190507f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324106001600160a01b0316826001600160a01b0316148061330b57506001600160a01b038216155b1561334d576040517f4de938d10000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024016109f2565b61335d60078361ffff8416614286565b5061336c61ffff821685615c70565b935050508061337a9061578f565b9050613278565b50601080547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff8416021790556040517f8c337bff38141c507abd25c547606bdde78fe8c12e941ab613f3a565fea6cd24906133f39083908690615d12565b60405180910390a1505050565b60005b815181101561359657600082828151811061342057613420615760565b6020908102919091018101516040805160a080820183528385015163ffffffff908116835283850151811683870190815260608087015161ffff9081168688019081526080808a0151861693880193845295890151851695870195865297516001600160a01b03166000908152600e9099529590972093518454915196519751935183166e010000000000000000000000000000027fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff9484166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff999097166801000000000000000002989098167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff978416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909316919093161717949094169390931791909117919091169190911790555061358f8161578f565b9050613403565b507f555c74101f7a15746d31c6731170310e667bcc607996b2fc0b981a7b26a416e9816040516111c99190615d31565b6000610df58261429c565b60008080806135e086866142a7565b909450925050505b9250929050565b815460009061361890700100000000000000000000000000000000900463ffffffff1642615caa565b905080156136ba5760018301548354613660916fffffffffffffffffffffffffffffffff8082169281169185917001000000000000000000000000000000009091041661414a565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b602082015183546136e0916fffffffffffffffffffffffffffffffff90811691166142d2565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19906133f39084908151151581526020808301516fffffffffffffffffffffffffffffffff90811691830191909152604092830151169181019190915260600190565b81516000805b82811015613974576000846001600160a01b031663d02641a08784815181106137f8576137f8615760565b6020908102919091010151516040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0390911660048201526024016040805180830381865afa15801561385f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138839190615db6565b5190507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116600003613908578582815181106138be576138be615760565b6020908102919091010151516040517f9a655f7b0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016109f2565b61395686838151811061391d5761391d615760565b602002602001015160200151827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166140ca90919063ffffffff16565b613960908461567c565b9250508061396d9061578f565b90506137cd565b506127bb60038260006142e8565b60008060001b8284602001518560400151866060015187608001518860a001518960c001518a60e001518b6101000151604051602001613a189897969594939291906001600160a01b039889168152968816602088015267ffffffffffffffff95861660408801526060870194909452911515608086015290921660a0840152921660c082015260e08101919091526101000190565b6040516020818303038152906040528051906020012085610120015180519060200120866101400151604051602001613a519190615de9565b60405160208183030381529060405280519060200120876101600151604051602001613a7d9190615dfc565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e00160405160208183030381529060405280519060200120905092915050565b60e08101516001600160a01b0316613b21576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600560008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548161ffff021916908361ffff16021790555060408201518160000160166101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001601a6101000a81548161ffff021916908361ffff160217905550608082015181600001601c6101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160006101000a81548161ffff021916908361ffff16021790555060c08201518160010160026101000a81548161ffff021916908361ffff16021790555060e08201518160010160046101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555061012082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055509050507f2a57f7c2027cf032c78b77d4d8d2fbd20ad22e5d5e5b5fb23ac7d7820d44adc66040518060e001604052807f000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324106001600160a01b031681526020017f0000000000000000000000000000000000000000000000008f90b8876dee653867ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000048810ec3e431431f67ffffffffffffffff1681526020017f0000000000000000000000000000000000000000000000000000000000030d4067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000052b7d2dcc80cd2e40000006bffffffffffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000099360767a4705f68cccb9533195b761648d6d8076001600160a01b0316815250826040516111c9929190615e0f565b6000610e9e836001600160a01b038416614637565b60005b8151811015613fa5576000828281518110613e6a57613e6a615760565b60209081029190910181015160408051608080820183528385015163ffffffff90811683528385015167ffffffffffffffff908116848801908152606080880151831686880190815294880151151590860190815296516001600160a01b03166000908152600d9098529490962092518354945192519551151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9688166c0100000000000000000000000002969096167fffffffffffffffffffffff000000000000000000ffffffffffffffffffffffff93909716640100000000027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090951691161792909217919091169290921717905550613f9e8161578f565b9050613e4d565b507f067924bf9277d905a9a4631a06d959bc032ace86b3caa835ae7e403d4f39010e816040516111c99190615e99565b336001600160a01b03821603614047576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016109f2565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000610df5826135c6565b60008080806135e086866135d1565b6000670de0b6b3a76400006140fd837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8616615665565b610e9e919061568f565b6000610e9e836001600160a01b038416614643565b60006121cb846001600160a01b0385168461464f565b6000610e9e8383614637565b6000610e9e8383614665565b60006141698561415a8486615665565b614164908761567c565b6142d2565b95945050505050565b60006141c7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166146ef9092919063ffffffff16565b805190915015612f6c57808060200190518101906141e591906157c7565b612f6c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016109f2565b6000610e9e836001600160a01b0384166146fe565b60006121cb846001600160a01b0385168461471b565b6000610df582614738565b600080806142b58585614742565b600081815260029690960160205260409095205494959350505050565b60008183106142e15781610e9e565b5090919050565b825474010000000000000000000000000000000000000000900460ff16158061430f575081155b1561431957505050565b825460018401546fffffffffffffffffffffffffffffffff8083169291169060009061435f90700100000000000000000000000000000000900463ffffffff1642615caa565b9050801561441f57818311156143a1576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546143db9083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661414a565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156144bc576001600160a01b038416614471576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016109f2565b6040517f1a76572a00000000000000000000000000000000000000000000000000000000815260048101839052602481018690526001600160a01b03851660448201526064016109f2565b848310156145b55760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169060009082906145009082615caa565b61450a878a615caa565b614514919061567c565b61451e919061568f565b90506001600160a01b03861661456a576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016109f2565b6040517fd0c8d23a00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526001600160a01b03871660448201526064016109f2565b6145bf8584615caa565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b6000610e9e838361474e565b6000610e9e83836146fe565b60006121cb84846001600160a01b03851661471b565b60008181526002830160205260408120548015158061468957506146898484614637565b610e9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b6579000060448201526064016109f2565b60606121cb8484600085614766565b60008181526002830160205260408120819055610e9e8383614867565b600082815260028401602052604081208290556121cb8484614873565b6000610df5825490565b6000610e9e838361487f565b60008181526001830160205260408120541515610e9e565b6060824710156147f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016109f2565b600080866001600160a01b031685876040516148149190615f1a565b60006040518083038185875af1925050503d8060008114614851576040519150601f19603f3d011682016040523d82523d6000602084013e614856565b606091505b5091509150612b90878383876148a9565b6000610e9e838361493c565b6000610e9e8383614a2f565b600082600001828154811061489657614896615760565b9060005260206000200154905092915050565b6060831561493257825160000361492b576001600160a01b0385163b61492b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016109f2565b50816121cb565b6121cb8383614a7e565b60008181526001830160205260408120548015614a25576000614960600183615caa565b855490915060009061497490600190615caa565b90508181146149d957600086600001828154811061499457614994615760565b90600052602060002001549050808760000184815481106149b7576149b7615760565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806149ea576149ea615f36565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610df5565b6000915050610df5565b6000818152600183016020526040812054614a7657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610df5565b506000610df5565b815115614a8e5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f29190614bd0565b60e08101610df582846001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015280606085015116606086015250506bffffffffffffffffffffffff60808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b6001600160a01b03811681146113d557600080fd5b8035614b5e81614b3e565b919050565b600060208284031215614b7557600080fd5b8135610e9e81614b3e565b60005b83811015614b9b578181015183820152602001614b83565b50506000910152565b60008151808452614bbc816020860160208601614b80565b601f01601f19169290920160200192915050565b602081526000610e9e6020830184614ba4565b67ffffffffffffffff811681146113d557600080fd5b600060a08284031215614c0b57600080fd5b50919050565b60008060408385031215614c2457600080fd5b8235614c2f81614be3565b9150602083013567ffffffffffffffff811115614c4b57600080fd5b614c5785828601614bf9565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614cb357614cb3614c61565b60405290565b60405160c0810167ffffffffffffffff81118282101715614cb357614cb3614c61565b604051610140810167ffffffffffffffff81118282101715614cb357614cb3614c61565b60405160a0810167ffffffffffffffff81118282101715614cb357614cb3614c61565b604051601f8201601f1916810167ffffffffffffffff81118282101715614d4c57614d4c614c61565b604052919050565b600067ffffffffffffffff821115614d6e57614d6e614c61565b5060051b60200190565b600082601f830112614d8957600080fd5b81356020614d9e614d9983614d54565b614d23565b82815260069290921b84018101918181019086841115614dbd57600080fd5b8286015b84811015614e0e5760408189031215614dda5760008081fd5b614de2614c90565b8135614ded81614b3e565b815281850135614dfc81614b3e565b81860152835291830191604001614dc1565b509695505050505050565b60008060408385031215614e2c57600080fd5b823567ffffffffffffffff80821115614e4457600080fd5b614e5086838701614d78565b93506020850135915080821115614e6657600080fd5b50614c5785828601614d78565b60008060408385031215614e8657600080fd5b8235614e9181614be3565b91506020830135614ea181614b3e565b809150509250929050565b60008060408385031215614ebf57600080fd5b8235614e9181614b3e565b80516001600160a01b031682526020810151614eec602084018261ffff169052565b506040810151614f04604084018263ffffffff169052565b506060810151614f1a606084018261ffff169052565b506080810151614f32608084018263ffffffff169052565b5060a0810151614f4860a084018261ffff169052565b5060c0810151614f5e60c084018261ffff169052565b5060e0810151614f7960e08401826001600160a01b03169052565b506101008181015163ffffffff81168483015250506101208181015163ffffffff8116848301526127bb565b6101408101610df58284614eca565b60008060208385031215614fc757600080fd5b823567ffffffffffffffff80821115614fdf57600080fd5b818501915085601f830112614ff357600080fd5b81358181111561500257600080fd5b8660208260061b850101111561501757600080fd5b60209290920196919550909350505050565b63ffffffff811681146113d557600080fd5b8035614b5e81615029565b803561ffff81168114614b5e57600080fd5b6000602080838503121561506b57600080fd5b823567ffffffffffffffff81111561508257600080fd5b8301601f8101851361509357600080fd5b80356150a1614d9982614d54565b81815260c091820283018401918482019190888411156150c057600080fd5b938501935b8385101561515c5780858a0312156150dd5760008081fd5b6150e5614cb9565b85356150f081614b3e565b8152858701356150ff81615029565b8188015260408681013561511281615029565b908201526060615123878201615046565b9082015260808681013561513681615029565b9082015260a08681013561514981615029565b90820152835293840193918501916150c5565b50979650505050505050565b600081518084526020808501945080840160005b838110156151b057815180516001600160a01b0316885283015161ffff16838801526040909601959082019060010161517c565b509495945050505050565b6040815260006151ce6040830185615168565b90508260208301529392505050565b80151581146113d557600080fd5b80356fffffffffffffffffffffffffffffffff81168114614b5e57600080fd5b60006060828403121561521d57600080fd5b6040516060810181811067ffffffffffffffff8211171561524057615240614c61565b604052823561524e816151dd565b815261525c602084016151eb565b602082015261526d604084016151eb565b60408201529392505050565b6000806000806080858703121561528f57600080fd5b843561529a81614be3565b9350602085013567ffffffffffffffff8111156152b657600080fd5b6152c287828801614bf9565b9350506040850135915060608501356152da81614b3e565b939692955090935050565b600061014082840312156152f857600080fd5b615300614cdc565b61530983614b53565b815261531760208401615046565b60208201526153286040840161503b565b604082015261533960608401615046565b606082015261534a6080840161503b565b608082015261535b60a08401615046565b60a082015261536c60c08401615046565b60c082015261537d60e08401614b53565b60e082015261010061539081850161503b565b908201526101206153a284820161503b565b908201529392505050565b600060208083850312156153c057600080fd5b823567ffffffffffffffff8111156153d757600080fd5b8301601f810185136153e857600080fd5b80356153f6614d9982614d54565b81815260a0918202830184019184820191908884111561541557600080fd5b938501935b8385101561515c5780858a0312156154325760008081fd5b61543a614d00565b853561544581614b3e565b81528587013561545481615029565b8188015260408681013561546781614be3565b9082015260608681013561547a81614be3565b9082015260808681013561548d816151dd565b908201528352938401939185019161541a565b6000602082840312156154b257600080fd5b8135610e9e81614be3565b6020808252825182820181905260009190848201906040850190845b818110156154fe5783516001600160a01b0316835292840192918401916001016154d9565b50909695505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261553f57600080fd5b83018035915067ffffffffffffffff82111561555a57600080fd5b6020019150368190038213156135e857600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126155a457600080fd5b83018035915067ffffffffffffffff8211156155bf57600080fd5b6020019150600681901b36038213156135e857600080fd5b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81168114614b5e57600080fd5b6000806040838503121561561657600080fd5b61561f836155d7565b915061562d602084016155d7565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610df557610df5615636565b80820180821115610df557610df5615636565b6000826156c5577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b67ffffffffffffffff81811683821601908082111561259d5761259d615636565b6000602082840312156156fd57600080fd5b5051919050565b60006040828403121561571657600080fd5b61571e614c90565b823561572981614b3e565b815261573760208401615046565b60208201529392505050565b60006020828403121561575557600080fd5b8151610e9e81614be3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036157c0576157c0615636565b5060010190565b6000602082840312156157d957600080fd5b8151610e9e816151dd565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6020815260006121cb6020830184866157e4565b60006020828403121561583557600080fd5b5035919050565b60006040828403121561584e57600080fd5b615856614c90565b823561586181614b3e565b81526020928301359281019290925250919050565b6bffffffffffffffffffffffff81811683821601908082111561259d5761259d615636565b600067ffffffffffffffff8083168181036158b8576158b8615636565b6001019392505050565b6001600160a01b038716815260a0602082015260006158e560a0830187896157e4565b85604084015267ffffffffffffffff85166060840152828103608084015261590d8185614ba4565b9998505050505050505050565b60006020828403121561592c57600080fd5b815167ffffffffffffffff8082111561594457600080fd5b818401915084601f83011261595857600080fd5b81518181111561596a5761596a614c61565b61597d6020601f19601f84011601614d23565b915080825285602082850101111561599457600080fd5b6159a5816020840160208601614b80565b50949350505050565b600081518084526020808501945080840160005b838110156151b057815180516001600160a01b0316885283015183880152604090960195908201906001016159c2565b600081518084526020808501808196508360051b8101915082860160005b85811015615a3a578284038952615a28848351614ba4565b98850198935090840190600101615a10565b5091979650505050505050565b60208152615a6260208201835167ffffffffffffffff169052565b60006020830151615a7e60408401826001600160a01b03169052565b5060408301516001600160a01b038116606084015250606083015167ffffffffffffffff8116608084015250608083015160a083015260a0830151615ac760c084018215159052565b5060c083015167ffffffffffffffff811660e08401525060e0830151610100615afa818501836001600160a01b03169052565b840151610120848101919091528401516101a061014080860182905291925090615b286101c0860184614ba4565b9250808601519050601f19610160818786030181880152615b4985846159ae565b945080880151925050610180818786030181880152615b6885846159f2565b970151959092019490945250929392505050565b6bffffffffffffffffffffffff82811682821603908082111561259d5761259d615636565b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015615be15780818660040360031b1b83161692505b505092915050565b60008085851115615bf957600080fd5b83861115615c0657600080fd5b5050820193919092039150565b600060208284031215615c2557600080fd5b6040516020810181811067ffffffffffffffff82111715615c4857615c48614c61565b6040529135825250919050565b600060208284031215615c6757600080fd5b610e9e826155d7565b63ffffffff81811683821601908082111561259d5761259d615636565b600060208284031215615c9f57600080fd5b8151610e9e81614b3e565b81810381811115610df557610df5615636565b818103600083128015838313168383128216171561259d5761259d615636565b600081615cec57615cec615636565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b63ffffffff831681526040602082015260006121cb6040830184615168565b602080825282518282018190526000919060409081850190868401855b82811015615a3a57815180516001600160a01b031685528681015163ffffffff908116888701528682015181168787015260608083015161ffff169087015260808083015182169087015260a091820151169085015260c09093019290850190600101615d4e565b600060408284031215615dc857600080fd5b615dd0614c90565b615dd9836155d7565b8152602083015161573781615029565b602081526000610e9e60208301846159ae565b602081526000610e9e60208301846159f2565b6102208101615e8c82856001600160a01b03808251168352602082015167ffffffffffffffff808216602086015280604085015116604086015280606085015116606086015250506bffffffffffffffffffffffff60808301511660808401528060a08301511660a08401528060c08301511660c0840152505050565b610e9e60e0830184614eca565b602080825282518282018190526000919060409081850190868401855b82811015615a3a57815180516001600160a01b031685528681015163ffffffff16878601528581015167ffffffffffffffff908116878701526060808301519091169086015260809081015115159085015260a09093019290850190600101615eb6565b60008251615f2c818460208701614b80565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000813000a

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

000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a324100000000000000000000000000000000000000000000000008f90b8876dee653800000000000000000000000000000000000000000000000048810ec3e431431f0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000052b7d2dcc80cd2e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099360767a4705f68cccb9533195b761648d6d807000000000000000000000000d3b06cebf099ce7da4accf578aaebfdbd6e88a930000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000005573000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d20536e60832be579cd38e89dc03d11e1741fba000000000000000000000000000000000000000000000000000000000000753000000000000000000000000000000000000000000000000000000000002dc6c00000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000152d02c7e14af68000000000000000000000000000000000000000000000000000090d972f32323c00000000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a3241000000000000000000000000099685281ec520a003f1a726a5a8078c2124c1477000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000004d57c6d8037c65fa66d6231844785a428310a73500000000000000000000000088a2d74f47a237a62e7a51cdda67270ce381555e000000000000000000000000477099a1e7812c09d4df5a9158f97fb7b7105dc5000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c000000000000000000000000b2958d1bd07448865e555feeff32b58d254ffb4c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000c7d713b49da000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000071480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000001d4c0000000000000000000000000000000000000000000000000000000000000028000000000000000000000000088a2d74f47a237a62e7a51cdda67270ce381555e000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000071480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000714800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : staticConfig (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [1] : dynamicConfig (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [2] : tokensAndPools (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [3] : rateLimiterConfig (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [4] : feeTokenConfigs (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : tokenTransferFeeConfigArgs (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
70 Constructor Arguments found :
Arg [0] : 000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410
Arg [1] : 0000000000000000000000000000000000000000000000008f90b8876dee6538
Arg [2] : 00000000000000000000000000000000000000000000000048810ec3e431431f
Arg [3] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [4] : 00000000000000000000000000000000000000000052b7d2dcc80cd2e4000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 00000000000000000000000099360767a4705f68cccb9533195b761648d6d807
Arg [7] : 000000000000000000000000d3b06cebf099ce7da4accf578aaebfdbd6e88a93
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 0000000000000000000000000000000000000000000000000000000000055730
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [14] : 0000000000000000000000004d20536e60832be579cd38e89dc03d11e1741fba
Arg [15] : 0000000000000000000000000000000000000000000000000000000000007530
Arg [16] : 00000000000000000000000000000000000000000000000000000000002dc6c0
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000300
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [19] : 00000000000000000000000000000000000000000000152d02c7e14af6800000
Arg [20] : 0000000000000000000000000000000000000000000000090d972f32323c0000
Arg [21] : 0000000000000000000000000000000000000000000000000000000000000420
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000580
Arg [23] : 00000000000000000000000000000000000000000000000000000000000008a0
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [25] : 000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410
Arg [26] : 00000000000000000000000099685281ec520a003f1a726a5a8078c2124c1477
Arg [27] : 000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e
Arg [28] : 0000000000000000000000004d57c6d8037c65fa66d6231844785a428310a735
Arg [29] : 00000000000000000000000088a2d74f47a237a62e7a51cdda67270ce381555e
Arg [30] : 000000000000000000000000477099a1e7812c09d4df5a9158f97fb7b7105dc5
Arg [31] : 000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c
Arg [32] : 000000000000000000000000b2958d1bd07448865e555feeff32b58d254ffb4c
Arg [33] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [34] : 000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410
Arg [35] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [36] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [37] : 0000000000000000000000000000000000000000000000000c7d713b49da0000
Arg [38] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [39] : 0000000000000000000000004200000000000000000000000000000000000006
Arg [40] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [41] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [42] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [43] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [44] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [45] : 000000000000000000000000e4ab69c077896252fafbd49efd26b5d171a32410
Arg [46] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [47] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [48] : 0000000000000000000000000000000000000000000000000000000000000046
Arg [49] : 0000000000000000000000000000000000000000000000000000000000007148
Arg [50] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [51] : 000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e
Arg [52] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [53] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [54] : 0000000000000000000000000000000000000000000000000000000000000046
Arg [55] : 000000000000000000000000000000000000000000000000000000000001d4c0
Arg [56] : 0000000000000000000000000000000000000000000000000000000000000280
Arg [57] : 00000000000000000000000088a2d74f47a237a62e7a51cdda67270ce381555e
Arg [58] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [59] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [60] : 0000000000000000000000000000000000000000000000000000000000000046
Arg [61] : 0000000000000000000000000000000000000000000000000000000000007148
Arg [62] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [63] : 000000000000000000000000a98fa8a008371b9408195e52734b1768c0d1cb5c
Arg [64] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [65] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [66] : 0000000000000000000000000000000000000000000000000000000000000046
Arg [67] : 0000000000000000000000000000000000000000000000000000000000007148
Arg [68] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [69] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading