ETH Price: $4,463.03 (-4.50%)

Contract

0x1E11bAB3f07fa72312182fFDc460AE45400E6e7b

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Advanced mode:
Parent Transaction Hash Block From To
View All Internal Transactions

Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xc23071a8...9780f632A
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
OnRamp

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 80000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IEVM2AnyOnRampClient} from "../interfaces/IEVM2AnyOnRampClient.sol";
import {IFeeQuoter} from "../interfaces/IFeeQuoter.sol";
import {IMessageInterceptor} from "../interfaces/IMessageInterceptor.sol";
import {INonceManager} from "../interfaces/INonceManager.sol";
import {IPoolV1} from "../interfaces/IPool.sol";
import {IRMNRemote} from "../interfaces/IRMNRemote.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol";
import {ITypeAndVersion} from "@chainlink/shared/interfaces/ITypeAndVersion.sol";

import {Client} from "../libraries/Client.sol";
import {Internal} from "../libraries/Internal.sol";
import {Pool} from "../libraries/Pool.sol";
import {USDPriceWith18Decimals} from "../libraries/USDPriceWith18Decimals.sol";
import {Ownable2StepMsgSender} from "@chainlink/shared/access/Ownable2StepMsgSender.sol";

import {IERC20} from "@chainlink/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@chainlink/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
import {EnumerableSet} from "@chainlink/vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol";

/// @notice The OnRamp is a contract that handles lane-specific fee logic.
/// @dev The OnRamp and OffRamp form a cross chain upgradeable unit. Any change to one of them results in an onchain
/// upgrade of both contracts.
contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender {
  using SafeERC20 for IERC20;
  using EnumerableSet for EnumerableSet.AddressSet;
  using USDPriceWith18Decimals for uint224;

  error CannotSendZeroTokens();
  error UnsupportedToken(address token);
  error MustBeCalledByRouter();
  error RouterMustSetOriginalSender();
  error InvalidConfig();
  error CursedByRMN(uint64 destChainSelector);
  error GetSupportedTokensFunctionalityRemovedCheckAdminRegistry();
  error InvalidDestChainConfig(uint64 destChainSelector);
  error OnlyCallableByOwnerOrAllowlistAdmin();
  error SenderNotAllowed(address sender);
  error InvalidAllowListRequest(uint64 destChainSelector);
  error ReentrancyGuardReentrantCall();

  event ConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig);
  event DestChainConfigSet(
    uint64 indexed destChainSelector, uint64 sequenceNumber, IRouter router, bool allowlistEnabled
  );
  event FeeTokenWithdrawn(address indexed feeAggregator, address indexed feeToken, uint256 amount);
  /// RMN depends on this event, if changing, please notify the RMN maintainers.
  event CCIPMessageSent(
    uint64 indexed destChainSelector, uint64 indexed sequenceNumber, Internal.EVM2AnyRampMessage message
  );
  event AllowListAdminSet(address indexed allowlistAdmin);
  event AllowListSendersAdded(uint64 indexed destChainSelector, address[] senders);
  event AllowListSendersRemoved(uint64 indexed destChainSelector, address[] senders);

  /// @dev Struct that contains the static configuration.
  /// RMN depends on this struct, if changing, please notify the RMN maintainers.
  // solhint-disable-next-line gas-struct-packing
  struct StaticConfig {
    uint64 chainSelector; // ────╮ Source chain selector.
    IRMNRemote rmnRemote; // ────╯ RMN remote address.
    address nonceManager; //       Nonce manager address.
    address tokenAdminRegistry; // Token admin registry address.
  }

  /// @dev Struct that contains the dynamic configuration
  // solhint-disable-next-line gas-struct-packing
  struct DynamicConfig {
    address feeQuoter; // FeeQuoter address.
    bool reentrancyGuardEntered; // Reentrancy protection.
    address messageInterceptor; // Optional message interceptor to validate messages. Zero address = no interceptor.
    address feeAggregator; // Fee aggregator address.
    address allowlistAdmin; // authorized admin to add or remove allowed senders.
  }

  /// @dev Struct to hold the configs for a single destination chain.
  struct DestChainConfig {
    // The last used sequence number. This is zero in the case where no messages have yet been sent.
    // 0 is not a valid sequence number for any real transaction as this value will be incremented before use.
    uint64 sequenceNumber; // ──╮ The last used sequence number.
    bool allowlistEnabled; //   │ True if the allowlist is enabled.
    IRouter router; // ─────────╯ Local router address  that is allowed to send messages to the destination chain.
    EnumerableSet.AddressSet allowedSendersList; // The list of addresses allowed to send messages.
  }

  /// @dev Same as DestChainConfig but with the destChainSelector so that an array of these can be passed in the
  /// constructor and the applyDestChainConfigUpdates function.
  // solhint-disable gas-struct-packing
  struct DestChainConfigArgs {
    uint64 destChainSelector; // ─╮ Destination chain selector.
    IRouter router; //            │ Source router address.
    bool allowlistEnabled; // ────╯ True if the allowlist is enabled.
  }

  /// @dev Struct to hold the allowlist configuration args per dest chain.
  struct AllowlistConfigArgs {
    uint64 destChainSelector; // ──╮ Destination chain selector.
    bool allowlistEnabled; // ─────╯ True if the allowlist is enabled.
    address[] addedAllowlistedSenders; // list of senders to be added to the allowedSendersList.
    address[] removedAllowlistedSenders; // list of senders to be removed from the allowedSendersList.
  }

  // STATIC CONFIG
  string public constant override typeAndVersion = "OnRamp 1.6.0";
  /// @dev The chain ID of the source chain that this contract is deployed to.
  uint64 private immutable i_chainSelector;
  /// @dev The rmn contract.
  IRMNRemote private immutable i_rmnRemote;
  /// @dev The address of the nonce manager.
  address private immutable i_nonceManager;
  /// @dev The address of the token admin registry.
  address private immutable i_tokenAdminRegistry;

  // DYNAMIC CONFIG
  /// @dev The dynamic config for the onRamp.
  DynamicConfig private s_dynamicConfig;

  /// @dev The destination chain specific configs.
  mapping(uint64 destChainSelector => DestChainConfig destChainConfig) private s_destChainConfigs;

  constructor(
    StaticConfig memory staticConfig,
    DynamicConfig memory dynamicConfig,
    DestChainConfigArgs[] memory destChainConfigArgs
  ) {
    if (
      staticConfig.chainSelector == 0 || address(staticConfig.rmnRemote) == address(0)
        || staticConfig.nonceManager == address(0) || staticConfig.tokenAdminRegistry == address(0)
    ) {
      revert InvalidConfig();
    }

    i_chainSelector = staticConfig.chainSelector;
    i_rmnRemote = staticConfig.rmnRemote;
    i_nonceManager = staticConfig.nonceManager;
    i_tokenAdminRegistry = staticConfig.tokenAdminRegistry;

    _setDynamicConfig(dynamicConfig);
    _applyDestChainConfigUpdates(destChainConfigArgs);
  }

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

  /// @notice Gets the next sequence number to be used in the onRamp.
  /// @param destChainSelector The destination chain selector.
  /// @return nextSequenceNumber The next sequence number to be used.
  function getExpectedNextSequenceNumber(
    uint64 destChainSelector
  ) external view returns (uint64) {
    return s_destChainConfigs[destChainSelector].sequenceNumber + 1;
  }

  /// @inheritdoc IEVM2AnyOnRampClient
  function forwardFromRouter(
    uint64 destChainSelector,
    Client.EVM2AnyMessage calldata message,
    uint256 feeTokenAmount,
    address originalSender
  ) external returns (bytes32) {
    // We rely on a reentrancy guard here due to the untrusted calls performed to the pools. This enables some
    // optimizations by not following the CEI pattern.
    if (s_dynamicConfig.reentrancyGuardEntered) revert ReentrancyGuardReentrantCall();
    s_dynamicConfig.reentrancyGuardEntered = true;

    DestChainConfig storage destChainConfig = s_destChainConfigs[destChainSelector];

    // NOTE: assumes the message has already been validated through the getFee call.
    // Validate originalSender is set and allowed. Not validated in `getFee` since it is not user-driven.
    if (originalSender == address(0)) revert RouterMustSetOriginalSender();

    if (destChainConfig.allowlistEnabled) {
      if (!destChainConfig.allowedSendersList.contains(originalSender)) {
        revert SenderNotAllowed(originalSender);
      }
    }

    // Router address may be zero intentionally to pause, which should stop all messages.
    if (msg.sender != address(destChainConfig.router)) revert MustBeCalledByRouter();

    {
      // scoped to reduce stack usage
      address messageInterceptor = s_dynamicConfig.messageInterceptor;
      if (messageInterceptor != address(0)) {
        IMessageInterceptor(messageInterceptor).onOutboundMessage(destChainSelector, message);
      }
    }

    Internal.EVM2AnyRampMessage memory newMessage = Internal.EVM2AnyRampMessage({
      header: Internal.RampMessageHeader({
        // Should be generated after the message is complete.
        messageId: "",
        sourceChainSelector: i_chainSelector,
        destChainSelector: destChainSelector,
        // We need the next available sequence number so we increment before we use the value.
        sequenceNumber: ++destChainConfig.sequenceNumber,
        // Only bump nonce for messages that specify allowOutOfOrderExecution == false. Otherwise, we may block ordered
        // message nonces, which is not what we want.
        nonce: 0
      }),
      sender: originalSender,
      data: message.data,
      extraArgs: "",
      receiver: message.receiver,
      feeToken: message.feeToken,
      feeTokenAmount: feeTokenAmount,
      feeValueJuels: 0, // calculated later.
      // Should be populated via lock / burn pool calls.
      tokenAmounts: new Internal.EVM2AnyTokenTransfer[](message.tokenAmounts.length)
    });

    // Convert message fee to juels and retrieve converted args.
    // Validate pool return data after it is populated (view function - no state changes).
    bool isOutOfOrderExecution;
    bytes memory tokenReceiver;
    (newMessage.feeValueJuels, isOutOfOrderExecution, newMessage.extraArgs, tokenReceiver) = IFeeQuoter(
      s_dynamicConfig.feeQuoter
    ).processMessageArgs(destChainSelector, message.feeToken, feeTokenAmount, message.extraArgs, message.receiver);

    Client.EVMTokenAmount[] memory tokenAmounts = message.tokenAmounts;
    // Lock / burn the tokens as last step. TokenPools may not always be trusted.
    for (uint256 i = 0; i < message.tokenAmounts.length; ++i) {
      newMessage.tokenAmounts[i] =
        _lockOrBurnSingleToken(tokenAmounts[i], destChainSelector, tokenReceiver, originalSender);
    }

    bytes[] memory destExecDataPerToken = IFeeQuoter(s_dynamicConfig.feeQuoter).processPoolReturnData(
      destChainSelector, newMessage.tokenAmounts, tokenAmounts
    );
    newMessage.header.nonce = isOutOfOrderExecution
      ? 0
      : INonceManager(i_nonceManager).getIncrementedOutboundNonce(destChainSelector, originalSender);

    for (uint256 i = 0; i < newMessage.tokenAmounts.length; ++i) {
      newMessage.tokenAmounts[i].destExecData = destExecDataPerToken[i];
    }

    newMessage = _postProcessMessage(newMessage);

    // Hash only after all fields have been set.
    newMessage.header.messageId = Internal._hash(
      newMessage,
      // Metadata hash preimage to ensure global uniqueness, ensuring 2 identical messages sent to 2 different lanes
      // will have a distinct hash.
      keccak256(abi.encode(Internal.EVM_2_ANY_MESSAGE_HASH, i_chainSelector, destChainSelector, address(this)))
    );

    // Emit message request.
    // This must happen after any pool events as some tokens (e.g. USDC) emit events that we expect to precede this
    // event in the offchain code.
    emit CCIPMessageSent(destChainSelector, newMessage.header.sequenceNumber, newMessage);

    s_dynamicConfig.reentrancyGuardEntered = false;

    return newMessage.header.messageId;
  }

  /// @notice hook for applying custom logic to the message from the router
  /// @param message router message
  /// @return transformedMessage modified message
  function _postProcessMessage(
    Internal.EVM2AnyRampMessage memory message
  ) internal virtual returns (Internal.EVM2AnyRampMessage memory transformedMessage) {
    return message;
  }

  /// @notice Uses a pool to lock or burn a token.
  /// @param tokenAndAmount Token address and amount to lock or burn.
  /// @param destChainSelector Target destination chain selector of the message.
  /// @param receiver Message receiver.
  /// @param originalSender Message sender.
  /// @return evm2AnyTokenTransfer EVM2Any token and amount data.
  function _lockOrBurnSingleToken(
    Client.EVMTokenAmount memory tokenAndAmount,
    uint64 destChainSelector,
    bytes memory receiver,
    address originalSender
  ) internal returns (Internal.EVM2AnyTokenTransfer memory) {
    if (tokenAndAmount.amount == 0) revert CannotSendZeroTokens();

    IPoolV1 sourcePool = getPoolBySourceToken(destChainSelector, IERC20(tokenAndAmount.token));
    // We don't have to check if it supports the pool version in a non-reverting way here because
    // if we revert here, there is no effect on CCIP. Therefore we directly call the supportsInterface
    // function and not through the ERC165Checker.
    if (address(sourcePool) == address(0) || !sourcePool.supportsInterface(Pool.CCIP_POOL_V1)) {
      revert UnsupportedToken(tokenAndAmount.token);
    }

    Pool.LockOrBurnOutV1 memory poolReturnData = sourcePool.lockOrBurn(
      Pool.LockOrBurnInV1({
        receiver: receiver,
        remoteChainSelector: destChainSelector,
        originalSender: originalSender,
        amount: tokenAndAmount.amount,
        localToken: tokenAndAmount.token
      })
    );

    // NOTE: pool data validations are outsourced to the FeeQuoter to handle family-specific logic handling.
    return Internal.EVM2AnyTokenTransfer({
      sourcePoolAddress: address(sourcePool),
      destTokenAddress: poolReturnData.destTokenAddress,
      extraData: poolReturnData.destPoolData,
      amount: tokenAndAmount.amount,
      destExecData: "" // This is set in the processPoolReturnData function.
    });
  }

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

  /// @notice Returns the static onRamp config.
  /// @dev RMN depends on this function, if modified, please notify the RMN maintainers.
  /// @return staticConfig the static configuration.
  function getStaticConfig() external view returns (StaticConfig memory) {
    return StaticConfig({
      chainSelector: i_chainSelector,
      rmnRemote: i_rmnRemote,
      nonceManager: i_nonceManager,
      tokenAdminRegistry: i_tokenAdminRegistry
    });
  }

  /// @notice Returns the dynamic onRamp config.
  /// @return dynamicConfig the dynamic 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 {
    if (
      dynamicConfig.feeQuoter == address(0) || dynamicConfig.feeAggregator == address(0)
        || dynamicConfig.reentrancyGuardEntered
    ) revert InvalidConfig();

    s_dynamicConfig = dynamicConfig;

    emit ConfigSet(
      StaticConfig({
        chainSelector: i_chainSelector,
        rmnRemote: i_rmnRemote,
        nonceManager: i_nonceManager,
        tokenAdminRegistry: i_tokenAdminRegistry
      }),
      dynamicConfig
    );
  }

  /// @notice Updates destination chains specific configs.
  /// @param destChainConfigArgs Array of destination chain specific configs.
  function applyDestChainConfigUpdates(
    DestChainConfigArgs[] memory destChainConfigArgs
  ) external onlyOwner {
    _applyDestChainConfigUpdates(destChainConfigArgs);
  }

  /// @notice Internal version of applyDestChainConfigUpdates.
  function _applyDestChainConfigUpdates(
    DestChainConfigArgs[] memory destChainConfigArgs
  ) internal {
    for (uint256 i = 0; i < destChainConfigArgs.length; ++i) {
      DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[i];
      uint64 destChainSelector = destChainConfigArgs[i].destChainSelector;

      if (destChainSelector == 0) {
        revert InvalidDestChainConfig(destChainSelector);
      }

      DestChainConfig storage destChainConfig = s_destChainConfigs[destChainSelector];
      // The router can be zero to pause the destination chain
      destChainConfig.router = destChainConfigArg.router;
      destChainConfig.allowlistEnabled = destChainConfigArg.allowlistEnabled;

      emit DestChainConfigSet(
        destChainSelector, destChainConfig.sequenceNumber, destChainConfigArg.router, destChainConfig.allowlistEnabled
      );
    }
  }

  /// @notice get ChainConfig configured for the DestinationChainSelector.
  /// @param destChainSelector The destination chain selector.
  /// @return sequenceNumber The last used sequence number.
  /// @return allowlistEnabled boolean indicator to specify if allowlist check is enabled.
  /// @return router address of the router.
  function getDestChainConfig(
    uint64 destChainSelector
  ) external view returns (uint64 sequenceNumber, bool allowlistEnabled, address router) {
    DestChainConfig storage config = s_destChainConfigs[destChainSelector];
    sequenceNumber = config.sequenceNumber;
    allowlistEnabled = config.allowlistEnabled;
    router = address(config.router);
    return (sequenceNumber, allowlistEnabled, router);
  }

  /// @notice get allowedSenders List configured for the DestinationChainSelector.
  /// @param destChainSelector The destination chain selector.
  /// @return isEnabled True if allowlist is enabled.
  /// @return configuredAddresses This is always populated with the list of allowed senders, even if the allowlist
  /// is turned off. This is because the only way to know what addresses are configured is through this function. If
  /// it would return an empty list when the allowlist is disabled, it would be impossible to know what addresses are
  /// configured.
  function getAllowedSendersList(
    uint64 destChainSelector
  ) external view returns (bool isEnabled, address[] memory configuredAddresses) {
    return (
      s_destChainConfigs[destChainSelector].allowlistEnabled,
      s_destChainConfigs[destChainSelector].allowedSendersList.values()
    );
  }

  // ================================================================
  // │                          Allowlist                           │
  // ================================================================

  /// @notice Updates allowlistConfig for Senders.
  /// @dev configuration used to set the list of senders who are authorized to send messages.
  /// @param allowlistConfigArgsItems Array of AllowlistConfigArguments where each item is for a destChainSelector.
  function applyAllowlistUpdates(
    AllowlistConfigArgs[] calldata allowlistConfigArgsItems
  ) external {
    if (msg.sender != owner()) {
      if (msg.sender != s_dynamicConfig.allowlistAdmin) {
        revert OnlyCallableByOwnerOrAllowlistAdmin();
      }
    }

    for (uint256 i = 0; i < allowlistConfigArgsItems.length; ++i) {
      AllowlistConfigArgs memory allowlistConfigArgs = allowlistConfigArgsItems[i];

      DestChainConfig storage destChainConfig = s_destChainConfigs[allowlistConfigArgs.destChainSelector];
      destChainConfig.allowlistEnabled = allowlistConfigArgs.allowlistEnabled;

      if (allowlistConfigArgs.addedAllowlistedSenders.length > 0) {
        if (allowlistConfigArgs.allowlistEnabled) {
          for (uint256 j = 0; j < allowlistConfigArgs.addedAllowlistedSenders.length; ++j) {
            address toAdd = allowlistConfigArgs.addedAllowlistedSenders[j];
            if (toAdd == address(0)) {
              revert InvalidAllowListRequest(allowlistConfigArgs.destChainSelector);
            }
            destChainConfig.allowedSendersList.add(toAdd);
          }

          emit AllowListSendersAdded(allowlistConfigArgs.destChainSelector, allowlistConfigArgs.addedAllowlistedSenders);
        } else {
          revert InvalidAllowListRequest(allowlistConfigArgs.destChainSelector);
        }
      }

      for (uint256 j = 0; j < allowlistConfigArgs.removedAllowlistedSenders.length; ++j) {
        destChainConfig.allowedSendersList.remove(allowlistConfigArgs.removedAllowlistedSenders[j]);
      }

      if (allowlistConfigArgs.removedAllowlistedSenders.length > 0) {
        emit AllowListSendersRemoved(
          allowlistConfigArgs.destChainSelector, allowlistConfigArgs.removedAllowlistedSenders
        );
      }
    }
  }

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

  /// @inheritdoc IEVM2AnyOnRampClient
  function getPoolBySourceToken(uint64, /*destChainSelector*/ IERC20 sourceToken) public view returns (IPoolV1) {
    return IPoolV1(ITokenAdminRegistry(i_tokenAdminRegistry).getPool(address(sourceToken)));
  }

  /// @inheritdoc IEVM2AnyOnRampClient
  function getSupportedTokens(
    uint64 // destChainSelector
  ) external pure returns (address[] memory) {
    revert GetSupportedTokensFunctionalityRemovedCheckAdminRegistry();
  }

  // ================================================================
  // │                             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 (i_rmnRemote.isCursed(bytes16(uint128(destChainSelector)))) revert CursedByRMN(destChainSelector);

    return IFeeQuoter(s_dynamicConfig.feeQuoter).getValidatedFee(destChainSelector, message);
  }

  /// @notice Withdraws the outstanding fee token balances to the fee aggregator.
  /// @param feeTokens The fee tokens to withdraw.
  /// @dev This function can be permissionless as it only transfers tokens to the fee aggregator which is a trusted address.
  function withdrawFeeTokens(
    address[] calldata feeTokens
  ) external {
    address feeAggregator = s_dynamicConfig.feeAggregator;

    for (uint256 i = 0; i < feeTokens.length; ++i) {
      IERC20 feeToken = IERC20(feeTokens[i]);
      uint256 feeTokenBalance = feeToken.balanceOf(address(this));

      if (feeTokenBalance > 0) {
        feeToken.safeTransfer(feeAggregator, feeTokenBalance);

        emit FeeTokenWithdrawn(feeAggregator, address(feeToken), feeTokenBalance);
      }
    }
  }
}

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

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

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

