ETH Price: $1,863.61 (-2.39%)

Contract

0x3596fB4e61dE7d5Cca41Ae5a3fFFcCdedE313a4e
 

Overview

ETH Balance

0.000492889457441965 ETH

Eth Value

$0.92 (@ $1,863.61/ETH)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Travel188888582023-12-29 4:27:47456 days ago1703824067IN
0x3596fB4e...edE313a4e
0.00111428 ETH0.0032338517.91220883
Travel188846722023-12-28 14:22:35456 days ago1703773355IN
0x3596fB4e...edE313a4e
0.00110404 ETH0.0078587843.52957827
Travel188445252023-12-22 23:02:47462 days ago1703286167IN
0x3596fB4e...edE313a4e
0.00439551 ETH0.0042859423.73970863
Travel187318112023-12-07 3:43:11478 days ago1701920591IN
0x3596fB4e...edE313a4e
0.0007614 ETH0.0056259131.16178142
Travel186860872023-11-30 18:04:47484 days ago1701367487IN
0x3596fB4e...edE313a4e
0.00067743 ETH0.0144172872.61908479
Travel186855782023-11-30 16:21:59484 days ago1701361319IN
0x3596fB4e...edE313a4e
0.0006868 ETH0.0127517270.63142514
Travel186855642023-11-30 16:19:11484 days ago1701361151IN
0x3596fB4e...edE313a4e
0.00068695 ETH0.0139329570.17529294
Travel186855522023-11-30 16:16:47484 days ago1701361007IN
0x3596fB4e...edE313a4e
0.0006868 ETH0.0111732961.88851344
Travel186854442023-11-30 15:55:11484 days ago1701359711IN
0x3596fB4e...edE313a4e
0.0006868 ETH0.0108584460.14460499
Travel186852622023-11-30 15:18:23484 days ago1701357503IN
0x3596fB4e...edE313a4e
0.0006868 ETH0.0096646553.53225004
Travel186844192023-11-30 12:27:47484 days ago1701347267IN
0x3596fB4e...edE313a4e
0.00067382 ETH0.0060112230.27638838
Travel186840692023-11-30 11:16:59484 days ago1701343019IN
0x3596fB4e...edE313a4e
0.00067888 ETH0.0086206929.87280333
Travel186840482023-11-30 11:12:47484 days ago1701342767IN
0x3596fB4e...edE313a4e
0.000678 ETH0.0043090523.86772038
Travel186840112023-11-30 11:05:23484 days ago1701342323IN
0x3596fB4e...edE313a4e
0.000678 ETH0.0045802425.37151543
Travel186838872023-11-30 10:40:35485 days ago1701340835IN
0x3596fB4e...edE313a4e
0.00068582 ETH0.0048546526.88980115
Travel186838352023-11-30 10:30:11485 days ago1701340211IN
0x3596fB4e...edE313a4e
0.00068731 ETH0.0099188527.50577197
Travel186837722023-11-30 10:17:23485 days ago1701339443IN
0x3596fB4e...edE313a4e
0.00068382 ETH0.004587725.4111328
Travel186837272023-11-30 10:08:11485 days ago1701338891IN
0x3596fB4e...edE313a4e
0.00068501 ETH0.0101199331.17710768
Travel186833372023-11-30 8:49:11485 days ago1701334151IN
0x3596fB4e...edE313a4e
0.00069106 ETH0.0053190829.46224487
Travel186827132023-11-30 6:43:35485 days ago1701326615IN
0x3596fB4e...edE313a4e
0.00072428 ETH0.0056070228.24057548
Travel186824192023-11-30 5:44:47485 days ago1701323087IN
0x3596fB4e...edE313a4e
0.00072197 ETH0.0064486529.7789431
Travel186823702023-11-30 5:34:47485 days ago1701322487IN
0x3596fB4e...edE313a4e
0.00072166 ETH0.0054784630.34505635
Travel186822172023-11-30 5:04:11485 days ago1701320651IN
0x3596fB4e...edE313a4e
0.00072166 ETH0.0054022429.92286806
Travel186814962023-11-30 2:39:11485 days ago1701311951IN
0x3596fB4e...edE313a4e
0.00068478 ETH0.006404235.4726771
Travel186813502023-11-30 2:09:59485 days ago1701310199IN
0x3596fB4e...edE313a4e
0.00068427 ETH0.0080057240.32194933
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Ccip Send188888582023-12-29 4:27:47456 days ago1703824067
0x3596fB4e...edE313a4e
0.00111428 ETH
Ccip Send188846722023-12-28 14:22:35456 days ago1703773355
0x3596fB4e...edE313a4e
0.00110404 ETH
Ccip Send188445252023-12-22 23:02:47462 days ago1703286167
0x3596fB4e...edE313a4e
0.00439551 ETH
Ccip Send187318112023-12-07 3:43:11478 days ago1701920591
0x3596fB4e...edE313a4e
0.0007614 ETH
Ccip Send186860872023-11-30 18:04:47484 days ago1701367487
0x3596fB4e...edE313a4e
0.00067743 ETH
Ccip Send186855782023-11-30 16:21:59484 days ago1701361319
0x3596fB4e...edE313a4e
0.0006868 ETH
Ccip Send186855642023-11-30 16:19:11484 days ago1701361151
0x3596fB4e...edE313a4e
0.00068695 ETH
Ccip Send186855522023-11-30 16:16:47484 days ago1701361007
0x3596fB4e...edE313a4e
0.0006868 ETH
Ccip Send186854442023-11-30 15:55:11484 days ago1701359711
0x3596fB4e...edE313a4e
0.0006868 ETH
Ccip Send186852622023-11-30 15:18:23484 days ago1701357503
0x3596fB4e...edE313a4e
0.0006868 ETH
Ccip Send186844192023-11-30 12:27:47484 days ago1701347267
0x3596fB4e...edE313a4e
0.00067382 ETH
Ccip Send186840692023-11-30 11:16:59484 days ago1701343019
0x3596fB4e...edE313a4e
0.00067888 ETH
Ccip Send186840482023-11-30 11:12:47484 days ago1701342767
0x3596fB4e...edE313a4e
0.000678 ETH
Ccip Send186840112023-11-30 11:05:23484 days ago1701342323
0x3596fB4e...edE313a4e
0.000678 ETH
Ccip Send186838872023-11-30 10:40:35485 days ago1701340835
0x3596fB4e...edE313a4e
0.00068582 ETH
Ccip Send186838352023-11-30 10:30:11485 days ago1701340211
0x3596fB4e...edE313a4e
0.00068731 ETH
Ccip Send186837722023-11-30 10:17:23485 days ago1701339443
0x3596fB4e...edE313a4e
0.00068382 ETH
Ccip Send186837272023-11-30 10:08:11485 days ago1701338891
0x3596fB4e...edE313a4e
0.00068501 ETH
Ccip Send186833372023-11-30 8:49:11485 days ago1701334151
0x3596fB4e...edE313a4e
0.00069106 ETH
Ccip Send186827132023-11-30 6:43:35485 days ago1701326615
0x3596fB4e...edE313a4e
0.00072428 ETH
Ccip Send186824192023-11-30 5:44:47485 days ago1701323087
0x3596fB4e...edE313a4e
0.00072197 ETH
Ccip Send186823702023-11-30 5:34:47485 days ago1701322487
0x3596fB4e...edE313a4e
0.00072166 ETH
Ccip Send186822172023-11-30 5:04:11485 days ago1701320651
0x3596fB4e...edE313a4e
0.00072166 ETH
Ccip Send186814962023-11-30 2:39:11485 days ago1701311951
0x3596fB4e...edE313a4e
0.00068478 ETH
Ccip Send186813502023-11-30 2:09:59485 days ago1701310199
0x3596fB4e...edE313a4e
0.00068427 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CCIPeth

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 17 : CCIPeth.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";

/// @title Cozy Penguin NFT bridge powered by Chainlink CCIP - Eth side
/// @author Cozy Labs
/// @notice Send Cozy Penguin NFTs to Avalanche using Chainlink CCIP
///         Code reviewed by engineers external to Cozy Labs, no formal audit
///         WARNING: Experimental, use or replicate at your own risk.
contract CCIPeth is CCIPReceiver {
    error EmergencyPaused();
    error NotEnoughFees(uint256 calculatedFees, uint256 sentFees);
    error NotConfirmedSourceChain(uint64 sourceChainSelector);
    error NotConfirmedSourceAddress(address sourceAddress);
    error NotOwner(address caller, uint256 tokenId);
    error NotAdmin(address caller);
    error TravelLocked();
    error FailedToWithdrawEth(address admin, address target, uint256 value);
    error MigrationNotProposed();
    error TimestampNotPassed(uint blockTimestamp, uint allowedTimestamp);
    error ExceededMaxAmountOfNfts();
    error NotEOA();
    error NotSortedTokenIds();

    /// @notice Message ID of successful travel call to CCIP
    event MessageSent(bytes32 messageId);
    /// @notice Message ID of a received message from authorized sender
    event MessageReceived(bytes32 messageId);
    /// @notice tokenIds of Cozy Penguins unlocked and the address
    ///         they are sent to
    event PenguinsUnlocked(address owner, uint256[] tokenIds);
    /// @notice A migration of NFTs locked in the contract is proposed
    event MigrationQueued();
    /// @notice A migration of tokens out of the contract occurred
    event MigrationExecuted(address indexed migrateTo, uint256[] tokenIds);
    /// @notice A migration proposal is cancelled
    event MigrationCanceled();
    /// @notice A new authorized source address is set
    event SourceAddressSet(address sourceAddress);
    /// @notice A new contract admin is set
    event AdminSet(address admin);
    /// @notice A new gas limit on travel calls is set
    event GasLimitSet(uint256 gasLimit);
    /// @notice A new limit on NFTs sent per travel call is set
    event MaxAmountOfNftsSet(uint16);

    /// @notice the Cozy Penguin NFT contract
    ERC721 public immutable cozyPenguin;
    /// @notice the CCIP router client
    IRouterClient public immutable router;
    /// @notice address of the bridge contract on Avalanche
    address public targetAddress;
    /// @notice address of the admin that can perform maintenance
    address private admin;
    /// @notice address of the contract that this contract is
    ///         authorized to receive CCIP messages from
    address public sourceAddress;
    /// @notice proposed address to migrate locked Cozys to
    address public migrationAddress;
    /// @notice minimum time required to pass after proposal to migrate
    uint256 public constant MIGRATION_DELAY_DAYS = 7 days;
    /// @notice timestamp after migration to proposed address can occur
    uint256 public migrationAllowedTimestamp;
    /// @notice gasLimit of receiving CCIP call on destination chain
    uint256 public gasLimit = 2000000;
    /// @notice CCIP chain selector of destination (and source)
    uint64 public immutable destinationChainSelector;
    /// @notice Max amount of NFTs that can be transferred at once
    uint16 public maxAmountOfNfts = 25;
    /// @notice boolean that allows users to travel
    bool public travelLock = false;
    /// @notice boolean that allows receiver to process inbound messages
    bool public emergencyPause = false;

    /// @param _router CCIP router address
    /// @param _admin Contract admin address
    /// @param _cozyPenguinNft address of the ERC NFT contract
    /// @param _destinationChainSelector ID of the bridge sends and receives from
    /// @param _travelLock Initial state of travelLock (true = users can't send)
    constructor(
        address _router,
        address _admin,
        address _cozyPenguinNft,
        uint64 _destinationChainSelector,
        bool _travelLock
    ) CCIPReceiver(_router) {
        admin = _admin;
        cozyPenguin = ERC721(_cozyPenguinNft);
        require(
            IRouterClient(_router).isChainSupported(_destinationChainSelector),
            "Unsupported chain selector"
        );
        destinationChainSelector = _destinationChainSelector;
        travelLock = _travelLock;
        router = IRouterClient(_router);
    }

    /// @notice Modifier for admin only calls
    modifier onlyAdmin() {
        if (msg.sender != admin) {
            revert NotAdmin(msg.sender);
        }
        _;
    }

    /// @notice modifier to confirm cross chain message
    ///         sender contract address
    modifier onlyConfirmedSourceAddress(address _sourceAddress) {
        if (_sourceAddress != sourceAddress) {
            revert NotConfirmedSourceAddress(_sourceAddress);
        }
        _;
    }

    /// @notice modifier to confirm chain the message came from
    modifier onlyConfirmedSourceChain(uint64 _sourceChainSelector) {
        if (_sourceChainSelector != destinationChainSelector) {
            revert NotConfirmedSourceChain(_sourceChainSelector);
        }
        _;
    }

    /// @notice modifier to ensure traveling NFTs under limit
    modifier allowedAmountOfNfts(uint256 _amountOfNfts) {
        if (_amountOfNfts > maxAmountOfNfts) {
            revert ExceededMaxAmountOfNfts();
        }
        _;
    }

    /// @notice modifier to ensure bridge is unlocked
    modifier unlocked() {
        if (travelLock) {
            revert TravelLocked();
        }
        _;
    }

    /// @notice modifier to ensure no emeregency pause before
    ///         processing a CCIP receive message
    modifier unpaused() {
        if (emergencyPause) {
            revert EmergencyPaused();
        }
        _;
    }

    /// @notice Best effort check to ensure Cozys are sent from EOAs,
    ///         as destinations address is same as sender address
    /// @dev Can be circumvented via constructor call - doesn't benefit caller
    modifier onlyEOA() {
        address sender = msg.sender;
        uint256 size;
        assembly {
            size := extcodesize(sender)
        }
        if (size > 0) {
            revert NotEOA();
        }
        _;
    }

    // ------ CCIP ---------

    /// @notice Calculate the fees for a travel call
    /// @param _tokenIds An ordered list of token Ids to be sent
    function travelRequest(
        uint256[] calldata _tokenIds
    )
        external
        view
        unlocked
        allowedAmountOfNfts(_tokenIds.length)
        returns (uint256 fees)
    {
        Client.EVM2AnyMessage memory message = _buildCCIPMessage(_tokenIds);
        fees = router.getFee(destinationChainSelector, message);

        return fees;
    }

    /// @notice Builds the message to be sent to the CCIP router
    function _buildCCIPMessage(
        uint256[] calldata _tokenIds
    ) public view returns (Client.EVM2AnyMessage memory) {
        bytes memory messageData = abi.encode(msg.sender, _tokenIds);
        Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
            receiver: abi.encode(targetAddress),
            data: messageData,
            tokenAmounts: new Client.EVMTokenAmount[](0),
            extraArgs: Client._argsToBytes(
                Client.EVMExtraArgsV1({gasLimit: gasLimit, strict: false})
            ),
            feeToken: address(0)
        });

        return evm2AnyMessage;
    }

    /// @notice Locks NFT tokens and sends a message to mint/unlock on
    ///         destination chain and target address
    /// @notice Must send fee amount determined by `travelRequest`
    /// @notice Can only call travel from EOAs, sending from contract
    ///         will result in locked Cozys!!!
    /// @param _tokenIds An ordered list of token ids to be sent.
    ///        Note: out of order or duplicate ids will result in a revert
    function travel(
        uint256[] calldata _tokenIds
    )
        external
        payable
        onlyEOA
        allowedAmountOfNfts(_tokenIds.length)
        unlocked
        returns (bytes32 messageId)
    {
        Client.EVM2AnyMessage memory message = _buildCCIPMessage(_tokenIds);
        uint256 fees = router.getFee(destinationChainSelector, message);

        if (fees > msg.value) revert NotEnoughFees(fees, msg.value);

        uint256 tokenId;
        for (uint256 i = 0; i < _tokenIds.length; ++i) {
            tokenId = _tokenIds[i];
            if (i > 0 && tokenId <= _tokenIds[i - 1]) {
                revert NotSortedTokenIds();
            }
            if (cozyPenguin.ownerOf(tokenId) != msg.sender) {
                revert NotOwner(msg.sender, tokenId);
            }
            cozyPenguin.transferFrom(
                msg.sender,
                address(this),
                tokenId
            );
        }

        messageId = router.ccipSend{value: fees}(
            destinationChainSelector,
            message
        );

        emit MessageSent(messageId);
    }

    /// @notice Receive owner & tokenIds data from CCIP. Unlocks penguins
    function _ccipReceive(
        Client.Any2EVMMessage memory message
    )
        internal
        override
        unpaused
        onlyConfirmedSourceAddress(abi.decode(message.sender, (address)))
        onlyConfirmedSourceChain(message.sourceChainSelector)
    {
        emit MessageReceived(message.messageId);
        (address owner, uint256[] memory tokenIds) = abi.decode(
            message.data,
            (address, uint256[])
        );

        unlockPenguin(owner, tokenIds);
        emit PenguinsUnlocked(owner, tokenIds);
    }

    /// @notice Sends penguins from contract to owner
    /// @param _owner address to send penguins to
    /// @param _tokenIds Token ids to send
    function unlockPenguin(
        address _owner,
        uint256[] memory _tokenIds
    ) internal {
        uint256 tokenId;
        for (uint256 i = 0; i < _tokenIds.length; ++i) {
            tokenId = _tokenIds[i];
            cozyPenguin.transferFrom(address(this), _owner, tokenId);
        }
    }

    // ------ Receive Ether ---------

    receive() external payable {}

    // ------ Withdraw Ether ---------

    /// @notice Withdraw native token. Not part of core workflow
    /// @notice Admin only operation
    /// @param beneficiary address to send tokens to
    function withdraw(address beneficiary) external onlyAdmin {
        uint256 amount = address(this).balance;
        (bool sent, ) = beneficiary.call{value: amount}("");
        if (!sent) revert FailedToWithdrawEth(msg.sender, beneficiary, amount);
    }

    // ------ Administration ---------

    /// @notice Set new target contract address for CCIP messages
    /// @notice Admin only operation
    /// @param _targetAddress New address CCIP will send messages to
    function setTargetAddress(address _targetAddress) external onlyAdmin {
        targetAddress = _targetAddress;
    }

    /// @notice Set new source contract address to receive CCIP messages
    /// @notice Admin only operation
    /// @param _sourceAddress New address that contract is authorized to
    ///        receive CCIP messages from
    function setSourceAddress(address _sourceAddress) external onlyAdmin {
        sourceAddress = _sourceAddress;
        emit SourceAddressSet(_sourceAddress);
    }

    /// @notice Set new contract admin
    /// @notice Admin only operation
    /// @param _admin New address for contract admin
    function setAdmin(address _admin) external onlyAdmin {
        admin = _admin;
        emit AdminSet(_admin);
    }

    /// @notice Set new max NFTs that can travel in one call
    /// @notice Admin only operation
    /// @param _maxAmountOfNfts New number of max NFts to be sent
    function setMaxAmountOfNfts(uint16 _maxAmountOfNfts) external onlyAdmin {
        maxAmountOfNfts = _maxAmountOfNfts;
        emit MaxAmountOfNftsSet(_maxAmountOfNfts);
    }

    /// @notice Set new Gas Limit on what CCIP can use on destination call
    /// @notice Admin only operation
    /// @param _gasLimit New gas limit (max 2,000,000 for now)
    function setGasLimit(uint256 _gasLimit) external onlyAdmin {
        gasLimit = _gasLimit;
        emit GasLimitSet(_gasLimit);
    }

    // ------------ Locking -------------

    /// @notice Set travel lock state (true = no travel, false = can travel)
    /// @notice Admin only operation
    /// @param _lock Boolean determining if the travel call is locked
    function lockTravel(bool _lock) external onlyAdmin {
        travelLock = _lock;
    }

    /// @notice Set emergency pause state that determines if contract can
    ///         can receive CCIP messages
    /// @notice Admin only operation
    /// @param _pause Boolean determining pause (true = no message, false = yes)
    function setEmergencyPause(bool _pause) external onlyAdmin {
        emergencyPause = _pause;
    }

    // -------- Migrating -----------

    /// @notice Propose new address to migrate to. Subject to timelock
    /// @notice Admin only operation
    /// @param _migrateTo New address to allow transfer of NFTs to
    function proposeMigration(address _migrateTo) external onlyAdmin {
        migrationAddress = _migrateTo;
        migrationAllowedTimestamp = block.timestamp + MIGRATION_DELAY_DAYS;
        emit MigrationQueued();
    }

    /// @notice Cancel migration proposal by setting migration address
    ///         to the zero address
    /// @notice Admin only operation
    function cancelMigration() external onlyAdmin {
        migrationAddress = address(0);
        emit MigrationCanceled();
    }

    /// @notice Migrate specified NFTs to the migration address
    /// @notice Can only migrate eafter timelock is up
    /// @notice Can't migrate to zero address
    /// @notice Admin only operation
    /// @param tokenIds Token ids to migrate
    function migrate(uint256[] calldata tokenIds) external onlyAdmin {
        if (migrationAddress == address(0)) {
            revert MigrationNotProposed();
        }
        if (block.timestamp < migrationAllowedTimestamp) {
            revert TimestampNotPassed(
                block.timestamp,
                migrationAllowedTimestamp
            );
        }
        uint256 tokenId;
        for (uint256 i = 0; i < tokenIds.length; ++i) {
            tokenId = tokenIds[i];
            if (i > 0 && tokenId <= tokenIds[i - 1]) {
                revert NotSortedTokenIds();
            }
            if (cozyPenguin.ownerOf(tokenId) != address(this)) {
                revert NotOwner(address(this), tokenId);
            }
            cozyPenguin.safeTransferFrom(
                address(this),
                migrationAddress,
                tokenId
            );
        }
        emit MigrationExecuted(migrationAddress, tokenIds);
    }
}