import {IERC20} from "@chainlink/vendor/openzeppelin-solidity/v4.8.3/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 (IPoolV1);

  /// @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.
  /// @return messageId The message id.
  function forwardFromRouter(
    uint64 destChainSelector,
    Client.EVM2AnyMessage memory message,
    uint256 feeTokenAmount,
    address originalSender
  ) external returns (bytes32);
}

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

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

interface IFeeQuoter is IPriceRegistry {
  /// @notice Validates the ccip message & returns the fee.
  /// @param destChainSelector The destination chain selector.
  /// @param message The message to get quote for.
  /// @return feeTokenAmount The amount of fee token needed for the fee, in smallest denomination of the fee token.
  function getValidatedFee(
    uint64 destChainSelector,
    Client.EVM2AnyMessage calldata message
  ) external view returns (uint256 feeTokenAmount);

  /// @notice Converts the extraArgs to the latest version and returns the converted message fee in juels.
  /// @notice Validates pool return data.
  /// @param destChainSelector destination chain selector to process, must be a configured valid chain.
  /// @param feeToken token address used to pay for message fees, must be a configured valid fee token.
  /// @param feeTokenAmount Fee token amount.
  /// @param extraArgs Message extra args that were passed in by the client.
  /// @param messageReceiver Message receiver address in bytes from EVM2AnyMessage.receiver
  /// @return msgFeeJuels message fee in juels.
  /// @return isOutOfOrderExecution true if the message should be executed out of order.
  /// @return convertedExtraArgs extra args converted to the latest family-specific args version.
  /// @return tokenReceiver token receiver address in bytes on destination chain
  function processMessageArgs(
    uint64 destChainSelector,
    address feeToken,
    uint256 feeTokenAmount,
    bytes calldata extraArgs,
    bytes calldata messageReceiver
  )
    external
    view
    returns (
      uint256 msgFeeJuels,
      bool isOutOfOrderExecution,
      bytes memory convertedExtraArgs,
      bytes memory tokenReceiver
    );

  /// @notice Validates pool return data.
  /// @param destChainSelector Destination chain selector to which the token amounts are sent to.
  /// @param onRampTokenTransfers Token amounts with populated pool return data.
  /// @param sourceTokenAmounts Token amounts originally sent in a Client.EVM2AnyMessage message.
  /// @return destExecDataPerToken Destination chain execution data.
  function processPoolReturnData(
    uint64 destChainSelector,
    Internal.EVM2AnyTokenTransfer[] calldata onRampTokenTransfers,
    Client.EVMTokenAmount[] calldata sourceTokenAmounts
  ) external view returns (bytes[] memory destExecDataPerToken);
}

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

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

/// @notice Interface for plug-in message hook contracts that intercept OffRamp & OnRamp messages and perform
/// validations / state changes on top of the messages. The interceptor functions are expected to revert
/// on validation failures.
interface IMessageInterceptor {
  /// @notice Common error that can be thrown on validation failures and used by consumers.
  /// @param errorReason abi encoded revert reason.
  error MessageValidationError(bytes errorReason);

  /// @notice Intercepts & validates the given OffRamp message. Reverts on validation failure.
  /// @param message to validate.
  function onInboundMessage(
    Client.Any2EVMMessage memory message
  ) external;

  /// @notice Intercepts & validates the given OnRamp message. Reverts on validation failure.
  /// @param destChainSelector remote destination chain selector where the message is being sent to.
  /// @param message to validate.
  function onOutboundMessage(uint64 destChainSelector, Client.EVM2AnyMessage memory message) external;
}

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

/// @notice Contract interface that allows managing sender nonces.
interface INonceManager {
  /// @notice Increments the outbound nonce for a given sender on a given destination chain.
  /// @param destChainSelector The destination chain selector.
  /// @param sender The sender address.
  /// @return incrementedOutboundNonce The new outbound nonce.
  function getIncrementedOutboundNonce(uint64 destChainSelector, address sender) external returns (uint64);

  /// @notice Increments the inbound nonce for a given sender on a given source chain.
  /// @notice The increment is only applied if the resulting nonce matches the expectedNonce.
  /// @param sourceChainSelector The destination chain selector.
  /// @param expectedNonce The expected inbound nonce.
  /// @param sender The encoded sender address.
  /// @return incremented True if the nonce was incremented, false otherwise.
  function incrementInboundNonce(
    uint64 sourceChainSelector,
    uint64 expectedNonce,
    bytes calldata sender
  ) external returns (bool);
}

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

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

import {IERC165} from "@chainlink/vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol";

/// @notice Shared public interface for multiple V1 pool types.
/// Each pool type handles a different child token model e.g. lock/unlock, mint/burn.
interface IPoolV1 is IERC165 {
  /// @notice Lock tokens into the pool or burn the tokens.
  /// @param lockOrBurnIn Encoded data fields for the processing of tokens on the source chain.
  /// @return lockOrBurnOut Encoded data fields for the processing of tokens on the destination chain.
  function lockOrBurn(
    Pool.LockOrBurnInV1 calldata lockOrBurnIn
  ) external returns (Pool.LockOrBurnOutV1 memory lockOrBurnOut);

  /// @notice Releases or mints tokens to the receiver address.
  /// @param releaseOrMintIn All data required to release or mint tokens.
  /// @return releaseOrMintOut The amount of tokens released or minted on the local chain, denominated
  /// in the local token's decimals.
  /// @dev The offramp asserts that the balanceOf of the receiver has been incremented by exactly the number
  /// of tokens that is returned in ReleaseOrMintOutV1.destinationAmount. If the amounts do not match, the tx reverts.
  function releaseOrMint(
    Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
  ) external returns (Pool.ReleaseOrMintOutV1 memory);

  /// @notice Checks whether a remote chain is supported in the token pool.
  /// @param remoteChainSelector The selector of the remote chain.
  /// @return true if the given chain is a permissioned remote chain.
  function isSupportedChain(
    uint64 remoteChainSelector
  ) external view returns (bool);

  /// @notice Returns if the token pool supports the given token.
  /// @param token The address of the token.
  /// @return true if the token is supported by the pool.
  function isSupportedToken(
    address token
  ) external view returns (bool);
}

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

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

interface IPriceRegistry {
  /// @notice Update the price for given tokens and gas prices for given chains.
  /// @param priceUpdates The price updates to apply.
  function updatePrices(
    Internal.PriceUpdates memory priceUpdates
  ) external;

  /// @notice Get the `tokenPrice` for a given token.
  /// @param token The token to get the price for.
  /// @return tokenPrice The tokenPrice for the given token.
  function getTokenPrice(
    address token
  ) external view returns (Internal.TimestampedPackedUint224 memory);

  /// @notice Get the `tokenPrice` for a given token, checks if the price is valid.
  /// @param token The token to get the price for.
  /// @return tokenPrice The tokenPrice for the given token if it exists and is valid.
  function getValidatedTokenPrice(
    address token
  ) external view returns (uint224);

  /// @notice Get the `tokenPrice` for an array of tokens.
  /// @param tokens The tokens to get prices for.
  /// @return tokenPrices The tokenPrices for the given tokens.
  function getTokenPrices(
    address[] calldata tokens
  ) external view returns (Internal.TimestampedPackedUint224[] memory);

  /// @notice Get an encoded `gasPrice` for a given destination chain ID.
  /// The 224-bit result encodes necessary gas price components.
  /// On L1 chains like Ethereum or Avax, the only component is the gas price.
  /// On Optimistic Rollups, there are two components - the L2 gas price, and L1 base fee for data availability.
  /// On future chains, there could be more or differing price components.
  /// PriceRegistry does not contain chain-specific logic to parse destination chain price components.
  /// @param destChainSelector The destination chain to get the price for.
  /// @return gasPrice The encoded gasPrice for the given destination chain ID.
  function getDestinationChainGasPrice(
    uint64 destChainSelector
  ) external view returns (Internal.TimestampedPackedUint224 memory);