File 2 of 17 : IRouterClient.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

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

interface IRouterClient {
  error UnsupportedDestinationChain(uint64 destChainSelector);
  error InsufficientFeeTokenAmount();
  error InvalidMsgValue();

  /// @notice Checks if the given chain ID is supported for sending/receiving.
  /// @param chainSelector The chain to check.
  /// @return supported is true if it is supported, false if not.
  function isChainSupported(uint64 chainSelector) external view returns (bool supported);

  /// @notice Gets a list of all supported tokens which can be sent or received
  /// to/from a given chain id.
  /// @param chainSelector The chainSelector.
  /// @return tokens The addresses of all tokens that are supported.
  function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);

  /// @param destinationChainSelector The destination chainSelector
  /// @param message The cross-chain CCIP message including data and/or tokens
  /// @return fee returns guaranteed execution fee for the specified message
  /// delivery to destination chain
  /// @dev returns 0 fee on invalid message.
  function getFee(
    uint64 destinationChainSelector,
    Client.EVM2AnyMessage memory message
  ) external view returns (uint256 fee);

  /// @notice Request a message to be sent to the destination chain
  /// @param destinationChainSelector The destination chain ID
  /// @param message The cross-chain CCIP message including data and/or tokens
  /// @return messageId The message ID
  /// @dev Note if msg.value is larger than the required fee (from getFee) we accept
  /// the overpayment with no refund.
  function ccipSend(
    uint64 destinationChainSelector,
    Client.EVM2AnyMessage calldata message
  ) external payable returns (bytes32);
}