  /// @notice Gets the fee token price and the gas price, both denominated in dollars.
  /// @param token The source token to get the price for.
  /// @param destChainSelector The destination chain to get the gas price for.
  /// @return tokenPrice The price of the feeToken in 1e18 dollars per base unit.
  /// @return gasPrice The price of gas in 1e18 dollars per base unit.
  function getTokenAndGasPrices(
    address token,
    uint64 destChainSelector
  ) external view returns (uint224 tokenPrice, uint224 gasPrice);

  /// @notice Convert a given token amount to target token amount.
  /// @param fromToken The given token address.
  /// @param fromTokenAmount The given token amount.
  /// @param toToken The target token address.
  /// @return toTokenAmount The target token amount.
  function convertTokenAmount(
    address fromToken,
    uint256 fromTokenAmount,
    address toToken
  ) external view returns (uint256 toTokenAmount);

  /// @notice Get the list of fee tokens.
  /// @return feeTokens The tokens set as fee tokens.
  function getFeeTokens() external view returns (address[] memory);
}

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

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

/// @notice This interface contains the only RMN-related functions that might be used on-chain by other CCIP contracts.
interface IRMNRemote {
  /// @notice signature components from RMN nodes.
  struct Signature {
    bytes32 r;
    bytes32 s;
  }

  /// @notice Verifies signatures of RMN nodes, on dest lane updates as provided in the CommitReport.
  /// @param offRampAddress is not inferred by msg.sender, in case the call is made through RMNProxy.
  /// @param merkleRoots must be well formed, and is a representation of the CommitReport received from the oracles.
  /// @param signatures rmnNodes ECDSA sigs, only r & s, must be sorted in ascending order by signer address.
  /// @dev Will revert if verification fails.
  function verify(
    address offRampAddress,
    Internal.MerkleRoot[] memory merkleRoots,
    Signature[] memory signatures
  ) external view;

  /// @notice gets the current set of cursed subjects.
  /// @return subjects the list of cursed subjects.
  function getCursedSubjects() external view returns (bytes16[] memory subjects);

  /// @notice If there is an active global or legacy curse, this function returns true.
  /// @return bool true if there is an active global curse.
  function isCursed() external view returns (bool);

  /// @notice If there is an active global curse, or an active curse for `subject`, this function returns true.
  /// @param subject To check whether a particular chain is cursed, set to bytes16(uint128(chainSelector)).
  /// @return bool true if the provided subject is cured *or* if there is an active global curse.
  function isCursed(
    bytes16 subject
  ) external view returns (bool);
}

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

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

interface IRouter {
  error OnlyOffRamp();

  /// @notice Route the message to its intended receiver contract.
  /// @param message Client.Any2EVMMessage struct.
  /// @param gasForCallExactCheck of params for exec.
  /// @param gasLimit set of params for exec.
  /// @param receiver set of params for exec.
  /// @dev if the receiver is a contracts that signals support for CCIP execution through EIP-165.
  /// the contract is called. If not, only tokens are transferred.
  /// @return success A boolean value indicating whether the ccip message was received without errors.
  /// @return retBytes A bytes array containing return data form CCIP receiver.
  /// @return gasUsed the gas used by the external customer call. Does not include any overhead.
  function routeMessage(
    Client.Any2EVMMessage calldata message,
    uint16 gasForCallExactCheck,
    uint256 gasLimit,
    address receiver
  ) external returns (bool success, bytes memory retBytes, uint256 gasUsed);

  /// @notice Returns the configured onramp for a specific destination chain.
  /// @param destChainSelector The destination chain Id to get the onRamp for.
  /// @return onRampAddress The address of the onRamp.
  function getOnRamp(
    uint64 destChainSelector
  ) external view returns (address onRampAddress);

  /// @notice Return true if the given offRamp is a configured offRamp for the given source chain.
  /// @param sourceChainSelector The source chain selector to check.
  /// @param offRamp The address of the offRamp to check.
  function isOffRamp(uint64 sourceChainSelector, address offRamp) external view returns (bool isOffRamp);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

interface ITokenAdminRegistry {
  /// @notice Returns the pool for the given token.
  function getPool(
    address token
  ) external view returns (address);

  /// @notice Proposes an administrator for the given token as pending administrator.
  /// @param localToken The token to register the administrator for.
  /// @param administrator The administrator to register.
  function proposeAdministrator(address localToken, address administrator) external;

  /// @notice Accepts the administrator role for a token.
  /// @param localToken The token to accept the administrator role for.
  /// @dev This function can only be called by the pending administrator.
  function acceptAdminRole(
    address localToken
  ) external;

  /// @notice Sets the pool for a token. Setting the pool to address(0) effectively delists the token
  /// from CCIP. Setting the pool to any other address enables the token on CCIP.
  /// @param localToken The token to set the pool for.
  /// @param pool The pool to set for the token.
  function setPool(address localToken, address pool) external;

  /// @notice Transfers the administrator role for a token to a new address with a 2-step process.
  /// @param localToken The token to transfer the administrator role for.
  /// @param newAdmin The address to transfer the administrator role to. Can be address(0) to cancel
  /// a pending transfer.
  /// @dev The new admin must call `acceptAdminRole` to accept the role.
  function transferAdminRole(address localToken, address newAdmin) external;
}

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

// End consumer library.
library Client {
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct EVMTokenAmount {
    address token; // token address on the local chain.
    uint256 amount; // Amount of tokens.
  }

  struct Any2EVMMessage {
    bytes32 messageId; // MessageId corresponding to ccipSend on source.
    uint64 sourceChainSelector; // Source chain selector.
    bytes sender; // abi.decode(sender) if coming from an EVM chain.
    bytes data; // payload sent in original message.
    EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
  }

  // If extraArgs is empty bytes, the default is 200k gas limit.
  struct EVM2AnyMessage {
    bytes receiver; // abi.encode(receiver address) for dest EVM chains.
    bytes data; // Data payload.
    EVMTokenAmount[] tokenAmounts; // Token transfers.
    address feeToken; // Address of feeToken. address(0) means you will send msg.value.
    bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2).
  }

  // Tag to indicate only a gas limit. Only usable for EVM as destination chain.
  bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;

  struct EVMExtraArgsV1 {
    uint256 gasLimit;
  }

  function _argsToBytes(
    EVMExtraArgsV1 memory extraArgs
  ) internal pure returns (bytes memory bts) {
    return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);
  }

  // Tag to indicate a gas limit (or dest chain equivalent processing units) and Out Of Order Execution. This tag is
  // available for multiple chain families. If there is no chain family specific tag, this is the default available
  // for a chain.
  // Note: not available for Solana VM based chains.
  bytes4 public constant GENERIC_EXTRA_ARGS_V2_TAG = 0x181dcf10;

  /// @param gasLimit: gas limit for the callback on the destination chain.
  /// @param allowOutOfOrderExecution: if true, it indicates that the message can be executed in any order relative to
  /// other messages from the same sender. This value's default varies by chain. On some chains, a particular value is
  /// enforced, meaning if the expected value is not set, the message request will revert.
  /// @dev Fully compatible with the previously existing EVMExtraArgsV2.
  struct GenericExtraArgsV2 {
    uint256 gasLimit;
    bool allowOutOfOrderExecution;
  }

  // Extra args tag for chains that use the Solana VM.
  bytes4 public constant SVM_EXTRA_ARGS_V1_TAG = 0x1f3b3aba;

  struct SVMExtraArgsV1 {
    uint32 computeUnits;
    uint64 accountIsWritableBitmap;
    bool allowOutOfOrderExecution;
    bytes32 tokenReceiver;
    bytes32[] accounts;
  }

  /// @dev The maximum number of accounts that can be passed in SVMExtraArgs.
  uint256 public constant SVM_EXTRA_ARGS_MAX_ACCOUNTS = 64;

  function _argsToBytes(
    GenericExtraArgsV2 memory extraArgs
  ) internal pure returns (bytes memory bts) {
    return abi.encodeWithSelector(GENERIC_EXTRA_ARGS_V2_TAG, extraArgs);
  }

  function _svmArgsToBytes(
    SVMExtraArgsV1 memory extraArgs
  ) internal pure returns (bytes memory bts) {
    return abi.encodeWithSelector(SVM_EXTRA_ARGS_V1_TAG, extraArgs);
  }
}

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

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

/// @notice Library for CCIP internal definitions common to multiple contracts.
/// @dev The following is a non-exhaustive list of "known issues" for CCIP:
/// - We could implement yield claiming for Blast. This is not worth the custom code path on non-blast chains.
/// - uint32 is used for timestamps, which will overflow in 2106. This is not a concern for the current use case, as we
/// expect to have migrated to a new version by then.
library Internal {
  error InvalidEVMAddress(bytes encodedAddress);
  error Invalid32ByteAddress(bytes encodedAddress);

  /// @dev We limit return data to a selector plus 4 words. This is to avoid malicious contracts from returning
  /// large amounts of data and causing repeated out-of-gas scenarios.
  uint16 internal constant MAX_RET_BYTES = 4 + 4 * 32;
  /// @dev The expected number of bytes returned by the balanceOf function.
  uint256 internal constant MAX_BALANCE_OF_RET_BYTES = 32;

  /// @dev The address used to send calls for gas estimation.
  /// You only need to use this address if the minimum gas limit specified by the user is not actually enough to execute the
  /// given message and you're attempting to estimate the actual necessary gas limit
  address public constant GAS_ESTIMATION_SENDER = address(0xC11C11C11C11C11C11C11C11C11C11C11C11C1);

  /// @notice A collection of token price and gas price updates.
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct PriceUpdates {
    TokenPriceUpdate[] tokenPriceUpdates;
    GasPriceUpdate[] gasPriceUpdates;
  }

  /// @notice Token price in USD.
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct TokenPriceUpdate {
    address sourceToken; // Source token.
    uint224 usdPerToken; // 1e18 USD per 1e18 of the smallest token denomination.
  }

  /// @notice Gas price for a given chain in USD, its value may contain tightly packed fields.
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct GasPriceUpdate {
    uint64 destChainSelector; // Destination chain selector.
    uint224 usdPerUnitGas; // 1e18 USD per smallest unit (e.g. wei) of destination chain gas.
  }

  /// @notice A timestamped uint224 value that can contain several tightly packed fields.
  struct TimestampedPackedUint224 {
    uint224 value; // ────╮ Value in uint224, packed.
    uint32 timestamp; // ─╯ Timestamp of the most recent price update.
  }

  /// @dev Gas price is stored in 112-bit unsigned int. uint224 can pack 2 prices.
  /// When packing L1 and L2 gas prices, L1 gas price is left-shifted to the higher-order bits.
  /// Using uint8 type, which cannot be higher than other bit shift operands, to avoid shift operand type warning.
  uint8 public constant GAS_PRICE_BITS = 112;

  struct SourceTokenData {
    // The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be
    // relied upon by the destination pool to validate the source pool.
    bytes sourcePoolAddress;
    // The address of the destination token, abi encoded in the case of EVM chains.
    // This value is UNTRUSTED as any pool owner can return whatever value they want.
    bytes destTokenAddress;
    // Optional pool data to be transferred to the destination chain. Be default this is capped at
    // CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
    // has to be set for the specific token.
    bytes extraData;
    uint32 destGasAmount; // The amount of gas available for the releaseOrMint and balanceOf calls on the offRamp
  }

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

  /// @dev Any2EVMRampMessage struct has 10 fields, including 3 variable unnested arrays, sender, data and tokenAmounts.
  /// Each variable array takes 1 more slot to store its length.
  /// When abi encoded, excluding array contents, Any2EVMMessage takes up a fixed number of 13 slots, 32 bytes each.
  /// Assume 1 slot for sender
  /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 14.
  /// The fixed bytes does not cover struct data (this is represented by MESSAGE_FIXED_BYTES_PER_TOKEN)
  uint256 public constant MESSAGE_FIXED_BYTES = 32 * 15;

  /// @dev Any2EVMTokensTransfer struct bytes length
  /// 0x20
  /// sourcePoolAddress_offset
  /// destTokenAddress
  /// destGasAmount
  /// extraData_offset
  /// amount
  /// sourcePoolAddress_length
  /// sourcePoolAddress_content // assume 1 slot
  /// extraData_length // contents billed separately
  uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * (4 + (3 + 2));

  bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1");
  bytes32 internal constant EVM_2_ANY_MESSAGE_HASH = keccak256("EVM2AnyMessageHashV1");

  /// @dev Used to hash messages for multi-lane family-agnostic OffRamps.
  /// OnRamp hash(EVM2AnyMessage) != Any2EVMRampMessage.messageId.
  /// OnRamp hash(EVM2AnyMessage) != OffRamp hash(Any2EVMRampMessage).
  /// @param original OffRamp message to hash.
  /// @param metadataHash Hash preimage to ensure global uniqueness.
  /// @return hashedMessage hashed message as a keccak256.
  function _hash(Any2EVMRampMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) {
    // Fixed-size message fields are included in nested hash to reduce stack pressure.
    // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
    return keccak256(
      abi.encode(
        MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
        metadataHash,
        keccak256(
          abi.encode(
            original.header.messageId,
            original.receiver,
            original.header.sequenceNumber,
            original.gasLimit,
            original.header.nonce
          )
        ),
        keccak256(original.sender),
        keccak256(original.data),
        keccak256(abi.encode(original.tokenAmounts))
      )
    );
  }

  function _hash(EVM2AnyRampMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) {
    // Fixed-size message fields are included in nested hash to reduce stack pressure.
    // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers.
    return keccak256(
      abi.encode(
        MerkleMultiProof.LEAF_DOMAIN_SEPARATOR,
        metadataHash,
        keccak256(
          abi.encode(
            original.sender,
            original.header.sequenceNumber,
            original.header.nonce,
            original.feeToken,
            original.feeTokenAmount
          )
        ),
        keccak256(original.receiver),
        keccak256(original.data),
        keccak256(abi.encode(original.tokenAmounts)),
        keccak256(original.extraArgs)
      )
    );
  }

  /// @dev We disallow the first 1024 addresses to avoid calling into a range known for hosting precompiles. Calling
  /// into precompiles probably won't cause any issues, but to be safe we can disallow this range. It is extremely
  /// unlikely that anyone would ever be able to generate an address in this range. There is no official range of
  /// precompiles, but EIP-7587 proposes to reserve the range 0x100 to 0x1ff. Our range is more conservative, even
  /// though it might not be exhaustive for all chains, which is OK. We also disallow the zero address, which is a
  /// common practice.
  uint256 public constant EVM_PRECOMPILE_SPACE = 1024;

  // According to the Aptos docs, the first 0xa addresses are reserved for precompiles.
  // https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/doc/account.md#function-create_framework_reserved_account-1
  uint256 public constant APTOS_PRECOMPILE_SPACE = 0x0b;

  /// @notice This methods provides validation for parsing abi encoded addresses by ensuring the address is within the
  /// EVM address space. If it isn't it will revert with an InvalidEVMAddress error, which we can catch and handle
  /// more gracefully than a revert from abi.decode.
  function _validateEVMAddress(
    bytes memory encodedAddress
  ) internal pure {
    if (encodedAddress.length != 32) revert InvalidEVMAddress(encodedAddress);
    uint256 encodedAddressUint = abi.decode(encodedAddress, (uint256));
    if (encodedAddressUint > type(uint160).max || encodedAddressUint < EVM_PRECOMPILE_SPACE) {
      revert InvalidEVMAddress(encodedAddress);
    }
  }

  /// @notice This methods provides validation for parsing abi encoded addresses by ensuring the address is within the
  /// bounds of [minValue, uint256.max]. If it isn't it will revert with an Invalid32ByteAddress error.
  function _validate32ByteAddress(bytes memory encodedAddress, uint256 minValue) internal pure {
    if (encodedAddress.length != 32) revert Invalid32ByteAddress(encodedAddress);
    if (minValue > 0) {
      if (abi.decode(encodedAddress, (uint256)) < minValue) {
        revert Invalid32ByteAddress(encodedAddress);
      }
    }
  }

  /// @notice Enum listing the possible message execution states within the offRamp contract.
  /// UNTOUCHED never executed.
  /// IN_PROGRESS currently being executed, used a replay protection.
  /// SUCCESS successfully executed. End state.
  /// FAILURE unsuccessfully executed, manual execution is now enabled.
  /// @dev RMN depends on this enum, if changing, please notify the RMN maintainers.
  enum MessageExecutionState {
    UNTOUCHED,
    IN_PROGRESS,
    SUCCESS,
    FAILURE
  }

  /// @notice CCIP OCR plugin type, used to separate execution & commit transmissions and configs.
  enum OCRPluginType {
    Commit,
    Execution
  }

  /// @notice Family-agnostic header for OnRamp & OffRamp messages.
  /// The messageId is not expected to match hash(message), since it may originate from another ramp family.
  struct RampMessageHeader {
    bytes32 messageId; // Unique identifier for the message, generated with the source chain's encoding scheme (i.e. not necessarily abi.encoded).
    uint64 sourceChainSelector; // ─╮ the chain selector of the source chain, note: not chainId.
    uint64 destChainSelector; //    │ the chain selector of the destination chain, note: not chainId.
    uint64 sequenceNumber; //       │ sequence number, not unique across lanes.
    uint64 nonce; // ───────────────╯ nonce for this lane for this sender, not unique across senders/lanes.
  }

  struct EVM2AnyTokenTransfer {
    // The source pool EVM address. This value is trusted as it was obtained through the onRamp. It can be relied
    // upon by the destination pool to validate the source pool.
    address sourcePoolAddress;
    // The EVM address of the destination token.
    // This value is UNTRUSTED as any pool owner can return whatever value they want.
    bytes destTokenAddress;
    // Optional pool data to be transferred to the destination chain. Be default this is capped at
    // CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
    // has to be set for the specific token.
    bytes extraData;
    uint256 amount; // Amount of tokens.
    // Destination chain data used to execute the token transfer on the destination chain. For an EVM destination, it
    // consists of the amount of gas available for the releaseOrMint and transfer calls made by the offRamp.
    bytes destExecData;
  }

  struct Any2EVMTokenTransfer {
    // The source pool EVM address encoded to bytes. This value is trusted as it is obtained through the onRamp. It can
    // be relied upon by the destination pool to validate the source pool.
    bytes sourcePoolAddress;
    address destTokenAddress; // ─╮ Address of destination token
    uint32 destGasAmount; // ─────╯ The amount of gas available for the releaseOrMint and transfer calls on the offRamp.
    // Optional pool data to be transferred to the destination chain. Be default this is capped at
    // CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
    // has to be set for the specific token.
    bytes extraData;
    uint256 amount; // Amount of tokens.
  }

  /// @notice Family-agnostic message routed to an OffRamp.
  /// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage), hash(Any2EVMRampMessage) != messageId due to encoding
  /// and parameter differences.
  struct Any2EVMRampMessage {
    RampMessageHeader header; // Message header.
    bytes sender; // sender address on the source chain.
    bytes data; // arbitrary data payload supplied by the message sender.
    address receiver; // receiver address on the destination chain.
    uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution.
    Any2EVMTokenTransfer[] tokenAmounts; // array of tokens and amounts to transfer.
  }

  /// @notice Family-agnostic message emitted from the OnRamp.
  /// Note: hash(Any2EVMRampMessage) != hash(EVM2AnyRampMessage) due to encoding & parameter differences.
  /// messageId = hash(EVM2AnyRampMessage) using the source EVM chain's encoding format.
  struct EVM2AnyRampMessage {
    RampMessageHeader header; // Message header.
    address sender; // sender address on the source chain.
    bytes data; // arbitrary data payload supplied by the message sender.
    bytes receiver; // receiver address on the destination chain.
    bytes extraArgs; // destination-chain specific extra args, such as the gasLimit for EVM chains.
    address feeToken; // fee token.
    uint256 feeTokenAmount; // fee token amount.
    uint256 feeValueJuels; // fee amount in Juels.
    EVM2AnyTokenTransfer[] tokenAmounts; // array of tokens and amounts to transfer.
  }

  // bytes4(keccak256("CCIP ChainFamilySelector EVM"));
  bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c;

  // bytes4(keccak256("CCIP ChainFamilySelector SVM"));
  bytes4 public constant CHAIN_FAMILY_SELECTOR_SVM = 0x1e10bdc4;

  // bytes4(keccak256("CCIP ChainFamilySelector APTOS"));
  bytes4 public constant CHAIN_FAMILY_SELECTOR_APTOS = 0xac77ffec;

  /// @dev Holds a merkle root and interval for a source chain so that an array of these can be passed in the CommitReport.
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  /// @dev inefficient struct packing intentionally chosen to maintain order of specificity. Not a storage struct so impact is minimal.
  // solhint-disable-next-line gas-struct-packing
  struct MerkleRoot {
    uint64 sourceChainSelector; // Remote source chain selector that the Merkle Root is scoped to
    bytes onRampAddress; //        Generic onRamp address, to support arbitrary sources; for EVM, use abi.encode
    uint64 minSeqNr; // ─────────╮ Minimum sequence number, inclusive
    uint64 maxSeqNr; // ─────────╯ Maximum sequence number, inclusive
    bytes32 merkleRoot; //         Merkle root covering the interval & source chain messages
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;

library MerkleMultiProof {
  /// @notice Leaf domain separator, should be used as the first 32 bytes of a leaf's preimage.
  bytes32 internal constant LEAF_DOMAIN_SEPARATOR = 0x0000000000000000000000000000000000000000000000000000000000000000;
  /// @notice Internal domain separator, should be used as the first 32 bytes of an internal node's preimage.
  bytes32 internal constant INTERNAL_DOMAIN_SEPARATOR =
    0x0000000000000000000000000000000000000000000000000000000000000001;

  uint256 internal constant MAX_NUM_HASHES = 256;

  error InvalidProof();
  error LeavesCannotBeEmpty();

  /// @notice Computes the root based on provided pre-hashed leaf nodes in leaves, internal nodes  in proofs, and using
  /// proofFlagBits' i-th bit to determine if an element of proofs or one of the previously computed leafs or internal
  /// nodes will be used for the i-th hash.
  /// @param leaves Should be pre-hashed and the first 32 bytes of a leaf's preimage should match LEAF_DOMAIN_SEPARATOR.
  /// @param proofs Hashes to be used instead of a leaf hash when the proofFlagBits indicates a proof should be used.
  /// @param proofFlagBits A single uint256 of which each bit indicates whether a leaf or a proof needs to be used in
  /// a hash operation.
  /// @dev the maximum number of hash operations it set to 256. Any input that would require more than 256 hashes to get
  /// to a root will revert.
  /// @dev For given input `leaves` = [a,b,c] `proofs` = [D] and `proofFlagBits` = 5
  ///     totalHashes = 3 + 1 - 1 = 3
  ///  ** round 1 **
  ///    proofFlagBits = (5 >> 0) & 1 = true
  ///    hashes[0] = hashPair(a, b)
  ///    (leafPos, hashPos, proofPos) = (2, 0, 0);
  ///
  ///  ** round 2 **
  ///    proofFlagBits = (5 >> 1) & 1 = false
  ///    hashes[1] = hashPair(D, c)
  ///    (leafPos, hashPos, proofPos) = (3, 0, 1);
  ///
  ///  ** round 3 **
  ///    proofFlagBits = (5 >> 2) & 1 = true
  ///    hashes[2] = hashPair(hashes[0], hashes[1])
  ///    (leafPos, hashPos, proofPos) = (3, 2, 1);
  ///
  ///    i = 3 and no longer < totalHashes. The algorithm is done
  ///    return hashes[totalHashes - 1] = hashes[2]; the last hash we computed.
  // We mark this function as internal to force it to be inlined in contracts that use it, but semantically it is public.
  function _merkleRoot(
    bytes32[] memory leaves,
    bytes32[] memory proofs,
    uint256 proofFlagBits
  ) internal pure returns (bytes32) {
    unchecked {
      uint256 leavesLen = leaves.length;
      uint256 proofsLen = proofs.length;
      if (leavesLen == 0) revert LeavesCannotBeEmpty();
      if (!(leavesLen <= MAX_NUM_HASHES + 1 && proofsLen <= MAX_NUM_HASHES + 1)) revert InvalidProof();
      uint256 totalHashes = leavesLen + proofsLen - 1;
      if (!(totalHashes <= MAX_NUM_HASHES)) revert InvalidProof();
      if (totalHashes == 0) {
        return leaves[0];
      }
      bytes32[] memory hashes = new bytes32[](totalHashes);
      (uint256 leafPos, uint256 hashPos, uint256 proofPos) = (0, 0, 0);

      for (uint256 i = 0; i < totalHashes; ++i) {
        // Checks if the bit flag signals the use of a supplied proof or a leaf/previous hash.
        bytes32 a;
        if (proofFlagBits & (1 << i) == (1 << i)) {
          // Use a leaf or a previously computed hash.
          if (leafPos < leavesLen) {
            a = leaves[leafPos++];
          } else {
            a = hashes[hashPos++];
          }
        } else {
          // Use a supplied proof.
          a = proofs[proofPos++];
        }

        // The second part of the hashed pair is never a proof as hashing two proofs would result in a
        // hash that can already be computed offchain.
        bytes32 b;
        if (leafPos < leavesLen) {
          b = leaves[leafPos++];
        } else {
          b = hashes[hashPos++];
        }

        if (!(hashPos <= i)) revert InvalidProof();

        hashes[i] = _hashPair(a, b);
      }
      if (!(hashPos == totalHashes - 1 && leafPos == leavesLen && proofPos == proofsLen)) revert InvalidProof();
      // Return the last hash.
      return hashes[totalHashes - 1];
    }
  }

  /// @notice Hashes two bytes32 objects in their given order, prepended by the INTERNAL_DOMAIN_SEPARATOR.
  function _hashInternalNode(bytes32 left, bytes32 right) private pure returns (bytes32 hash) {
    return keccak256(abi.encode(INTERNAL_DOMAIN_SEPARATOR, left, right));
  }

  /// @notice Hashes two bytes32 objects. The order is taken into account, using the lower value first.
  function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
    return a < b ? _hashInternalNode(a, b) : _hashInternalNode(b, a);
  }
}

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

/// @notice This library contains various token pool functions to aid constructing the return data.
library Pool {
  // The tag used to signal support for the pool v1 standard.
  // bytes4(keccak256("CCIP_POOL_V1"))
  bytes4 public constant CCIP_POOL_V1 = 0xaff2afbf;

  // The number of bytes in the return data for a pool v1 releaseOrMint call.
  // This should match the size of the ReleaseOrMintOutV1 struct.
  uint16 public constant CCIP_POOL_V1_RET_BYTES = 32;

  // The default max number of bytes in the return data for a pool v1 lockOrBurn call.
  // This data can be used to send information to the destination chain token pool. Can be overwritten
  // in the TokenTransferFeeConfig.destBytesOverhead if more data is required.
  uint32 public constant CCIP_LOCK_OR_BURN_V1_RET_BYTES = 32;

  struct LockOrBurnInV1 {
    bytes receiver; //  The recipient of the tokens on the destination chain, abi encoded.
    uint64 remoteChainSelector; // ─╮ The chain ID of the destination chain.
    address originalSender; // ─────╯ The original sender of the tx on the source chain.
    uint256 amount; //  The amount of tokens to lock or burn, denominated in the source token's decimals.
    address localToken; //  The address on this chain of the token to lock or burn.
  }

  struct LockOrBurnOutV1 {
    // The address of the destination token, abi encoded in the case of EVM chains.
    // This value is UNTRUSTED as any pool owner can return whatever value they want.
    bytes destTokenAddress;
    // Optional pool data to be transferred to the destination chain. Be default this is capped at
    // CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
    // has to be set for the specific token.
    bytes destPoolData;
  }

  struct ReleaseOrMintInV1 {
    bytes originalSender; //          The original sender of the tx on the source chain.
    uint64 remoteChainSelector; // ─╮ The chain ID of the source chain.
    address receiver; // ───────────╯ The recipient of the tokens on the destination chain.
    uint256 amount; //                The amount of tokens to release or mint, denominated in the source token's decimals.
    address localToken; //            The address on this chain of the token to release or mint.
    /// @dev WARNING: sourcePoolAddress should be checked prior to any processing of funds. Make sure it matches the
    /// expected pool address for the given remoteChainSelector.
    bytes sourcePoolAddress; //       The address of the source pool, abi encoded in the case of EVM chains.
    bytes sourcePoolData; //          The data received from the source pool to process the release or mint.
    /// @dev WARNING: offchainTokenData is untrusted data.
    bytes offchainTokenData; //       The offchain data to process the release or mint.
  }

  struct ReleaseOrMintOutV1 {
    // The number of tokens released or minted on the destination chain, denominated in the local token's decimals.
    // This value is expected to be equal to the ReleaseOrMintInV1.amount in the case where the source and destination
    // chain have the same number of decimals.
    uint256 destinationAmount;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library USDPriceWith18Decimals {
  /// @notice Takes a price in USD, with 18 decimals per 1e18 token amount, and amount of the smallest token
  /// denomination, calculates the value in USD with 18 decimals.
  /// @param tokenPrice The USD price of the token.
  /// @param tokenAmount Amount of the smallest token denomination.
  /// @return USD value with 18 decimals.
  /// @dev this function assumes that no more than 1e59 US dollar worth of token is passed in. If more is sent, this
  /// function will overflow and revert. Since there isn't even close to 1e59 dollars, this is ok for all legit tokens.
  function _calcUSDValueFromTokenAmount(uint224 tokenPrice, uint256 tokenAmount) internal pure returns (uint256) {
    /// LINK Example:
    /// tokenPrice:         8e18 -> $8/LINK, as 1e18 token amount is 1 LINK, worth 8 USD, or 8e18 with 18 decimals
    /// tokenAmount:        2e18 -> 2 LINK
    /// result:             8e18 * 2e18 / 1e18 -> 16e18 with 18 decimals = $16

    /// USDC Example:
    /// tokenPrice:         1e30 -> $1/USDC, as 1e18 token amount is 1e12 USDC, worth 1e12 USD, or 1e30 with 18 decimals
    /// tokenAmount:        5e6  -> 5 USDC
    /// result:             1e30 * 5e6 / 1e18 -> 5e18 with 18 decimals = $5
    return (tokenPrice * tokenAmount) / 1e18;
  }

  /// @notice Takes a price in USD, with 18 decimals per 1e18 token amount, and USD value with 18 decimals, calculates
  /// amount of the smallest token denomination.
  /// @param tokenPrice The USD price of the token.
  /// @param usdValue USD value with 18 decimals.
  /// @return Amount of the smallest token denomination.
  function _calcTokenAmountFromUSDValue(uint224 tokenPrice, uint256 usdValue) internal pure returns (uint256) {
    /// LINK Example:
    /// tokenPrice:          8e18 -> $8/LINK, as 1e18 token amount is 1 LINK, worth 8 USD, or 8e18 with 18 decimals
    /// usdValue:           16e18 -> $16
    /// result:             16e18 * 1e18 / 8e18 -> 2e18 = 2 LINK

    /// USDC Example:
    /// tokenPrice:         1e30 -> $1/USDC, as 1e18 token amount is 1e12 USDC, worth 1e12 USD, or 1e30 with 18 decimals
    /// usdValue:           5e18 -> $5
    /// result:             5e18 * 1e18 / 1e30 -> 5e6 = 5 USDC
    return (usdValue * 1e18) / tokenPrice;
  }
}

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

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

/// @notice A minimal contract that implements 2-step ownership transfer and nothing more. It's made to be minimal
/// to reduce the impact of the bytecode size on any contract that inherits from it.
contract Ownable2Step is IOwnable {
  /// @notice The pending owner is the address to which ownership may be transferred.
  address private s_pendingOwner;
  /// @notice The owner is the current owner of the contract.
  /// @dev The owner is the second storage variable so any implementing contract could pack other state with it
  /// instead of the much less used s_pendingOwner.
  address private s_owner;

  error OwnerCannotBeZero();
  error MustBeProposedOwner();
  error CannotTransferToSelf();
  error OnlyCallableByOwner();

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

  constructor(address newOwner, address pendingOwner) {
    if (newOwner == address(0)) {
      revert OwnerCannotBeZero();
    }

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

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

  /// @notice Allows an owner to begin transferring ownership to a new address. The new owner needs to call
  /// `acceptOwnership` to accept the transfer before any permissions are changed.
  /// @param to The address to which ownership will be transferred.
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /// @notice validate, transfer ownership, and emit relevant events
  /// @param to The address to which ownership will be transferred.
  function _transferOwnership(address to) private {
    if (to == msg.sender) {
      revert CannotTransferToSelf();
    }

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /// @notice Allows an ownership transfer to be completed by the recipient.
  function acceptOwnership() external override {
    if (msg.sender != s_pendingOwner) {
      revert MustBeProposedOwner();
    }

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

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /// @notice validate access
  function _validateOwnership() internal view {
    if (msg.sender != s_owner) {
      revert OnlyCallableByOwner();
    }
  }

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

File 17 of 25 : Ownable2StepMsgSender.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

/// @notice Sets the msg.sender to be the owner of the contract and does not set a pending owner.
contract Ownable2StepMsgSender is Ownable2Step {
  constructor() Ownable2Step(msg.sender, address(0)) {}
}

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

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

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

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

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

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

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

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

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

Settings
{
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "appendCBOR": true,
    "bytecodeHash": "none",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": true,
    "runs": 80000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "forge-std/=node_modules/@chainlink/contracts/src/v0.8/vendor/forge-std/src/",
    "@chainlink/=node_modules/@chainlink/contracts/src/v0.8/"
  ],
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"components":[{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"contract IRMNRemote","name":"rmnRemote","type":"address"},{"internalType":"address","name":"nonceManager","type":"address"},{"internalType":"address","name":"tokenAdminRegistry","type":"address"}],"internalType":"struct OnRamp.StaticConfig","name":"staticConfig","type":"tuple"},{"components":[{"internalType":"address","name":"feeQuoter","type":"address"},{"internalType":"bool","name":"reentrancyGuardEntered","type":"bool"},{"internalType":"address","name":"messageInterceptor","type":"address"},{"internalType":"address","name":"feeAggregator","type":"address"},{"internalType":"address","name":"allowlistAdmin","type":"address"}],"internalType":"struct OnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"},{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"contract IRouter","name":"router","type":"address"},{"internalType":"bool","name":"allowlistEnabled","type":"bool"}],"internalType":"struct OnRamp.DestChainConfigArgs[]","name":"destChainConfigArgs","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CannotSendZeroTokens","type":"error"},{"inputs":[],"name":"CannotTransferToSelf","type":"error"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"CursedByRMN","type":"error"},{"inputs":[],"name":"GetSupportedTokensFunctionalityRemovedCheckAdminRegistry","type":"error"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"InvalidAllowListRequest","type":"error"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"InvalidDestChainConfig","type":"error"},{"inputs":[],"name":"MustBeCalledByRouter","type":"error"},{"inputs":[],"name":"MustBeProposedOwner","type":"error"},{"inputs":[],"name":"OnlyCallableByOwner","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAllowlistAdmin","type":"error"},{"inputs":[],"name":"OwnerCannotBeZero","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RouterMustSetOriginalSender","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"UnsupportedToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"allowlistAdmin","type":"address"}],"name":"AllowListAdminSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"senders","type":"address[]"}],"name":"AllowListSendersAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"senders","type":"address[]"}],"name":"AllowListSendersRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"components":[{"components":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct Internal.RampMessageHeader","name":"header","type":"tuple"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"receiver","type":"bytes"},{"internalType":"bytes","name":"extraArgs","type":"bytes"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"internalType":"uint256","name":"feeValueJuels","type":"uint256"},{"components":[{"internalType":"address","name":"sourcePoolAddress","type":"address"},{"internalType":"bytes","name":"destTokenAddress","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"destExecData","type":"bytes"}],"internalType":"struct Internal.EVM2AnyTokenTransfer[]","name":"tokenAmounts","type":"tuple[]"}],"indexed":false,"internalType":"struct Internal.EVM2AnyRampMessage","name":"message","type":"tuple"}],"name":"CCIPMessageSent","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"contract IRMNRemote","name":"rmnRemote","type":"address"},{"internalType":"address","name":"nonceManager","type":"address"},{"internalType":"address","name":"tokenAdminRegistry","type":"address"}],"indexed":false,"internalType":"struct OnRamp.StaticConfig","name":"staticConfig","type":"tuple"},{"components":[{"internalType":"address","name":"feeQuoter","type":"address"},{"internalType":"bool","name":"reentrancyGuardEntered","type":"bool"},{"internalType":"address","name":"messageInterceptor","type":"address"},{"internalType":"address","name":"feeAggregator","type":"address"},{"internalType":"address","name":"allowlistAdmin","type":"address"}],"indexed":false,"internalType":"struct OnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"indexed":false,"internalType":"contract IRouter","name":"router","type":"address"},{"indexed":false,"internalType":"bool","name":"allowlistEnabled","type":"bool"}],"name":"DestChainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeAggregator","type":"address"},{"indexed":true,"internalType":"address","name":"feeToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeeTokenWithdrawn","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"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"bool","name":"allowlistEnabled","type":"bool"},{"internalType":"address[]","name":"addedAllowlistedSenders","type":"address[]"},{"internalType":"address[]","name":"removedAllowlistedSenders","type":"address[]"}],"internalType":"struct OnRamp.AllowlistConfigArgs[]","name":"allowlistConfigArgsItems","type":"tuple[]"}],"name":"applyAllowlistUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"},{"internalType":"contract IRouter","name":"router","type":"address"},{"internalType":"bool","name":"allowlistEnabled","type":"bool"}],"internalType":"struct OnRamp.DestChainConfigArgs[]","name":"destChainConfigArgs","type":"tuple[]"}],"name":"applyDestChainConfigUpdates","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"getAllowedSendersList","outputs":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"address[]","name":"configuredAddresses","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"name":"getDestChainConfig","outputs":[{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"bool","name":"allowlistEnabled","type":"bool"},{"internalType":"address","name":"router","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDynamicConfig","outputs":[{"components":[{"internalType":"address","name":"feeQuoter","type":"address"},{"internalType":"bool","name":"reentrancyGuardEntered","type":"bool"},{"internalType":"address","name":"messageInterceptor","type":"address"},{"internalType":"address","name":"feeAggregator","type":"address"},{"internalType":"address","name":"allowlistAdmin","type":"address"}],"internalType":"struct OnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"destChainSelector","type":"uint64"}],"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":"uint64","name":"","type":"uint64"},{"internalType":"contract IERC20","name":"sourceToken","type":"address"}],"name":"getPoolBySourceToken","outputs":[{"internalType":"contract IPoolV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticConfig","outputs":[{"components":[{"internalType":"uint64","name":"chainSelector","type":"uint64"},{"internalType":"contract IRMNRemote","name":"rmnRemote","type":"address"},{"internalType":"address","name":"nonceManager","type":"address"},{"internalType":"address","name":"tokenAdminRegistry","type":"address"}],"internalType":"struct OnRamp.StaticConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"getSupportedTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"feeQuoter","type":"address"},{"internalType":"bool","name":"reentrancyGuardEntered","type":"bool"},{"internalType":"address","name":"messageInterceptor","type":"address"},{"internalType":"address","name":"feeAggregator","type":"address"},{"internalType":"address","name":"allowlistAdmin","type":"address"}],"internalType":"struct OnRamp.DynamicConfig","name":"dynamicConfig","type":"tuple"}],"name":"setDynamicConfig","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":"feeTokens","type":"address[]"}],"name":"withdrawFeeTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x6101006040523461055f57613ce88038038061001a81610599565b92833981019080820390610140821261055f576080821261055f5761003d61057a565b90610047816105be565b82526020810151906001600160a01b038216820361055f5760208301918252610072604082016105d2565b926040810193845260a0610088606084016105d2565b6060830190815295607f19011261055f5760405160a081016001600160401b03811182821017610564576040526100c1608084016105d2565b81526100cf60a084016105e6565b602082019081526100e260c085016105d2565b91604081019283526100f660e086016105d2565b936060820194855261010b61010087016105d2565b6080830190815261012087015190966001600160401b03821161055f57018a601f8201121561055f578051906001600160401b0382116105645760209b8c6060610159828660051b01610599565b9e8f8681520194028301019181831161055f57602001925b8284106104f1575050505033156104e057600180546001600160a01b0319163317905580516001600160401b03161580156104ce575b80156104bc575b80156104aa575b61047d57516001600160401b0316608081905295516001600160a01b0390811660a08190529751811660c08190529851811660e08190528251909116158015610498575b801561048e575b61047d57815160028054855160ff60a01b90151560a01b166001600160a01b039384166001600160a81b0319909216919091171790558451600380549183166001600160a01b03199283161790558651600480549184169183169190911790558751600580549190931691161790557fc7372d2d886367d7bb1b0e0708a5436f2c91d6963de210eb2dc1ec2ecd6d21f19861012098606061029f61057a565b8a8152602080820193845260408083019586529290910194855281519a8b5291516001600160a01b03908116928b019290925291518116918901919091529051811660608801529051811660808701529051151560a08601529051811660c08501529051811660e0840152905116610100820152a16000905b805182101561040b5761032b82826105f3565b51916001600160401b0361033f82846105f3565b5151169283156103f65760008481526006602090815260409182902081840151815494840151600160401b600160e81b03198616604883901b600160481b600160e81b031617901515851b68ff000000000000000016179182905583516001600160401b0390951685526001600160a01b031691840191909152811c60ff1615159082015291926001927fd5ad72bc37dc7a80a8b9b9df20500046fd7341adb1be2258a540466fdd7dcef590606090a20190610318565b8363c35aa79d60e01b60005260045260246000fd5b6040516136ca908161061e82396080518181816103e301528181610bd901528181612163015261290a015260a05181818161219c015281816126bd0152612943015260c051818181610f9c015281816121d8015261297f015260e051818181612214015281816129bb0152612f990152f35b6306b7c75960e31b60005260046000fd5b5082511515610200565b5084516001600160a01b0316156101f9565b5088516001600160a01b0316156101b5565b5087516001600160a01b0316156101ae565b5086516001600160a01b0316156101a7565b639b15e16f60e01b60005260046000fd5b60608483031261055f5760405190606082016001600160401b0381118382101761056457604052610521856105be565b82526020850151906001600160a01b038216820361055f578260209283606095015261054f604088016105e6565b6040820152815201930192610171565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60405190608082016001600160401b0381118382101761056457604052565b6040519190601f01601f191682016001600160401b0381118382101761056457604052565b51906001600160401b038216820361055f57565b51906001600160a01b038216820361055f57565b5190811515820361055f57565b80518210156106075760209160051b010190565b634e487b7160e01b600052603260045260246000fdfe608080604052600436101561001357600080fd5b600090813560e01c90816306285c69146128a457508063181f5a771461282557806320487ded146125e15780632716072b1461233157806327e936f114611f2757806348a98aa414611ea45780635cb80c5d14611bbc5780636def4ce714611b2d5780637437ff9f14611a1057806379ba50971461192b5780638da5cb5b146118d95780639041be3d1461182c578063972b46121461175e578063c9b146b314611391578063df0aa9e91461022c578063f2fde38b1461013f5763fbca3b74146100dc57600080fd5b3461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57600490610116612bb0565b507f9e7177c8000000000000000000000000000000000000000000000000000000008152fd5b80fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5773ffffffffffffffffffffffffffffffffffffffff61018c612c06565b610194613367565b1633811461020457807fffffffffffffffffffffffff000000000000000000000000000000000000000083541617825573ffffffffffffffffffffffffffffffffffffffff600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788380a380f35b6004827fdad89dca000000000000000000000000000000000000000000000000000000008152fd5b503461013c5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57610264612bb0565b67ffffffffffffffff6024351161138d5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6024353603011261138d576102ab612c29565b60025460ff8160a01c16611365577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001760025567ffffffffffffffff8216835260066020526040832073ffffffffffffffffffffffffffffffffffffffff82161561133d57805460ff8160401c166112cf575b60481c73ffffffffffffffffffffffffffffffffffffffff1633036112a75773ffffffffffffffffffffffffffffffffffffffff600354168061123e575b50805467ffffffffffffffff811667ffffffffffffffff8114611211579067ffffffffffffffff60017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009493011692839116179055604051906103d582612a7a565b84825267ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602083015267ffffffffffffffff8416604083015260608201528360808201526104366024803501602435600401613147565b61044560046024350180613147565b610453606460243501613053565b93610468604460243501602435600401613198565b9490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06104ae61049887612be1565b966104a66040519889612acf565b808852612be1565b018a5b8181106111fa575050604051966104c788612a96565b875273ffffffffffffffffffffffffffffffffffffffff8816602088015236906104f092613218565b6040860152369061050092613218565b60608401526020916040516105158482612acf565b878152608085015273ffffffffffffffffffffffffffffffffffffffff1660a084015260443560c08401528560e084015261010083015260025473ffffffffffffffffffffffffffffffffffffffff16938560243560640161057690613053565b9561058a6024356084810190600401613147565b90919061059c60046024350180613147565b99906040519a8b95869485947f3a49bb4900000000000000000000000000000000000000000000000000000000865267ffffffffffffffff8b16600487015273ffffffffffffffffffffffffffffffffffffffff16602486015260443560448601526064850160a0905260a485019061061492612d45565b908382037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01608485015261064892612d45565b03915afa80156110f85786879688908993611179575b50608086015260e085015261067d604460243501602435600401613198565b909661068882612be1565b916106966040519384612acf565b8083528583018099368360061b8201116111755780915b8360061b8201831061113e5750505050885b6106d3604460243501602435600401613198565b9050811015610a3b576106e68184613104565b51906106f06131ec565b508682015115610a135773ffffffffffffffffffffffffffffffffffffffff61071b81845116612f3a565b169182158015610970575b61092e57808c878a8a73ffffffffffffffffffffffffffffffffffffffff8f836107d39801518280895116926040519761075f89612a7a565b885267ffffffffffffffff87890196168652816040890191168152606088019283526080880193845267ffffffffffffffff6040519b8c998a997f9a4575b9000000000000000000000000000000000000000000000000000000008b5260048b01525160a060248b015260c48a0190612b6d565b965116604488015251166064860152516084850152511660a4830152038183885af1918215610923578d809361086b575b50506001938284928b806108649651930151910151916040519361082785612a7a565b84528c840152604083015260608201528d604051906108468c83612acf565b815260808201526101008b01519061085e8383613104565b52613104565b50016106bf565b9250933d8093863e61087d8386612acf565b8985848101031261091f5784519167ffffffffffffffff831161091b576040838701858801031261091b576040516108b481612ab3565b8387015167ffffffffffffffff8111610916576108d89086890190868a010161324f565b81528b84880101519467ffffffffffffffff861161091657876108649688966109079360019b0192010161324f565b8c82015293509150938d610804565b508f80fd5b8e80fd5b8d80fd5b6040513d8f823e3d90fd5b517fbf16aab6000000000000000000000000000000000000000000000000000000008c5273ffffffffffffffffffffffffffffffffffffffff1660045260248bfd5b506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf0000000000000000000000000000000000000000000000000000000060048201528881602481875afa908115610923578d916109da575b5015610726565b90508881813d8311610a0c575b6109f18183612acf565b81010312610a0857610a0290612ce8565b386109d3565b8c80fd5b503d6109e7565b60048b7f5cf04449000000000000000000000000000000000000000000000000000000008152fd5b5090889692508690610ab39873ffffffffffffffffffffffffffffffffffffffff600254169061010089015192886040519c8d957f01447eaa00000000000000000000000000000000000000000000000000000000875267ffffffffffffffff8b166004880152606060248801526064870190613291565b917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8684030160448701525191828152019190855b8a828210611103575050505082809103915afa9687156110f857869761101f575b5015610f2d5750835b67ffffffffffffffff608085510191169052835b61010084015151811015610b5c5780610b4160019288613104565b516080610b5383610100890151613104565b51015201610b26565b5090926060610100604051610b7081612a96565b610b78613074565b8152838782015282604082015282808201528260808201528360a08201528360c08201528360e08201520152604051848101907f130ac867e79e2789f923760a88743d292acdf7002139a588206e2260f73f7321825267ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604082015267ffffffffffffffff8416606082015230608082015260808152610c2360a082612acf565b51902073ffffffffffffffffffffffffffffffffffffffff602085015116845167ffffffffffffffff6080816060840151169201511673ffffffffffffffffffffffffffffffffffffffff60a08801511660c088015191604051938a850195865260408501526060840152608083015260a082015260a08152610ca760c082612acf565b51902060608501518681519101206040860151878151910120610100870151604051610d0d81610ce18c8201948d86526040830190613291565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612acf565b51902091608088015189815191012093604051958a870197885260408701526060860152608085015260a084015260c083015260e082015260e08152610d5561010082612acf565b51902082515267ffffffffffffffff60608351015116907f192442a2b2adb6a7948f097023cb6b57d29d3a7a5dd33e6666d33c39cc456f3260405185815267ffffffffffffffff608086518051898501528289820151166040850152826040820151166060850152826060820151168285015201511660a082015273ffffffffffffffffffffffffffffffffffffffff60208601511660c082015280610ef8610e7f610e4a610e1560408a01516101a060e08701526101c0860190612b6d565b60608a01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086830301610100870152612b6d565b60808901517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085830301610120860152612b6d565b73ffffffffffffffffffffffffffffffffffffffff60a08901511661014084015260c088015161016084015260e088015161018084015267ffffffffffffffff610100890151967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0858403016101a08601521695613291565b0390a37fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff600254166002555151604051908152f35b73ffffffffffffffffffffffffffffffffffffffff604051917fea458c0c00000000000000000000000000000000000000000000000000000000835267ffffffffffffffff8416600484015216602482015282816044818873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1908115611014578591610fd2575b50610b12565b90508281813d831161100d575b610fe98183612acf565b81010312611009575167ffffffffffffffff811681036110095786610fcc565b8480fd5b503d610fdf565b6040513d87823e3d90fd5b9096503d908187823e6110328282612acf565b848183810103126110f45780519167ffffffffffffffff83116110f057808201601f8484010112156110f057828201519161106c83612be1565b9361107a6040519586612acf565b83855287850192808301898660051b8486010101116110ec578882840101935b898660051b848601010185106110b7575050505050509587610b09565b845167ffffffffffffffff8111610a08578a80926110df829383878a0191898b01010161324f565b815201950194905061109a565b8a80fd5b8780fd5b8680fd5b6040513d88823e3d90fd5b8351805173ffffffffffffffffffffffffffffffffffffffff168652810151818601528d97508e965060409094019390920191600101610ae8565b604083360312610a085788604091825161115781612ab3565b61116086612c4c565b815282860135838201528152019201916106ad565b8b80fd5b97505050503d8087873e61118d8187612acf565b60808682810103126110f4578551906111a7848801612ce8565b90604088015167ffffffffffffffff81116111f6576111cb90828a01908a0161324f565b97606081015167ffffffffffffffff81116110ec576111ed928201910161324f565b9190963861065e565b8980fd5b6020906112056131ec565b82828a010152016104b1565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b803b15611009578460405180927fe0a0e5060000000000000000000000000000000000000000000000000000000082528183816112846024356004018b60048401612d84565b03925af18015611014571561037357846112a091959295612acf565b9238610373565b6004847f1c0a3529000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081526002830160205260409020546103355760248573ffffffffffffffffffffffffffffffffffffffff857fd0d2597600000000000000000000000000000000000000000000000000000000835216600452fd5b6004847fa4ec7479000000000000000000000000000000000000000000000000000000008152fd5b6004847f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5760043567ffffffffffffffff811161138d576113e1903690600401612c6d565b73ffffffffffffffffffffffffffffffffffffffff600154163303611716575b919081907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8181360301915b84811015611712578060051b8201358381121561100957820191608083360312611009576040519461145d86612a2f565b61146684612bcc565b865261147460208501612bf9565b9660208701978852604085013567ffffffffffffffff811161170e5761149d903690870161309f565b9460408801958652606081013567ffffffffffffffff811161170a576114c59136910161309f565b60608801908152875167ffffffffffffffff1683526006602052604080842099518a547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff169015159182901b68ff000000000000000016178a559095908151516115e2575b5095976001019550815b85518051821015611573579061156c73ffffffffffffffffffffffffffffffffffffffff61156483600195613104565b51168961345b565b5001611534565b50509590969450600192919351908151611593575b50500193929361142c565b6115d867ffffffffffffffff7fc237ec1921f855ccd5e9a5af9733f2d58943a5a8501ec5988e305d7a4d42158692511692604051918291602083526020830190612c9e565b0390a23880611588565b989395929691909497986000146116d357600184019591875b86518051821015611678576116258273ffffffffffffffffffffffffffffffffffffffff92613104565b51168015611641579061163a6001928a6133ca565b50016115fb565b60248a67ffffffffffffffff8e51167f463258ff000000000000000000000000000000000000000000000000000000008252600452fd5b50509692955090929796937f330939f6eafe8bb516716892fe962ff19770570838686e6579dbc1cc51fc32816116c967ffffffffffffffff8a51169251604051918291602083526020830190612c9e565b0390a2388061152a565b60248767ffffffffffffffff8b51167f463258ff000000000000000000000000000000000000000000000000000000008252600452fd5b8380fd5b8280fd5b8380f35b73ffffffffffffffffffffffffffffffffffffffff60055416330315611401576004837f905d7d9b000000000000000000000000000000000000000000000000000000008152fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5767ffffffffffffffff61179f612bb0565b16808252600660205260ff604083205460401c16908252600660205260016040832001916040518093849160208254918281520191845260208420935b8181106118135750506117f192500383612acf565b61180f60405192839215158352604060208401526040830190612c9e565b0390f35b84548352600194850194879450602090930192016117dc565b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5767ffffffffffffffff61186d612bb0565b1681526006602052600167ffffffffffffffff604083205416019067ffffffffffffffff82116118ac5760208267ffffffffffffffff60405191168152f35b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57805473ffffffffffffffffffffffffffffffffffffffff811633036119e8577fffffffffffffffffffffffff000000000000000000000000000000000000000060015491338284161760015516825573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6004827f02b543c6000000000000000000000000000000000000000000000000000000008152fd5b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57611a47613074565b5060a0604051611a5681612a7a565b60ff60025473ffffffffffffffffffffffffffffffffffffffff81168352831c161515602082015273ffffffffffffffffffffffffffffffffffffffff60035416604082015273ffffffffffffffffffffffffffffffffffffffff60045416606082015273ffffffffffffffffffffffffffffffffffffffff600554166080820152611b2b604051809273ffffffffffffffffffffffffffffffffffffffff60808092828151168552602081015115156020860152826040820151166040860152826060820151166060860152015116910152565bf35b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57604060609167ffffffffffffffff611b73612bb0565b1681526006602052205473ffffffffffffffffffffffffffffffffffffffff6040519167ffffffffffffffff8116835260ff8160401c161515602084015260481c166040820152f35b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5760043567ffffffffffffffff811161138d57611c0c903690600401612c6d565b9073ffffffffffffffffffffffffffffffffffffffff6004541690835b83811015611ea05773ffffffffffffffffffffffffffffffffffffffff611c548260051b8401613053565b1690604051917f70a08231000000000000000000000000000000000000000000000000000000008352306004840152602083602481845afa928315611e95578793611e62575b5082611cac575b506001915001611c29565b8460405193611d6760208601957fa9059cbb00000000000000000000000000000000000000000000000000000000875283602482015282604482015260448152611cf7606482612acf565b8a80604098895193611d098b86612acf565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020860152519082895af13d15611e5a573d90611d4a82612b10565b91611d578a519384612acf565b82523d8d602084013e5b866135ed565b805180611da3575b505060207f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e9160019651908152a338611ca1565b819294959693509060209181010312611e56576020611dc29101612ce8565b15611dd35792919085903880611d6f565b608490517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b8880fd5b606090611d61565b9092506020813d8211611e8d575b81611e7d60209383612acf565b810103126110f457519138611c9a565b3d9150611e70565b6040513d89823e3d90fd5b8480f35b503461013c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57611edc612bb0565b506024359073ffffffffffffffffffffffffffffffffffffffff8216820361013c576020611f0983612f3a565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b503461013c5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57604051611f6381612a7a565b611f6b612c06565b8152602435801515810361170e576020820190815260443573ffffffffffffffffffffffffffffffffffffffff8116810361170a5760408301908152611faf612c29565b90606084019182526084359273ffffffffffffffffffffffffffffffffffffffff8416840361232d5760808501938452611fe7613367565b73ffffffffffffffffffffffffffffffffffffffff85511615801561230e575b8015612304575b6122dc579273ffffffffffffffffffffffffffffffffffffffff859381809461012097827fc7372d2d886367d7bb1b0e0708a5436f2c91d6963de210eb2dc1ec2ecd6d21f19a51167fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002555115157fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff00000000000000000000000000000000000000006002549260a01b1691161760025551167fffffffffffffffffffffffff0000000000000000000000000000000000000000600354161760035551167fffffffffffffffffffffffff0000000000000000000000000000000000000000600454161760045551167fffffffffffffffffffffffff000000000000000000000000000000000000000060055416176005556122d86040519161215883612a2f565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016835273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602084015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604084015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166060840152612288604051809473ffffffffffffffffffffffffffffffffffffffff6060809267ffffffffffffffff8151168552826020820151166020860152826040820151166040860152015116910152565b608083019073ffffffffffffffffffffffffffffffffffffffff60808092828151168552602081015115156020860152826040820151166040860152826060820151166060860152015116910152565ba180f35b6004867f35be3ac8000000000000000000000000000000000000000000000000000000008152fd5b508051151561200e565b5073ffffffffffffffffffffffffffffffffffffffff83511615612007565b8580fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c576004359067ffffffffffffffff821161013c573660238301121561013c57816004013561238d81612be1565b9261239b6040519485612acf565b818452602460606020860193028201019036821161170a57602401915b818310612539575050506123ca613367565b805b8251811015612535576123df8184613104565b5167ffffffffffffffff6123f38386613104565b51511690811561250957907fd5ad72bc37dc7a80a8b9b9df20500046fd7341adb1be2258a540466fdd7dcef5606060019493838752600660205260ff604088206124ca604060208501519483547fffffff0000000000000000000000000000000000000000ffffffffffffffffff7cffffffffffffffffffffffffffffffffffffffff0000000000000000008860481b1691161784550151151582907fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff68ff0000000000000000835492151560401b169116179055565b5473ffffffffffffffffffffffffffffffffffffffff6040519367ffffffffffffffff8316855216602084015260401c1615156040820152a2016123cc565b602484837fc35aa79d000000000000000000000000000000000000000000000000000000008252600452fd5b5080f35b60608336031261170a576040516060810181811067ffffffffffffffff8211176125b45760405261256984612bcc565b8152602084013573ffffffffffffffffffffffffffffffffffffffff8116810361232d5791816060936020809401526125a460408701612bf9565b60408201528152019201916123b8565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b503461013c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57612619612bb0565b60243567ffffffffffffffff811161170e5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc823603011261170e576040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815277ffffffffffffffff000000000000000000000000000000008360801b16600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561281a5784916127e0575b506127aa5761274c9160209173ffffffffffffffffffffffffffffffffffffffff60025416906040518095819482937fd8694ccd0000000000000000000000000000000000000000000000000000000084526004019060048401612d84565b03915afa90811561279f578291612769575b602082604051908152f35b90506020813d602011612797575b8161278460209383612acf565b8101031261138d5760209150513861275e565b3d9150612777565b6040513d84823e3d90fd5b60248367ffffffffffffffff847ffdbd6a7200000000000000000000000000000000000000000000000000000000835216600452fd5b90506020813d602011612812575b816127fb60209383612acf565b8101031261170a5761280c90612ce8565b386126ed565b3d91506127ee565b6040513d86823e3d90fd5b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c575061180f604051612866604082612acf565b600c81527f4f6e52616d7020312e362e3000000000000000000000000000000000000000006020820152604051918291602083526020830190612b6d565b90503461138d57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261138d57806128e0606092612a2f565b828152826020820152826040820152015260806040516128ff81612a2f565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604082015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166060820152611b2b604051809273ffffffffffffffffffffffffffffffffffffffff6060809267ffffffffffffffff8151168552826020820151166020860152826040820151166040860152015116910152565b6080810190811067ffffffffffffffff821117612a4b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff821117612a4b57604052565b610120810190811067ffffffffffffffff821117612a4b57604052565b6040810190811067ffffffffffffffff821117612a4b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612a4b57604052565b67ffffffffffffffff8111612a4b57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60005b838110612b5d5750506000910152565b8181015183820152602001612b4d565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612ba981518092818752878088019101612b4a565b0116010190565b6004359067ffffffffffffffff82168203612bc757565b600080fd5b359067ffffffffffffffff82168203612bc757565b67ffffffffffffffff8111612a4b5760051b60200190565b35908115158203612bc757565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203612bc757565b6064359073ffffffffffffffffffffffffffffffffffffffff82168203612bc757565b359073ffffffffffffffffffffffffffffffffffffffff82168203612bc757565b9181601f84011215612bc75782359167ffffffffffffffff8311612bc7576020808501948460051b010111612bc757565b906020808351928381520192019060005b818110612cbc5750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101612caf565b51908115158203612bc757565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe182360301811215612bc757016020813591019167ffffffffffffffff8211612bc7578136038313612bc757565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b9067ffffffffffffffff9093929316815260406020820152612dfa612dbd612dac8580612cf5565b60a0604086015260e0850191612d45565b612dca6020860186612cf5565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0858403016060860152612d45565b9060408401357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe185360301811215612bc75784016020813591019267ffffffffffffffff8211612bc7578160061b36038413612bc7578281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0016080840152818152602001929060005b818110612efb57505050612ec88473ffffffffffffffffffffffffffffffffffffffff612eb86060612ef8979801612c4c565b1660a08401526080810190612cf5565b9160c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082860301910152612d45565b90565b90919360408060019273ffffffffffffffffffffffffffffffffffffffff612f2289612c4c565b16815260208881013590820152019501929101612e85565b73ffffffffffffffffffffffffffffffffffffffff604051917fbbe4f6db00000000000000000000000000000000000000000000000000000000835216600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561304757600091612fe4575b5073ffffffffffffffffffffffffffffffffffffffff1690565b6020813d60201161303f575b81612ffd60209383612acf565b8101031261138d57519073ffffffffffffffffffffffffffffffffffffffff8216820361013c575073ffffffffffffffffffffffffffffffffffffffff612fca565b3d9150612ff0565b6040513d6000823e3d90fd5b3573ffffffffffffffffffffffffffffffffffffffff81168103612bc75790565b6040519061308182612a7a565b60006080838281528260208201528260408201528260608201520152565b9080601f83011215612bc75781356130b681612be1565b926130c46040519485612acf565b81845260208085019260051b820101928311612bc757602001905b8282106130ec5750505090565b602080916130f984612c4c565b8152019101906130df565b80518210156131185760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215612bc7570180359067ffffffffffffffff8211612bc757602001918136038313612bc757565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215612bc7570180359067ffffffffffffffff8211612bc757602001918160061b36038313612bc757565b604051906131f982612a7a565b6060608083600081528260208201528260408201526000838201520152565b92919261322482612b10565b916132326040519384612acf565b829481845281830111612bc7578281602093846000960137010152565b81601f82011215612bc757805161326581612b10565b926132736040519485612acf565b81845260208284010111612bc757612ef89160208085019101612b4a565b9080602083519182815201916020808360051b8301019401926000915b8383106132bd57505050505090565b9091929394602080613358837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0866001960301875289519073ffffffffffffffffffffffffffffffffffffffff8251168152608061333d61332b8685015160a08886015260a0850190612b6d565b60408501518482036040860152612b6d565b92606081015160608401520151906080818403910152612b6d565b970193019301919392906132ae565b73ffffffffffffffffffffffffffffffffffffffff60015416330361338857565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b80548210156131185760005260206000200190600090565b60008281526001820160205260409020546134545780549068010000000000000000821015612a4b578261343d6134088460018096018555846133b2565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b905580549260005201602052604060002055600190565b5050600090565b90600182019181600052826020526040600020548015156000146135e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018181116135b5578254907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116135b55781810361357e575b5050508054801561354f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019061351082826133b2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b191690555560005260205260006040812055600190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b61359e61358e61340893866133b2565b90549060031b1c928392866133b2565b9055600052836020526040600020553880806134d8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b50505050600090565b919290156136685750815115613601575090565b3b1561360a5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561367b5750805190602001fd5b6136b9906040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401526024830190612b6d565b0390fdfea164736f6c634300081a000a0000000000000000000000000000000000000000000000002220395d5f2affe100000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a1000000000000000000000000a27056438ffa1f286ab197488808692f0db93f8b000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54000000000000000000000000f4a170a36d4c656f614d44453f73308bdb27519600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062f05cd6c835677b05a8658a351969476861316000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608080604052600436101561001357600080fd5b600090813560e01c90816306285c69146128a457508063181f5a771461282557806320487ded146125e15780632716072b1461233157806327e936f114611f2757806348a98aa414611ea45780635cb80c5d14611bbc5780636def4ce714611b2d5780637437ff9f14611a1057806379ba50971461192b5780638da5cb5b146118d95780639041be3d1461182c578063972b46121461175e578063c9b146b314611391578063df0aa9e91461022c578063f2fde38b1461013f5763fbca3b74146100dc57600080fd5b3461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57600490610116612bb0565b507f9e7177c8000000000000000000000000000000000000000000000000000000008152fd5b80fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5773ffffffffffffffffffffffffffffffffffffffff61018c612c06565b610194613367565b1633811461020457807fffffffffffffffffffffffff000000000000000000000000000000000000000083541617825573ffffffffffffffffffffffffffffffffffffffff600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788380a380f35b6004827fdad89dca000000000000000000000000000000000000000000000000000000008152fd5b503461013c5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57610264612bb0565b67ffffffffffffffff6024351161138d5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6024353603011261138d576102ab612c29565b60025460ff8160a01c16611365577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001760025567ffffffffffffffff8216835260066020526040832073ffffffffffffffffffffffffffffffffffffffff82161561133d57805460ff8160401c166112cf575b60481c73ffffffffffffffffffffffffffffffffffffffff1633036112a75773ffffffffffffffffffffffffffffffffffffffff600354168061123e575b50805467ffffffffffffffff811667ffffffffffffffff8114611211579067ffffffffffffffff60017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009493011692839116179055604051906103d582612a7a565b84825267ffffffffffffffff7f0000000000000000000000000000000000000000000000002220395d5f2affe116602083015267ffffffffffffffff8416604083015260608201528360808201526104366024803501602435600401613147565b61044560046024350180613147565b610453606460243501613053565b93610468604460243501602435600401613198565b9490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06104ae61049887612be1565b966104a66040519889612acf565b808852612be1565b018a5b8181106111fa575050604051966104c788612a96565b875273ffffffffffffffffffffffffffffffffffffffff8816602088015236906104f092613218565b6040860152369061050092613218565b60608401526020916040516105158482612acf565b878152608085015273ffffffffffffffffffffffffffffffffffffffff1660a084015260443560c08401528560e084015261010083015260025473ffffffffffffffffffffffffffffffffffffffff16938560243560640161057690613053565b9561058a6024356084810190600401613147565b90919061059c60046024350180613147565b99906040519a8b95869485947f3a49bb4900000000000000000000000000000000000000000000000000000000865267ffffffffffffffff8b16600487015273ffffffffffffffffffffffffffffffffffffffff16602486015260443560448601526064850160a0905260a485019061061492612d45565b908382037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01608485015261064892612d45565b03915afa80156110f85786879688908993611179575b50608086015260e085015261067d604460243501602435600401613198565b909661068882612be1565b916106966040519384612acf565b8083528583018099368360061b8201116111755780915b8360061b8201831061113e5750505050885b6106d3604460243501602435600401613198565b9050811015610a3b576106e68184613104565b51906106f06131ec565b508682015115610a135773ffffffffffffffffffffffffffffffffffffffff61071b81845116612f3a565b169182158015610970575b61092e57808c878a8a73ffffffffffffffffffffffffffffffffffffffff8f836107d39801518280895116926040519761075f89612a7a565b885267ffffffffffffffff87890196168652816040890191168152606088019283526080880193845267ffffffffffffffff6040519b8c998a997f9a4575b9000000000000000000000000000000000000000000000000000000008b5260048b01525160a060248b015260c48a0190612b6d565b965116604488015251166064860152516084850152511660a4830152038183885af1918215610923578d809361086b575b50506001938284928b806108649651930151910151916040519361082785612a7a565b84528c840152604083015260608201528d604051906108468c83612acf565b815260808201526101008b01519061085e8383613104565b52613104565b50016106bf565b9250933d8093863e61087d8386612acf565b8985848101031261091f5784519167ffffffffffffffff831161091b576040838701858801031261091b576040516108b481612ab3565b8387015167ffffffffffffffff8111610916576108d89086890190868a010161324f565b81528b84880101519467ffffffffffffffff861161091657876108649688966109079360019b0192010161324f565b8c82015293509150938d610804565b508f80fd5b8e80fd5b8d80fd5b6040513d8f823e3d90fd5b517fbf16aab6000000000000000000000000000000000000000000000000000000008c5273ffffffffffffffffffffffffffffffffffffffff1660045260248bfd5b506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf0000000000000000000000000000000000000000000000000000000060048201528881602481875afa908115610923578d916109da575b5015610726565b90508881813d8311610a0c575b6109f18183612acf565b81010312610a0857610a0290612ce8565b386109d3565b8c80fd5b503d6109e7565b60048b7f5cf04449000000000000000000000000000000000000000000000000000000008152fd5b5090889692508690610ab39873ffffffffffffffffffffffffffffffffffffffff600254169061010089015192886040519c8d957f01447eaa00000000000000000000000000000000000000000000000000000000875267ffffffffffffffff8b166004880152606060248801526064870190613291565b917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8684030160448701525191828152019190855b8a828210611103575050505082809103915afa9687156110f857869761101f575b5015610f2d5750835b67ffffffffffffffff608085510191169052835b61010084015151811015610b5c5780610b4160019288613104565b516080610b5383610100890151613104565b51015201610b26565b5090926060610100604051610b7081612a96565b610b78613074565b8152838782015282604082015282808201528260808201528360a08201528360c08201528360e08201520152604051848101907f130ac867e79e2789f923760a88743d292acdf7002139a588206e2260f73f7321825267ffffffffffffffff7f0000000000000000000000000000000000000000000000002220395d5f2affe116604082015267ffffffffffffffff8416606082015230608082015260808152610c2360a082612acf565b51902073ffffffffffffffffffffffffffffffffffffffff602085015116845167ffffffffffffffff6080816060840151169201511673ffffffffffffffffffffffffffffffffffffffff60a08801511660c088015191604051938a850195865260408501526060840152608083015260a082015260a08152610ca760c082612acf565b51902060608501518681519101206040860151878151910120610100870151604051610d0d81610ce18c8201948d86526040830190613291565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612acf565b51902091608088015189815191012093604051958a870197885260408701526060860152608085015260a084015260c083015260e082015260e08152610d5561010082612acf565b51902082515267ffffffffffffffff60608351015116907f192442a2b2adb6a7948f097023cb6b57d29d3a7a5dd33e6666d33c39cc456f3260405185815267ffffffffffffffff608086518051898501528289820151166040850152826040820151166060850152826060820151168285015201511660a082015273ffffffffffffffffffffffffffffffffffffffff60208601511660c082015280610ef8610e7f610e4a610e1560408a01516101a060e08701526101c0860190612b6d565b60608a01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086830301610100870152612b6d565b60808901517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085830301610120860152612b6d565b73ffffffffffffffffffffffffffffffffffffffff60a08901511661014084015260c088015161016084015260e088015161018084015267ffffffffffffffff610100890151967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0858403016101a08601521695613291565b0390a37fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff600254166002555151604051908152f35b73ffffffffffffffffffffffffffffffffffffffff604051917fea458c0c00000000000000000000000000000000000000000000000000000000835267ffffffffffffffff8416600484015216602482015282816044818873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a27056438ffa1f286ab197488808692f0db93f8b165af1908115611014578591610fd2575b50610b12565b90508281813d831161100d575b610fe98183612acf565b81010312611009575167ffffffffffffffff811681036110095786610fcc565b8480fd5b503d610fdf565b6040513d87823e3d90fd5b9096503d908187823e6110328282612acf565b848183810103126110f45780519167ffffffffffffffff83116110f057808201601f8484010112156110f057828201519161106c83612be1565b9361107a6040519586612acf565b83855287850192808301898660051b8486010101116110ec578882840101935b898660051b848601010185106110b7575050505050509587610b09565b845167ffffffffffffffff8111610a08578a80926110df829383878a0191898b01010161324f565b815201950194905061109a565b8a80fd5b8780fd5b8680fd5b6040513d88823e3d90fd5b8351805173ffffffffffffffffffffffffffffffffffffffff168652810151818601528d97508e965060409094019390920191600101610ae8565b604083360312610a085788604091825161115781612ab3565b61116086612c4c565b815282860135838201528152019201916106ad565b8b80fd5b97505050503d8087873e61118d8187612acf565b60808682810103126110f4578551906111a7848801612ce8565b90604088015167ffffffffffffffff81116111f6576111cb90828a01908a0161324f565b97606081015167ffffffffffffffff81116110ec576111ed928201910161324f565b9190963861065e565b8980fd5b6020906112056131ec565b82828a010152016104b1565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b803b15611009578460405180927fe0a0e5060000000000000000000000000000000000000000000000000000000082528183816112846024356004018b60048401612d84565b03925af18015611014571561037357846112a091959295612acf565b9238610373565b6004847f1c0a3529000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081526002830160205260409020546103355760248573ffffffffffffffffffffffffffffffffffffffff857fd0d2597600000000000000000000000000000000000000000000000000000000835216600452fd5b6004847fa4ec7479000000000000000000000000000000000000000000000000000000008152fd5b6004847f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5760043567ffffffffffffffff811161138d576113e1903690600401612c6d565b73ffffffffffffffffffffffffffffffffffffffff600154163303611716575b919081907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8181360301915b84811015611712578060051b8201358381121561100957820191608083360312611009576040519461145d86612a2f565b61146684612bcc565b865261147460208501612bf9565b9660208701978852604085013567ffffffffffffffff811161170e5761149d903690870161309f565b9460408801958652606081013567ffffffffffffffff811161170a576114c59136910161309f565b60608801908152875167ffffffffffffffff1683526006602052604080842099518a547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff169015159182901b68ff000000000000000016178a559095908151516115e2575b5095976001019550815b85518051821015611573579061156c73ffffffffffffffffffffffffffffffffffffffff61156483600195613104565b51168961345b565b5001611534565b50509590969450600192919351908151611593575b50500193929361142c565b6115d867ffffffffffffffff7fc237ec1921f855ccd5e9a5af9733f2d58943a5a8501ec5988e305d7a4d42158692511692604051918291602083526020830190612c9e565b0390a23880611588565b989395929691909497986000146116d357600184019591875b86518051821015611678576116258273ffffffffffffffffffffffffffffffffffffffff92613104565b51168015611641579061163a6001928a6133ca565b50016115fb565b60248a67ffffffffffffffff8e51167f463258ff000000000000000000000000000000000000000000000000000000008252600452fd5b50509692955090929796937f330939f6eafe8bb516716892fe962ff19770570838686e6579dbc1cc51fc32816116c967ffffffffffffffff8a51169251604051918291602083526020830190612c9e565b0390a2388061152a565b60248767ffffffffffffffff8b51167f463258ff000000000000000000000000000000000000000000000000000000008252600452fd5b8380fd5b8280fd5b8380f35b73ffffffffffffffffffffffffffffffffffffffff60055416330315611401576004837f905d7d9b000000000000000000000000000000000000000000000000000000008152fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5767ffffffffffffffff61179f612bb0565b16808252600660205260ff604083205460401c16908252600660205260016040832001916040518093849160208254918281520191845260208420935b8181106118135750506117f192500383612acf565b61180f60405192839215158352604060208401526040830190612c9e565b0390f35b84548352600194850194879450602090930192016117dc565b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5767ffffffffffffffff61186d612bb0565b1681526006602052600167ffffffffffffffff604083205416019067ffffffffffffffff82116118ac5760208267ffffffffffffffff60405191168152f35b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57805473ffffffffffffffffffffffffffffffffffffffff811633036119e8577fffffffffffffffffffffffff000000000000000000000000000000000000000060015491338284161760015516825573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6004827f02b543c6000000000000000000000000000000000000000000000000000000008152fd5b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57611a47613074565b5060a0604051611a5681612a7a565b60ff60025473ffffffffffffffffffffffffffffffffffffffff81168352831c161515602082015273ffffffffffffffffffffffffffffffffffffffff60035416604082015273ffffffffffffffffffffffffffffffffffffffff60045416606082015273ffffffffffffffffffffffffffffffffffffffff600554166080820152611b2b604051809273ffffffffffffffffffffffffffffffffffffffff60808092828151168552602081015115156020860152826040820151166040860152826060820151166060860152015116910152565bf35b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57604060609167ffffffffffffffff611b73612bb0565b1681526006602052205473ffffffffffffffffffffffffffffffffffffffff6040519167ffffffffffffffff8116835260ff8160401c161515602084015260481c166040820152f35b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c5760043567ffffffffffffffff811161138d57611c0c903690600401612c6d565b9073ffffffffffffffffffffffffffffffffffffffff6004541690835b83811015611ea05773ffffffffffffffffffffffffffffffffffffffff611c548260051b8401613053565b1690604051917f70a08231000000000000000000000000000000000000000000000000000000008352306004840152602083602481845afa928315611e95578793611e62575b5082611cac575b506001915001611c29565b8460405193611d6760208601957fa9059cbb00000000000000000000000000000000000000000000000000000000875283602482015282604482015260448152611cf7606482612acf565b8a80604098895193611d098b86612acf565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020860152519082895af13d15611e5a573d90611d4a82612b10565b91611d578a519384612acf565b82523d8d602084013e5b866135ed565b805180611da3575b505060207f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e9160019651908152a338611ca1565b819294959693509060209181010312611e56576020611dc29101612ce8565b15611dd35792919085903880611d6f565b608490517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b8880fd5b606090611d61565b9092506020813d8211611e8d575b81611e7d60209383612acf565b810103126110f457519138611c9a565b3d9150611e70565b6040513d89823e3d90fd5b8480f35b503461013c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57611edc612bb0565b506024359073ffffffffffffffffffffffffffffffffffffffff8216820361013c576020611f0983612f3a565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b503461013c5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57604051611f6381612a7a565b611f6b612c06565b8152602435801515810361170e576020820190815260443573ffffffffffffffffffffffffffffffffffffffff8116810361170a5760408301908152611faf612c29565b90606084019182526084359273ffffffffffffffffffffffffffffffffffffffff8416840361232d5760808501938452611fe7613367565b73ffffffffffffffffffffffffffffffffffffffff85511615801561230e575b8015612304575b6122dc579273ffffffffffffffffffffffffffffffffffffffff859381809461012097827fc7372d2d886367d7bb1b0e0708a5436f2c91d6963de210eb2dc1ec2ecd6d21f19a51167fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002555115157fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff00000000000000000000000000000000000000006002549260a01b1691161760025551167fffffffffffffffffffffffff0000000000000000000000000000000000000000600354161760035551167fffffffffffffffffffffffff0000000000000000000000000000000000000000600454161760045551167fffffffffffffffffffffffff000000000000000000000000000000000000000060055416176005556122d86040519161215883612a2f565b67ffffffffffffffff7f0000000000000000000000000000000000000000000000002220395d5f2affe116835273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a116602084015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a27056438ffa1f286ab197488808692f0db93f8b16604084015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54166060840152612288604051809473ffffffffffffffffffffffffffffffffffffffff6060809267ffffffffffffffff8151168552826020820151166020860152826040820151166040860152015116910152565b608083019073ffffffffffffffffffffffffffffffffffffffff60808092828151168552602081015115156020860152826040820151166040860152826060820151166060860152015116910152565ba180f35b6004867f35be3ac8000000000000000000000000000000000000000000000000000000008152fd5b508051151561200e565b5073ffffffffffffffffffffffffffffffffffffffff83511615612007565b8580fd5b503461013c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c576004359067ffffffffffffffff821161013c573660238301121561013c57816004013561238d81612be1565b9261239b6040519485612acf565b818452602460606020860193028201019036821161170a57602401915b818310612539575050506123ca613367565b805b8251811015612535576123df8184613104565b5167ffffffffffffffff6123f38386613104565b51511690811561250957907fd5ad72bc37dc7a80a8b9b9df20500046fd7341adb1be2258a540466fdd7dcef5606060019493838752600660205260ff604088206124ca604060208501519483547fffffff0000000000000000000000000000000000000000ffffffffffffffffff7cffffffffffffffffffffffffffffffffffffffff0000000000000000008860481b1691161784550151151582907fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff68ff0000000000000000835492151560401b169116179055565b5473ffffffffffffffffffffffffffffffffffffffff6040519367ffffffffffffffff8316855216602084015260401c1615156040820152a2016123cc565b602484837fc35aa79d000000000000000000000000000000000000000000000000000000008252600452fd5b5080f35b60608336031261170a576040516060810181811067ffffffffffffffff8211176125b45760405261256984612bcc565b8152602084013573ffffffffffffffffffffffffffffffffffffffff8116810361232d5791816060936020809401526125a460408701612bf9565b60408201528152019201916123b8565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b503461013c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c57612619612bb0565b60243567ffffffffffffffff811161170e5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc823603011261170e576040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815277ffffffffffffffff000000000000000000000000000000008360801b16600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a1165afa90811561281a5784916127e0575b506127aa5761274c9160209173ffffffffffffffffffffffffffffffffffffffff60025416906040518095819482937fd8694ccd0000000000000000000000000000000000000000000000000000000084526004019060048401612d84565b03915afa90811561279f578291612769575b602082604051908152f35b90506020813d602011612797575b8161278460209383612acf565b8101031261138d5760209150513861275e565b3d9150612777565b6040513d84823e3d90fd5b60248367ffffffffffffffff847ffdbd6a7200000000000000000000000000000000000000000000000000000000835216600452fd5b90506020813d602011612812575b816127fb60209383612acf565b8101031261170a5761280c90612ce8565b386126ed565b3d91506127ee565b6040513d86823e3d90fd5b503461013c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013c575061180f604051612866604082612acf565b600c81527f4f6e52616d7020312e362e3000000000000000000000000000000000000000006020820152604051918291602083526020830190612b6d565b90503461138d57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261138d57806128e0606092612a2f565b828152826020820152826040820152015260806040516128ff81612a2f565b67ffffffffffffffff7f0000000000000000000000000000000000000000000000002220395d5f2affe116815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000032270e4fa459ca47ae0334488e27ffb9bc9ab4a116602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a27056438ffa1f286ab197488808692f0db93f8b16604082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54166060820152611b2b604051809273ffffffffffffffffffffffffffffffffffffffff6060809267ffffffffffffffff8151168552826020820151166020860152826040820151166040860152015116910152565b6080810190811067ffffffffffffffff821117612a4b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff821117612a4b57604052565b610120810190811067ffffffffffffffff821117612a4b57604052565b6040810190811067ffffffffffffffff821117612a4b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612a4b57604052565b67ffffffffffffffff8111612a4b57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60005b838110612b5d5750506000910152565b8181015183820152602001612b4d565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612ba981518092818752878088019101612b4a565b0116010190565b6004359067ffffffffffffffff82168203612bc757565b600080fd5b359067ffffffffffffffff82168203612bc757565b67ffffffffffffffff8111612a4b5760051b60200190565b35908115158203612bc757565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203612bc757565b6064359073ffffffffffffffffffffffffffffffffffffffff82168203612bc757565b359073ffffffffffffffffffffffffffffffffffffffff82168203612bc757565b9181601f84011215612bc75782359167ffffffffffffffff8311612bc7576020808501948460051b010111612bc757565b906020808351928381520192019060005b818110612cbc5750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101612caf565b51908115158203612bc757565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe182360301811215612bc757016020813591019167ffffffffffffffff8211612bc7578136038313612bc757565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b9067ffffffffffffffff9093929316815260406020820152612dfa612dbd612dac8580612cf5565b60a0604086015260e0850191612d45565b612dca6020860186612cf5565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0858403016060860152612d45565b9060408401357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe185360301811215612bc75784016020813591019267ffffffffffffffff8211612bc7578160061b36038413612bc7578281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0016080840152818152602001929060005b818110612efb57505050612ec88473ffffffffffffffffffffffffffffffffffffffff612eb86060612ef8979801612c4c565b1660a08401526080810190612cf5565b9160c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082860301910152612d45565b90565b90919360408060019273ffffffffffffffffffffffffffffffffffffffff612f2289612c4c565b16815260208881013590820152019501929101612e85565b73ffffffffffffffffffffffffffffffffffffffff604051917fbbe4f6db00000000000000000000000000000000000000000000000000000000835216600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000048b911a1ae5dd4f0aee5241a30d3deda3501d54165afa90811561304757600091612fe4575b5073ffffffffffffffffffffffffffffffffffffffff1690565b6020813d60201161303f575b81612ffd60209383612acf565b8101031261138d57519073ffffffffffffffffffffffffffffffffffffffff8216820361013c575073ffffffffffffffffffffffffffffffffffffffff612fca565b3d9150612ff0565b6040513d6000823e3d90fd5b3573ffffffffffffffffffffffffffffffffffffffff81168103612bc75790565b6040519061308182612a7a565b60006080838281528260208201528260408201528260608201520152565b9080601f83011215612bc75781356130b681612be1565b926130c46040519485612acf565b81845260208085019260051b820101928311612bc757602001905b8282106130ec5750505090565b602080916130f984612c4c565b8152019101906130df565b80518210156131185760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215612bc7570180359067ffffffffffffffff8211612bc757602001918136038313612bc757565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215612bc7570180359067ffffffffffffffff8211612bc757602001918160061b36038313612bc757565b604051906131f982612a7a565b6060608083600081528260208201528260408201526000838201520152565b92919261322482612b10565b916132326040519384612acf565b829481845281830111612bc7578281602093846000960137010152565b81601f82011215612bc757805161326581612b10565b926132736040519485612acf565b81845260208284010111612bc757612ef89160208085019101612b4a565b9080602083519182815201916020808360051b8301019401926000915b8383106132bd57505050505090565b9091929394602080613358837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0866001960301875289519073ffffffffffffffffffffffffffffffffffffffff8251168152608061333d61332b8685015160a08886015260a0850190612b6d565b60408501518482036040860152612b6d565b92606081015160608401520151906080818403910152612b6d565b970193019301919392906132ae565b73ffffffffffffffffffffffffffffffffffffffff60015416330361338857565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b80548210156131185760005260206000200190600090565b60008281526001820160205260409020546134545780549068010000000000000000821015612a4b578261343d6134088460018096018555846133b2565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b905580549260005201602052604060002055600190565b5050600090565b90600182019181600052826020526040600020548015156000146135e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018181116135b5578254907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116135b55781810361357e575b5050508054801561354f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019061351082826133b2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b191690555560005260205260006040812055600190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b61359e61358e61340893866133b2565b90549060031b1c928392866133b2565b9055600052836020526040600020553880806134d8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b50505050600090565b919290156136685750815115613601575090565b3b1561360a5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561367b5750805190602001fd5b6136b9906040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401526024830190612b6d565b0390fdfea164736f6c634300081a000a

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0x1E11bAB3f07fa72312182fFDc460AE45400E6e7b
Loading...
Loading
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.