File 3 of 17 : CCIPReceiver.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

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

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

import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.0/utils/introspection/IERC165.sol";

/// @title CCIPReceiver - Base contract for CCIP applications that can receive messages.
abstract contract CCIPReceiver is IAny2EVMMessageReceiver, IERC165 {
  address private immutable i_router;

  constructor(address router) {
    if (router == address(0)) revert InvalidRouter(address(0));
    i_router = router;
  }

  /// @notice IERC165 supports an interfaceId
  /// @param interfaceId The interfaceId to check
  /// @return true if the interfaceId is supported
  function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
    return interfaceId == type(IAny2EVMMessageReceiver).interfaceId || interfaceId == type(IERC165).interfaceId;
  }

  /// @inheritdoc IAny2EVMMessageReceiver
  function ccipReceive(Client.Any2EVMMessage calldata message) external override onlyRouter {
    _ccipReceive(message);
  }

  /// @notice Override this function in your implementation.
  /// @param message Any2EVMMessage
  function _ccipReceive(Client.Any2EVMMessage memory message) internal virtual;

  /////////////////////////////////////////////////////////////////////
  // Plumbing
  /////////////////////////////////////////////////////////////////////

  /// @notice Return the current router
  /// @return i_router address
  function getRouter() public view returns (address) {
    return address(i_router);
  }

  error InvalidRouter(address router);

  /// @dev only calls from the set router are accepted.
  modifier onlyRouter() {
    if (msg.sender != address(i_router)) revert InvalidRouter(msg.sender);
    _;
  }
}

File 4 of 17 : Client.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

// End consumer library.
library Client {
  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 and strict = false.
  struct EVM2AnyMessage {
    bytes receiver; // abi.encode(receiver address) for dest EVM chains
    bytes data; // Data payload
    EVMTokenAmount[] tokenAmounts; // Token transfers
    address feeToken; // Address of feeToken. address(0) means you will send msg.value.
    bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)
  }

  // extraArgs will evolve to support new features
  // bytes4(keccak256("CCIP EVMExtraArgsV1"));
  bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;
  struct EVMExtraArgsV1 {
    uint256 gasLimit; // ATTENTION!!! MAX GAS LIMIT 4M FOR BETA TESTING
    bool strict; // See strict sequencing details below.
  }

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

File 5 of 17 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.2) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner or approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId, 1);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId, 1);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
     * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
     * that `ownerOf(tokenId)` is `a`.
     */
    // solhint-disable-next-line func-name-mixedcase
    function __unsafe_increaseBalance(address account, uint256 amount) internal {
        _balances[account] += amount;
    }
}

File 6 of 17 : IAny2EVMMessageReceiver.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

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

/// @notice Application contracts that intend to receive messages from
/// the router should implement this interface.
interface IAny2EVMMessageReceiver {
  /// @notice Called by the Router to deliver a message.
  /// If this reverts, any token transfers also revert. The message
  /// will move to a FAILED state and become available for manual execution.
  /// @param message CCIP Message
  /// @dev Note ensure you check the msg.sender is the OffRampRouter
  function ccipReceive(Client.Any2EVMMessage calldata message) external;
}

File 7 of 17 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 8 of 17 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 9 of 17 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 10 of 17 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

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

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

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

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

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

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

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

File 12 of 17 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 13 of 17 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 14 of 17 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 15 of 17 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 16 of 17 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 17 of 17 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_router","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_cozyPenguinNft","type":"address"},{"internalType":"uint64","name":"_destinationChainSelector","type":"uint64"},{"internalType":"bool","name":"_travelLock","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EmergencyPaused","type":"error"},{"inputs":[],"name":"ExceededMaxAmountOfNfts","type":"error"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"FailedToWithdrawEth","type":"error"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"InvalidRouter","type":"error"},{"inputs":[],"name":"MigrationNotProposed","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NotAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"sourceAddress","type":"address"}],"name":"NotConfirmedSourceAddress","type":"error"},{"inputs":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"}],"name":"NotConfirmedSourceChain","type":"error"},{"inputs":[],"name":"NotEOA","type":"error"},{"inputs":[{"internalType":"uint256","name":"calculatedFees","type":"uint256"},{"internalType":"uint256","name":"sentFees","type":"uint256"}],"name":"NotEnoughFees","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotSortedTokenIds","type":"error"},{"inputs":[{"internalType":"uint256","name":"blockTimestamp","type":"uint256"},{"internalType":"uint256","name":"allowedTimestamp","type":"uint256"}],"name":"TimestampNotPassed","type":"error"},{"inputs":[],"name":"TravelLocked","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"AdminSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"GasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"","type":"uint16"}],"name":"MaxAmountOfNftsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"messageId","type":"bytes32"}],"name":"MessageReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"messageId","type":"bytes32"}],"name":"MessageSent","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"migrateTo","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"MigrationExecuted","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"PenguinsUnlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sourceAddress","type":"address"}],"name":"SourceAddressSet","type":"event"},{"inputs":[],"name":"MIGRATION_DELAY_DAYS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"_buildCCIPMessage","outputs":[{"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":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"messageId","type":"bytes32"},{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"bytes","name":"sender","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":"destTokenAmounts","type":"tuple[]"}],"internalType":"struct Client.Any2EVMMessage","name":"message","type":"tuple"}],"name":"ccipReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cozyPenguin","outputs":[{"internalType":"contract ERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"destinationChainSelector","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyPause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_lock","type":"bool"}],"name":"lockTravel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxAmountOfNfts","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrationAllowedTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_migrateTo","type":"address"}],"name":"proposeMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract IRouterClient","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_pause","type":"bool"}],"name":"setEmergencyPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasLimit","type":"uint256"}],"name":"setGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_maxAmountOfNfts","type":"uint16"}],"name":"setMaxAmountOfNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sourceAddress","type":"address"}],"name":"setSourceAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_targetAddress","type":"address"}],"name":"setTargetAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sourceAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"targetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"travel","outputs":[{"internalType":"bytes32","name":"messageId","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"travelLock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"travelRequest","outputs":[{"internalType":"uint256","name":"fees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

610100604052621e84806005556006805463ffffffff191660191790553480156200002957600080fd5b50604051620020df380380620020df8339810160408190526200004c91620001ca565b846001600160a01b0381166200007d576040516335fdcccd60e21b8152600060048201526024015b60405180910390fd5b6001600160a01b03908116608052600180546001600160a01b03191686831617905583811660a052604051631491520b60e31b81526001600160401b03841660048201529086169063a48a905890602401602060405180830381865afa158015620000ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000112919062000248565b620001605760405162461bcd60e51b815260206004820152601a60248201527f556e737570706f7274656420636861696e2073656c6563746f72000000000000604482015260640162000074565b6001600160401b0390911660e05260068054911515620100000262ff00001990921691909117905550506001600160a01b031660c0526200026d565b80516001600160a01b0381168114620001b457600080fd5b919050565b80518015158114620001b457600080fd5b600080600080600060a08688031215620001e357600080fd5b620001ee866200019c565b9450620001fe602087016200019c565b93506200020e604087016200019c565b60608701519093506001600160401b03811681146200022c57600080fd5b91506200023c60808701620001b9565b90509295509295909350565b6000602082840312156200025b57600080fd5b6200026682620001b9565b9392505050565b60805160a05160c05160e051611ddd620003026000396000818161029d01528181610bd40152818161104b0152818161130101526114870152600081816105bb01528181610ba70152818161101c01526112d20152600081816103a601528181610db001528181610e650152818161118c0152818161123e01526115ab0152600081816104a50152610a530152611ddd6000f3fe6080604052600436106101c65760003560e01c806371c396cc116100f7578063d8d1a65711610095578063f4eb7dec11610064578063f4eb7dec14610580578063f68016b714610593578063f887ea40146105a9578063fd8b2370146105dd57600080fd5b8063d8d1a65714610509578063d93bf4fe14610529578063ee7d72b414610549578063f0c9ad951461056957600080fd5b806386215a3d116100d157806386215a3d14610476578063b0f479a114610496578063b0fa8444146104c9578063c115e875146104e957600080fd5b806371c396cc14610416578063737669c51461043657806385572ffb1461045657600080fd5b80634dd5fbd5116101645780635210eb561161013e5780635210eb561461035c578063623702ee146103945780636b840ad0146103c8578063704b6c02146103f657600080fd5b80634dd5fbd5146102f757806351858e271461031b57806351cff8d91461033c57600080fd5b8063150be4d0116101a0578063150be4d01461023e5780632f587fe61461026b57806331db1ffc1461028b57806345facd8e146102d757600080fd5b806301ffc9a7146101d25780630b3c5dc01461020757806310639ea01461022957600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed366004611642565b6105fd565b60405190151581526020015b60405180910390f35b34801561021357600080fd5b50610227610222366004611673565b610634565b005b34801561023557600080fd5b50610227610682565b34801561024a57600080fd5b5061025e610259366004611695565b6106ea565b6040516101fe9190611812565b34801561027757600080fd5b50610227610286366004611825565b61082e565b34801561029757600080fd5b506102bf7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b0390911681526020016101fe565b3480156102e357600080fd5b506102276102f236600461185e565b6108a6565b34801561030357600080fd5b5061030d60045481565b6040519081526020016101fe565b34801561032757600080fd5b506006546101f2906301000000900460ff1681565b34801561034857600080fd5b5061022761035736600461185e565b610921565b34801561036857600080fd5b5060005461037c906001600160a01b031681565b6040516001600160a01b0390911681526020016101fe565b3480156103a057600080fd5b5061037c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103d457600080fd5b506006546103e39061ffff1681565b60405161ffff90911681526020016101fe565b34801561040257600080fd5b5061022761041136600461185e565b6109cd565b34801561042257600080fd5b5060035461037c906001600160a01b031681565b34801561044257600080fd5b506006546101f29062010000900460ff1681565b34801561046257600080fd5b5061022761047136600461187b565b610a48565b34801561048257600080fd5b5061022761049136600461185e565b610aa7565b3480156104a257600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061037c565b3480156104d557600080fd5b5060025461037c906001600160a01b031681565b3480156104f557600080fd5b5061030d610504366004611695565b610b2b565b34801561051557600080fd5b50610227610524366004611673565b610c48565b34801561053557600080fd5b50610227610544366004611695565b610c93565b34801561055557600080fd5b506102276105643660046118b5565b610f31565b34801561057557600080fd5b5061030d62093a8081565b61030d61058e366004611695565b610f93565b34801561059f57600080fd5b5061030d60055481565b3480156105b557600080fd5b5061037c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105e957600080fd5b506102276105f836600461185e565b6113b5565b60006001600160e01b031982166385572ffb60e01b148061062e57506001600160e01b031982166301ffc9a760e01b145b92915050565b6001546001600160a01b0316331461066657604051630bd4212160e11b81523360048201526024015b60405180910390fd5b60068054911515620100000262ff000019909216919091179055565b6001546001600160a01b031633146106af57604051630bd4212160e11b815233600482015260240161065d565b600380546001600160a01b03191690556040517faf83ca22877ecfa047bfa3500257eb7579dea2675c33d5ba2d8c4a9d47dff5c490600090a1565b6107256040518060a0016040528060608152602001606081526020016060815260200160006001600160a01b03168152602001606081525090565b600033848460405160200161073c93929190611900565b60408051601f1981840301815260a083018252600080546001600160a01b031660c0808601919091528351808603909101815260e08501845284526020808501839052835182815290810184529194509291820190836107be565b60408051808201909152600080825260208201528152602001906001900390816107975790505b508152600060208083018290526040805180820182526005548082529083019384528151602481019190915292511515604480850191909152815180850390910181526064909301815290820180516001600160e01b03166397a657c960e01b1790529091015295945050505050565b6001546001600160a01b0316331461085b57604051630bd4212160e11b815233600482015260240161065d565b6006805461ffff191661ffff83169081179091556040519081527ffad4e67750a0b2304e6e9519417ba9b4cdae2603e2f0b92b02117a687f97fca8906020015b60405180910390a150565b6001546001600160a01b031633146108d357604051630bd4212160e11b815233600482015260240161065d565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f77d154bf889c92f66d621896ddee089cef088d9347e711a21954744ccd3085509060200161089b565b6001546001600160a01b0316331461094e57604051630bd4212160e11b815233600482015260240161065d565b60405147906000906001600160a01b0384169083908381818185875af1925050503d806000811461099b576040519150601f19603f3d011682016040523d82523d6000602084013e6109a0565b606091505b50509050806109c857338383604051639d11f56360e01b815260040161065d9392919061193b565b505050565b6001546001600160a01b031633146109fa57604051630bd4212160e11b815233600482015260240161065d565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c9060200161089b565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a93576040516335fdcccd60e21b815233600482015260240161065d565b610aa4610a9f82611b1f565b611404565b50565b6001546001600160a01b03163314610ad457604051630bd4212160e11b815233600482015260240161065d565b600380546001600160a01b0319166001600160a01b038316179055610afc62093a8042611be1565b6004556040517fe5e4ec5fcfff613780a446c1a874b18abae9c7220bc04b480752214d1b05682e90600090a150565b60065460009062010000900460ff1615610b585760405163b0111e7760e01b815260040160405180910390fd5b600654829061ffff16811115610b81576040516392efc4b160e01b815260040160405180910390fd5b6000610b8d85856106ea565b6040516320487ded60e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906320487ded90610bfe907f0000000000000000000000000000000000000000000000000000000000000000908590600401611bf4565b602060405180830381865afa158015610c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3f9190611c1e565b95945050505050565b6001546001600160a01b03163314610c7557604051630bd4212160e11b815233600482015260240161065d565b6006805491151563010000000263ff00000019909216919091179055565b6001546001600160a01b03163314610cc057604051630bd4212160e11b815233600482015260240161065d565b6003546001600160a01b0316610ce95760405163599d7d3b60e01b815260040160405180910390fd5b600454421015610d19576004805460405163749a32c560e11b81524292810192909252602482015260440161065d565b6000805b82811015610ee257838382818110610d3757610d37611c37565b905060200201359150600081118015610d7257508383610d58600184611c4d565b818110610d6757610d67611c37565b905060200201358211155b15610d905760405163f1dee28d60e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810183905230906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015610df7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1b9190611c60565b6001600160a01b031614610e4b57604051636f7f64bf60e01b81523060048201526024810183905260440161065d565b600354604051632142170760e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116926342842e0e92610e9f9230921690879060040161193b565b600060405180830381600087803b158015610eb957600080fd5b505af1158015610ecd573d6000803e3d6000fd5b5050505080610edb90611c7d565b9050610d1d565b506003546040516001600160a01b03909116907fbd8ae6a6c9843d398efc680cda3d57ef991294ae0bbe481336ff8fc49bc81d0490610f249086908690611c96565b60405180910390a2505050565b6001546001600160a01b03163314610f5e57604051630bd4212160e11b815233600482015260240161065d565b60058190556040518181527f336210500e2973a38a8d7b3f978ac5bf874a3326119f3dff68651e472a1ae7729060200161089b565b600033803b8015610fb757604051635d04968b60e11b815260040160405180910390fd5b600654849061ffff16811115610fe0576040516392efc4b160e01b815260040160405180910390fd5b60065462010000900460ff161561100a5760405163b0111e7760e01b815260040160405180910390fd5b600061101687876106ea565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166320487ded7f0000000000000000000000000000000000000000000000000000000000000000846040518363ffffffff1660e01b8152600401611088929190611bf4565b602060405180830381865afa1580156110a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c99190611c1e565b9050348111156110f557604051634686467d60e11b81526004810182905234602482015260440161065d565b6000805b888110156112ba5789898281811061111357611113611c37565b90506020020135915060008111801561114e57508989611134600184611c4d565b81811061114357611143611c37565b905060200201358211155b1561116c5760405163f1dee28d60e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810183905233906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa1580156111d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f79190611c60565b6001600160a01b03161461122757604051636f7f64bf60e01b81523360048201526024810183905260440161065d565b6040516323b872dd60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906112779033903090879060040161193b565b600060405180830381600087803b15801561129157600080fd5b505af11580156112a5573d6000803e3d6000fd5b50505050806112b390611c7d565b90506110f9565b506040516396f4e9f960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906396f4e9f990849061132b907f0000000000000000000000000000000000000000000000000000000000000000908890600401611bf4565b60206040518083038185885af1158015611349573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061136e9190611c1e565b96507f54791b38f3859327992a1ca0590ad3c0f08feba98d1a4f56ab0dca74d203392a876040516113a191815260200190565b60405180910390a150505050505092915050565b6001546001600160a01b031633146113e257604051630bd4212160e11b815233600482015260240161065d565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6006546301000000900460ff161561142f57604051634cb3183d60e01b815260040160405180910390fd5b80604001518060200190518101906114479190611c60565b6002546001600160a01b0382811691161461148057604051630faadd1560e31b81526001600160a01b038216600482015260240161065d565b81602001517f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316816001600160401b0316146114e257604051630b17e82b60e01b81526001600160401b038216600482015260240161065d565b82516040519081527fe29dc34207c78fc0f6048a32f159139c33339c6d6df8b07dcd33f6d699ff23279060200160405180910390a160008084606001518060200190518101906115329190611caa565b915091506115408282611580565b7f6efae2a3255f3bcd53cecc9ce33bb4dd806291040f179c0448c94d149aa338e98282604051611571929190611d51565b60405180910390a15050505050565b6000805b825181101561163c5782818151811061159f5761159f611c37565b602002602001015191507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd3086856040518463ffffffff1660e01b81526004016115f99392919061193b565b600060405180830381600087803b15801561161357600080fd5b505af1158015611627573d6000803e3d6000fd5b505050508061163590611c7d565b9050611584565b50505050565b60006020828403121561165457600080fd5b81356001600160e01b03198116811461166c57600080fd5b9392505050565b60006020828403121561168557600080fd5b8135801515811461166c57600080fd5b600080602083850312156116a857600080fd5b82356001600160401b03808211156116bf57600080fd5b818501915085601f8301126116d357600080fd5b8135818111156116e257600080fd5b8660208260051b85010111156116f757600080fd5b60209290920196919550909350505050565b6000815180845260005b8181101561172f57602081850181015186830182015201611713565b506000602082860101526020601f19601f83011685010191505092915050565b6000815160a0845261176460a0850182611709565b90506020808401518583038287015261177d8382611709565b60408681015188830389830152805180845290850195509092506000918401905b808310156117d057855180516001600160a01b031683528501518583015294840194600192909201919083019061179e565b50606087015194506117ed60608901866001600160a01b03169052565b6080870151945087810360808901526118068186611709565b98975050505050505050565b60208152600061166c602083018461174f565b60006020828403121561183757600080fd5b813561ffff8116811461166c57600080fd5b6001600160a01b0381168114610aa457600080fd5b60006020828403121561187057600080fd5b813561166c81611849565b60006020828403121561188d57600080fd5b81356001600160401b038111156118a357600080fd5b820160a0818503121561166c57600080fd5b6000602082840312156118c757600080fd5b5035919050565b81835260006001600160fb1b038311156118e757600080fd5b8260051b80836020870137939093016020019392505050565b6001600160a01b0384168152604060208201819052600090610c3f90830184866118ce565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b604080519081016001600160401b038111828210171561198157611981611925565b60405290565b60405160a081016001600160401b038111828210171561198157611981611925565b604051601f8201601f191681016001600160401b03811182821017156119d1576119d1611925565b604052919050565b80356001600160401b03811681146119f057600080fd5b919050565b600082601f830112611a0657600080fd5b81356001600160401b03811115611a1f57611a1f611925565b611a32601f8201601f19166020016119a9565b818152846020838601011115611a4757600080fd5b816020850160208301376000918101602001919091529392505050565b60006001600160401b03821115611a7d57611a7d611925565b5060051b60200190565b600082601f830112611a9857600080fd5b81356020611aad611aa883611a64565b6119a9565b82815260069290921b84018101918181019086841115611acc57600080fd5b8286015b84811015611b145760408189031215611ae95760008081fd5b611af161195f565b8135611afc81611849565b81528185013585820152835291830191604001611ad0565b509695505050505050565b600060a08236031215611b3157600080fd5b611b39611987565b82358152611b49602084016119d9565b602082015260408301356001600160401b0380821115611b6857600080fd5b611b74368387016119f5565b60408401526060850135915080821115611b8d57600080fd5b611b99368387016119f5565b60608401526080850135915080821115611bb257600080fd5b50611bbf36828601611a87565b60808301525092915050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062e5761062e611bcb565b6001600160401b0383168152604060208201526000611c16604083018461174f565b949350505050565b600060208284031215611c3057600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b8181038181111561062e5761062e611bcb565b600060208284031215611c7257600080fd5b815161166c81611849565b600060018201611c8f57611c8f611bcb565b5060010190565b602081526000611c166020830184866118ce565b60008060408385031215611cbd57600080fd5b8251611cc881611849565b809250506020808401516001600160401b03811115611ce657600080fd5b8401601f81018613611cf757600080fd5b8051611d05611aa882611a64565b81815260059190911b82018301908381019088831115611d2457600080fd5b928401925b82841015611d4257835182529284019290840190611d29565b80955050505050509250929050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b81811015611d9a57845183529383019391830191600101611d7e565b509097965050505050505056fea2646970667358221220843dbda32337a71189910a4196996096e6829db22655cbb0e1b57da6a904c67a64736f6c63430008130033000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a147600000000000000000000000003b437df93ce6f16b60957fe8d4ba847222a9f8700000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a280000000000000000000000000000000000000000000000000594862ae1802b3d50000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x6080604052600436106101c65760003560e01c806371c396cc116100f7578063d8d1a65711610095578063f4eb7dec11610064578063f4eb7dec14610580578063f68016b714610593578063f887ea40146105a9578063fd8b2370146105dd57600080fd5b8063d8d1a65714610509578063d93bf4fe14610529578063ee7d72b414610549578063f0c9ad951461056957600080fd5b806386215a3d116100d157806386215a3d14610476578063b0f479a114610496578063b0fa8444146104c9578063c115e875146104e957600080fd5b806371c396cc14610416578063737669c51461043657806385572ffb1461045657600080fd5b80634dd5fbd5116101645780635210eb561161013e5780635210eb561461035c578063623702ee146103945780636b840ad0146103c8578063704b6c02146103f657600080fd5b80634dd5fbd5146102f757806351858e271461031b57806351cff8d91461033c57600080fd5b8063150be4d0116101a0578063150be4d01461023e5780632f587fe61461026b57806331db1ffc1461028b57806345facd8e146102d757600080fd5b806301ffc9a7146101d25780630b3c5dc01461020757806310639ea01461022957600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed366004611642565b6105fd565b60405190151581526020015b60405180910390f35b34801561021357600080fd5b50610227610222366004611673565b610634565b005b34801561023557600080fd5b50610227610682565b34801561024a57600080fd5b5061025e610259366004611695565b6106ea565b6040516101fe9190611812565b34801561027757600080fd5b50610227610286366004611825565b61082e565b34801561029757600080fd5b506102bf7f000000000000000000000000000000000000000000000000594862ae1802b3d581565b6040516001600160401b0390911681526020016101fe565b3480156102e357600080fd5b506102276102f236600461185e565b6108a6565b34801561030357600080fd5b5061030d60045481565b6040519081526020016101fe565b34801561032757600080fd5b506006546101f2906301000000900460ff1681565b34801561034857600080fd5b5061022761035736600461185e565b610921565b34801561036857600080fd5b5060005461037c906001600160a01b031681565b6040516001600160a01b0390911681526020016101fe565b3480156103a057600080fd5b5061037c7f00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a28081565b3480156103d457600080fd5b506006546103e39061ffff1681565b60405161ffff90911681526020016101fe565b34801561040257600080fd5b5061022761041136600461185e565b6109cd565b34801561042257600080fd5b5060035461037c906001600160a01b031681565b34801561044257600080fd5b506006546101f29062010000900460ff1681565b34801561046257600080fd5b5061022761047136600461187b565b610a48565b34801561048257600080fd5b5061022761049136600461185e565b610aa7565b3480156104a257600080fd5b507f000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a147661037c565b3480156104d557600080fd5b5060025461037c906001600160a01b031681565b3480156104f557600080fd5b5061030d610504366004611695565b610b2b565b34801561051557600080fd5b50610227610524366004611673565b610c48565b34801561053557600080fd5b50610227610544366004611695565b610c93565b34801561055557600080fd5b506102276105643660046118b5565b610f31565b34801561057557600080fd5b5061030d62093a8081565b61030d61058e366004611695565b610f93565b34801561059f57600080fd5b5061030d60055481565b3480156105b557600080fd5b5061037c7f000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a147681565b3480156105e957600080fd5b506102276105f836600461185e565b6113b5565b60006001600160e01b031982166385572ffb60e01b148061062e57506001600160e01b031982166301ffc9a760e01b145b92915050565b6001546001600160a01b0316331461066657604051630bd4212160e11b81523360048201526024015b60405180910390fd5b60068054911515620100000262ff000019909216919091179055565b6001546001600160a01b031633146106af57604051630bd4212160e11b815233600482015260240161065d565b600380546001600160a01b03191690556040517faf83ca22877ecfa047bfa3500257eb7579dea2675c33d5ba2d8c4a9d47dff5c490600090a1565b6107256040518060a0016040528060608152602001606081526020016060815260200160006001600160a01b03168152602001606081525090565b600033848460405160200161073c93929190611900565b60408051601f1981840301815260a083018252600080546001600160a01b031660c0808601919091528351808603909101815260e08501845284526020808501839052835182815290810184529194509291820190836107be565b60408051808201909152600080825260208201528152602001906001900390816107975790505b508152600060208083018290526040805180820182526005548082529083019384528151602481019190915292511515604480850191909152815180850390910181526064909301815290820180516001600160e01b03166397a657c960e01b1790529091015295945050505050565b6001546001600160a01b0316331461085b57604051630bd4212160e11b815233600482015260240161065d565b6006805461ffff191661ffff83169081179091556040519081527ffad4e67750a0b2304e6e9519417ba9b4cdae2603e2f0b92b02117a687f97fca8906020015b60405180910390a150565b6001546001600160a01b031633146108d357604051630bd4212160e11b815233600482015260240161065d565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f77d154bf889c92f66d621896ddee089cef088d9347e711a21954744ccd3085509060200161089b565b6001546001600160a01b0316331461094e57604051630bd4212160e11b815233600482015260240161065d565b60405147906000906001600160a01b0384169083908381818185875af1925050503d806000811461099b576040519150601f19603f3d011682016040523d82523d6000602084013e6109a0565b606091505b50509050806109c857338383604051639d11f56360e01b815260040161065d9392919061193b565b505050565b6001546001600160a01b031633146109fa57604051630bd4212160e11b815233600482015260240161065d565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f8fe72c3e0020beb3234e76ae6676fa576fbfcae600af1c4fea44784cf0db329c9060200161089b565b336001600160a01b037f000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a14761614610a93576040516335fdcccd60e21b815233600482015260240161065d565b610aa4610a9f82611b1f565b611404565b50565b6001546001600160a01b03163314610ad457604051630bd4212160e11b815233600482015260240161065d565b600380546001600160a01b0319166001600160a01b038316179055610afc62093a8042611be1565b6004556040517fe5e4ec5fcfff613780a446c1a874b18abae9c7220bc04b480752214d1b05682e90600090a150565b60065460009062010000900460ff1615610b585760405163b0111e7760e01b815260040160405180910390fd5b600654829061ffff16811115610b81576040516392efc4b160e01b815260040160405180910390fd5b6000610b8d85856106ea565b6040516320487ded60e01b81529091506001600160a01b037f000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a147616906320487ded90610bfe907f000000000000000000000000000000000000000000000000594862ae1802b3d5908590600401611bf4565b602060405180830381865afa158015610c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3f9190611c1e565b95945050505050565b6001546001600160a01b03163314610c7557604051630bd4212160e11b815233600482015260240161065d565b6006805491151563010000000263ff00000019909216919091179055565b6001546001600160a01b03163314610cc057604051630bd4212160e11b815233600482015260240161065d565b6003546001600160a01b0316610ce95760405163599d7d3b60e01b815260040160405180910390fd5b600454421015610d19576004805460405163749a32c560e11b81524292810192909252602482015260440161065d565b6000805b82811015610ee257838382818110610d3757610d37611c37565b905060200201359150600081118015610d7257508383610d58600184611c4d565b818110610d6757610d67611c37565b905060200201358211155b15610d905760405163f1dee28d60e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810183905230906001600160a01b037f00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a2801690636352211e90602401602060405180830381865afa158015610df7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1b9190611c60565b6001600160a01b031614610e4b57604051636f7f64bf60e01b81523060048201526024810183905260440161065d565b600354604051632142170760e11b81526001600160a01b037f00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a2808116926342842e0e92610e9f9230921690879060040161193b565b600060405180830381600087803b158015610eb957600080fd5b505af1158015610ecd573d6000803e3d6000fd5b5050505080610edb90611c7d565b9050610d1d565b506003546040516001600160a01b03909116907fbd8ae6a6c9843d398efc680cda3d57ef991294ae0bbe481336ff8fc49bc81d0490610f249086908690611c96565b60405180910390a2505050565b6001546001600160a01b03163314610f5e57604051630bd4212160e11b815233600482015260240161065d565b60058190556040518181527f336210500e2973a38a8d7b3f978ac5bf874a3326119f3dff68651e472a1ae7729060200161089b565b600033803b8015610fb757604051635d04968b60e11b815260040160405180910390fd5b600654849061ffff16811115610fe0576040516392efc4b160e01b815260040160405180910390fd5b60065462010000900460ff161561100a5760405163b0111e7760e01b815260040160405180910390fd5b600061101687876106ea565b905060007f000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a14766001600160a01b03166320487ded7f000000000000000000000000000000000000000000000000594862ae1802b3d5846040518363ffffffff1660e01b8152600401611088929190611bf4565b602060405180830381865afa1580156110a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c99190611c1e565b9050348111156110f557604051634686467d60e11b81526004810182905234602482015260440161065d565b6000805b888110156112ba5789898281811061111357611113611c37565b90506020020135915060008111801561114e57508989611134600184611c4d565b81811061114357611143611c37565b905060200201358211155b1561116c5760405163f1dee28d60e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810183905233906001600160a01b037f00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a2801690636352211e90602401602060405180830381865afa1580156111d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f79190611c60565b6001600160a01b03161461122757604051636f7f64bf60e01b81523360048201526024810183905260440161065d565b6040516323b872dd60e01b81526001600160a01b037f00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a28016906323b872dd906112779033903090879060040161193b565b600060405180830381600087803b15801561129157600080fd5b505af11580156112a5573d6000803e3d6000fd5b50505050806112b390611c7d565b90506110f9565b506040516396f4e9f960e01b81526001600160a01b037f000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a147616906396f4e9f990849061132b907f000000000000000000000000000000000000000000000000594862ae1802b3d5908890600401611bf4565b60206040518083038185885af1158015611349573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061136e9190611c1e565b96507f54791b38f3859327992a1ca0590ad3c0f08feba98d1a4f56ab0dca74d203392a876040516113a191815260200190565b60405180910390a150505050505092915050565b6001546001600160a01b031633146113e257604051630bd4212160e11b815233600482015260240161065d565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6006546301000000900460ff161561142f57604051634cb3183d60e01b815260040160405180910390fd5b80604001518060200190518101906114479190611c60565b6002546001600160a01b0382811691161461148057604051630faadd1560e31b81526001600160a01b038216600482015260240161065d565b81602001517f000000000000000000000000000000000000000000000000594862ae1802b3d56001600160401b0316816001600160401b0316146114e257604051630b17e82b60e01b81526001600160401b038216600482015260240161065d565b82516040519081527fe29dc34207c78fc0f6048a32f159139c33339c6d6df8b07dcd33f6d699ff23279060200160405180910390a160008084606001518060200190518101906115329190611caa565b915091506115408282611580565b7f6efae2a3255f3bcd53cecc9ce33bb4dd806291040f179c0448c94d149aa338e98282604051611571929190611d51565b60405180910390a15050505050565b6000805b825181101561163c5782818151811061159f5761159f611c37565b602002602001015191507f00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a2806001600160a01b03166323b872dd3086856040518463ffffffff1660e01b81526004016115f99392919061193b565b600060405180830381600087803b15801561161357600080fd5b505af1158015611627573d6000803e3d6000fd5b505050508061163590611c7d565b9050611584565b50505050565b60006020828403121561165457600080fd5b81356001600160e01b03198116811461166c57600080fd5b9392505050565b60006020828403121561168557600080fd5b8135801515811461166c57600080fd5b600080602083850312156116a857600080fd5b82356001600160401b03808211156116bf57600080fd5b818501915085601f8301126116d357600080fd5b8135818111156116e257600080fd5b8660208260051b85010111156116f757600080fd5b60209290920196919550909350505050565b6000815180845260005b8181101561172f57602081850181015186830182015201611713565b506000602082860101526020601f19601f83011685010191505092915050565b6000815160a0845261176460a0850182611709565b90506020808401518583038287015261177d8382611709565b60408681015188830389830152805180845290850195509092506000918401905b808310156117d057855180516001600160a01b031683528501518583015294840194600192909201919083019061179e565b50606087015194506117ed60608901866001600160a01b03169052565b6080870151945087810360808901526118068186611709565b98975050505050505050565b60208152600061166c602083018461174f565b60006020828403121561183757600080fd5b813561ffff8116811461166c57600080fd5b6001600160a01b0381168114610aa457600080fd5b60006020828403121561187057600080fd5b813561166c81611849565b60006020828403121561188d57600080fd5b81356001600160401b038111156118a357600080fd5b820160a0818503121561166c57600080fd5b6000602082840312156118c757600080fd5b5035919050565b81835260006001600160fb1b038311156118e757600080fd5b8260051b80836020870137939093016020019392505050565b6001600160a01b0384168152604060208201819052600090610c3f90830184866118ce565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b604080519081016001600160401b038111828210171561198157611981611925565b60405290565b60405160a081016001600160401b038111828210171561198157611981611925565b604051601f8201601f191681016001600160401b03811182821017156119d1576119d1611925565b604052919050565b80356001600160401b03811681146119f057600080fd5b919050565b600082601f830112611a0657600080fd5b81356001600160401b03811115611a1f57611a1f611925565b611a32601f8201601f19166020016119a9565b818152846020838601011115611a4757600080fd5b816020850160208301376000918101602001919091529392505050565b60006001600160401b03821115611a7d57611a7d611925565b5060051b60200190565b600082601f830112611a9857600080fd5b81356020611aad611aa883611a64565b6119a9565b82815260069290921b84018101918181019086841115611acc57600080fd5b8286015b84811015611b145760408189031215611ae95760008081fd5b611af161195f565b8135611afc81611849565b81528185013585820152835291830191604001611ad0565b509695505050505050565b600060a08236031215611b3157600080fd5b611b39611987565b82358152611b49602084016119d9565b602082015260408301356001600160401b0380821115611b6857600080fd5b611b74368387016119f5565b60408401526060850135915080821115611b8d57600080fd5b611b99368387016119f5565b60608401526080850135915080821115611bb257600080fd5b50611bbf36828601611a87565b60808301525092915050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062e5761062e611bcb565b6001600160401b0383168152604060208201526000611c16604083018461174f565b949350505050565b600060208284031215611c3057600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b8181038181111561062e5761062e611bcb565b600060208284031215611c7257600080fd5b815161166c81611849565b600060018201611c8f57611c8f611bcb565b5060010190565b602081526000611c166020830184866118ce565b60008060408385031215611cbd57600080fd5b8251611cc881611849565b809250506020808401516001600160401b03811115611ce657600080fd5b8401601f81018613611cf757600080fd5b8051611d05611aa882611a64565b81815260059190911b82018301908381019088831115611d2457600080fd5b928401925b82841015611d4257835182529284019290840190611d29565b80955050505050509250929050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b81811015611d9a57845183529383019391830191600101611d7e565b509097965050505050505056fea2646970667358221220843dbda32337a71189910a4196996096e6829db22655cbb0e1b57da6a904c67a64736f6c63430008130033

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

000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a147600000000000000000000000003b437df93ce6f16b60957fe8d4ba847222a9f8700000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a280000000000000000000000000000000000000000000000000594862ae1802b3d50000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : _router (address): 0xE561d5E02207fb5eB32cca20a699E0d8919a1476
Arg [1] : _admin (address): 0x03B437Df93Ce6f16B60957Fe8d4ba847222A9F87
Arg [2] : _cozyPenguinNft (address): 0x63d48Ed3f50aBA950c17e37CA03356CCd6b6a280
Arg [3] : _destinationChainSelector (uint64): 6433500567565415381
Arg [4] : _travelLock (bool): True

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000e561d5e02207fb5eb32cca20a699e0d8919a1476
Arg [1] : 00000000000000000000000003b437df93ce6f16b60957fe8d4ba847222a9f87
Arg [2] : 00000000000000000000000063d48ed3f50aba950c17e37ca03356ccd6b6a280
Arg [3] : 000000000000000000000000000000000000000000000000594862ae1802b3d5
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000001


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
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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