ETH Price: $3,524.28 (-5.57%)

Contract

0x311BCA70b6cd9f320a68A63B2331d7a3Ca55BdE7
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Migrate Se PSP2t...237111032025-11-02 10:39:5942 hrs ago1762079999IN
Velora: VLR Migrator
0 ETH0.000149190.23044109
Migrate Se PSP1t...237110982025-11-02 10:38:5942 hrs ago1762079939IN
Velora: VLR Migrator
0 ETH0.000015310.13868521
Migrate PS Pto V...237051802025-11-01 14:47:232 days ago1762008443IN
Velora: VLR Migrator
0 ETH0.000009970.08651409
Migrate Se PSP2t...237048532025-11-01 13:41:232 days ago1762004483IN
Velora: VLR Migrator
0 ETH0.000051830.08020477
Migrate PS Pto V...237048052025-11-01 13:31:472 days ago1762003907IN
Velora: VLR Migrator
0 ETH0.000174211.7786424
Migrate PS Pto V...237016562025-11-01 2:58:353 days ago1761965915IN
Velora: VLR Migrator
0 ETH0.00006570.57245683
Migrate Se PSP2t...236998512025-10-31 20:55:233 days ago1761944123IN
Velora: VLR Migrator
0 ETH0.000117260.17747339
Migrate PS Pto V...236940902025-10-31 1:33:354 days ago1761874415IN
Velora: VLR Migrator
0 ETH0.000010780.11022944
Migrate Se PSP2t...236940772025-10-31 1:30:594 days ago1761874259IN
Velora: VLR Migrator
0 ETH0.000086820.13111663
Migrate PS Pto V...236899692025-10-30 11:41:234 days ago1761824483IN
Velora: VLR Migrator
0 ETH0.000153041.33327726
Migrate Se PSP2t...236880732025-10-30 5:19:355 days ago1761801575IN
Velora: VLR Migrator
0 ETH0.000786541.19074377
Migrate Se PSP2t...236860922025-10-29 22:40:355 days ago1761777635IN
Velora: VLR Migrator
0 ETH0.000096650.1459856
Migrate Se PSP2t...236771122025-10-28 16:28:476 days ago1761668927IN
Velora: VLR Migrator
0 ETH0.001542242.33483608
Migrate Se PSP2t...236739752025-10-28 5:55:356 days ago1761630935IN
Velora: VLR Migrator
0 ETH0.000395280.59704761
Migrate Se PSP2t...236708992025-10-27 19:35:117 days ago1761593711IN
Velora: VLR Migrator
0 ETH0.000177610.31937731
Migrate PS Pto V...236690222025-10-27 13:17:117 days ago1761571031IN
Velora: VLR Migrator
0 ETH0.001105019.62785816
Migrate Se PSP2t...236623342025-10-26 14:48:358 days ago1761490115IN
Velora: VLR Migrator
0 ETH0.00121251.83548852
Migrate Se PSP1t...236602452025-10-26 7:48:478 days ago1761464927IN
Velora: VLR Migrator
0 ETH0.000006450.07998311
Migrate PS Pto S...236572462025-10-25 21:46:239 days ago1761428783IN
Velora: VLR Migrator
0.01426795 ETH0.000049420.1738083
Migrate PS Pto V...236552482025-10-25 15:03:599 days ago1761404639IN
Velora: VLR Migrator
0 ETH0.000009410.09613766
Migrate Se PSP2t...236546422025-10-25 13:02:359 days ago1761397355IN
Velora: VLR Migrator
0 ETH0.000099840.18382658
Migrate Se PSP2t...236541822025-10-25 11:29:479 days ago1761391787IN
Velora: VLR Migrator
0 ETH0.000053330.08073504
Migrate Se PSP2t...236532042025-10-25 8:12:359 days ago1761379955IN
Velora: VLR Migrator
0 ETH0.000057350.08662142
Migrate Se PSP2t...236509952025-10-25 0:48:1110 days ago1761353291IN
Velora: VLR Migrator
0 ETH0.000058580.08848154
Migrate Se PSP2t...236474902025-10-24 13:01:2310 days ago1761310883IN
Velora: VLR Migrator
0 ETH0.000184120.27808962
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Deposit VLR And ...237081382025-11-02 0:43:592 days ago1762044239
Velora: VLR Migrator
0.00000673 ETH
Migrate Se PSP1t...237081382025-11-02 0:43:592 days ago1762044239
Velora: VLR Migrator
0.00000673 ETH
Deposit VLR And ...237081312025-11-02 0:42:352 days ago1762044155
Velora: VLR Migrator
0.00002697 ETH
Migrate PS Pto S...237081312025-11-02 0:42:352 days ago1762044155
Velora: VLR Migrator
0.00002697 ETH
Deposit VLR And ...236969242025-10-31 11:05:113 days ago1761908711
Velora: VLR Migrator
0.00000061 ETH
Migrate Se PSP1t...236969242025-10-31 11:05:113 days ago1761908711
Velora: VLR Migrator
0.00000061 ETH
Deposit VLR And ...236572462025-10-25 21:46:239 days ago1761428783
Velora: VLR Migrator
0.01426795 ETH
Deposit VLR And ...236317662025-10-22 8:05:2312 days ago1761120323
Velora: VLR Migrator
0.00065189 ETH
Deposit VLR And ...236136852025-10-19 19:11:5915 days ago1760901119
Velora: VLR Migrator
0.00110396 ETH
Deposit235981742025-10-17 15:07:2317 days ago1760713643
Velora: VLR Migrator
0.00016353 ETH
Deposit VLR And ...235804002025-10-15 3:25:1120 days ago1760498711
Velora: VLR Migrator
0.00075701 ETH
Deposit235602912025-10-12 7:56:2322 days ago1760255783
Velora: VLR Migrator
0.00023827 ETH
Deposit235597872025-10-12 6:15:1122 days ago1760249711
Velora: VLR Migrator
0.00018251 ETH
Deposit VLR And ...235548542025-10-11 13:42:4723 days ago1760190167
Velora: VLR Migrator
0.00001446 ETH
Deposit VLR And ...235548202025-10-11 13:35:5923 days ago1760189759
Velora: VLR Migrator
0.00002165 ETH
Deposit VLR And ...235404112025-10-09 13:15:2325 days ago1760015723
Velora: VLR Migrator
0.00581794 ETH
Deposit VLR And ...235336092025-10-08 14:25:1126 days ago1759933511
Velora: VLR Migrator
0.00235319 ETH
Deposit VLR And ...235150832025-10-06 0:14:3529 days ago1759709675
Velora: VLR Migrator
0.00021724 ETH
Deposit235110012025-10-05 10:34:5929 days ago1759660499
Velora: VLR Migrator
0.00032975 ETH
Deposit VLR And ...235047812025-10-04 13:42:1130 days ago1759585331
Velora: VLR Migrator
0.0001621 ETH
Deposit VLR And ...235047742025-10-04 13:40:4730 days ago1759585247
Velora: VLR Migrator
0.00002273 ETH
Deposit VLR And ...235036612025-10-04 9:56:5930 days ago1759571819
Velora: VLR Migrator
0.00189681 ETH
Deposit VLR And ...235033352025-10-04 8:51:3530 days ago1759567895
Velora: VLR Migrator
0.00001205 ETH
Deposit VLR And ...234545382025-09-27 13:08:2337 days ago1758978503
Velora: VLR Migrator
0.00018964 ETH
Deposit VLR And ...234407882025-09-25 14:55:5939 days ago1758812159
Velora: VLR Migrator
0.0001389 ETH
View All Internal Transactions
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

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

Contract Name:
MiroMigrator

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

// Contracts
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";

// Interfaces
import { IMiroMigrator } from "./interfaces/IMiroMigrator.sol";
import { IAcrossSpokePool } from "./interfaces/IAcrossSpokePool.sol";
import { ISeVLR } from "./interfaces/ISeVLR.sol";
import { ISePSP2 } from "./interfaces/ISePSP2.sol";

// Libraries
import { SafeTransferLib } from "solady/src/utils/SafeTransferLib.sol";
import { ERC20UtilsLib } from "./libraries/ERC20UtilsLib.sol";
import { WethLib } from "./libraries/WethLib.sol";

/// @title Miro Migrator contract
/// @author Laita Labs
/// @notice Migrator for Miro project.
///         Allows to migrate from legacy PSP staking, including sePSP1 and sePSP2
contract MiroMigrator is IMiroMigrator, Ownable, Pausable {
    /*//////////////////////////////////////////////////////////////
                              LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using SafeTransferLib for address;
    using ERC20UtilsLib for address;
    using WethLib for address;

    /*//////////////////////////////////////////////////////////////
                              CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @notice PSP token address
    address public immutable PSP;

    /// @notice VLR token address
    address public immutable VLR;

    /// @notice WETH token address
    address public immutable WETH;

    /// @notice Address to send burned tokens to
    address private constant BURN_ADDR = 0x000000000000000000000000000000000000dEaD;

    /// @notice seVLR staked token address
    address public immutable SE_VLR;

    /// @notice sePSP1 staked token address
    address public immutable SE_PSP1;

    /// @notice sePSP2 staked token address
    address public immutable SE_PSP2;

    /// @notice Across protocol's Spoke Pool address
    IAcrossSpokePool public immutable SPOKE_POOL;

    /// @notice Mapping for configured destination chain ids for cross-chain bridging
    mapping(uint256 chainId => BridgeConfig config) public bridgeConfigs;

    /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /// @param _bridgeConfigs Configuration for destination chain ids for cross-chain bridging
    /// @param sePSP1 sePSP1 staked token address
    /// @param sePSP2 sePSP2 staked token address
    /// @param seVLR seVLR staked token address
    /// @param spokePool Across protocol's Spoke Pool address
    constructor(
        BridgeConfig[] memory _bridgeConfigs,
        address sePSP1,
        address sePSP2,
        address seVLR,
        IAcrossSpokePool spokePool
    ) Ownable(msg.sender) {
        SE_PSP1 = sePSP1;
        SE_PSP2 = sePSP2;
        SE_VLR = seVLR;

        PSP = ISePSP2(SE_PSP2).PSP();
        VLR = ISeVLR(SE_VLR).VLR();
        WETH = ISeVLR(SE_VLR).WETH();

        SPOKE_POOL = spokePool;

        for (uint256 i = 0; i < _bridgeConfigs.length; i++) {
            bridgeConfigs[_bridgeConfigs[i].destChainId] = _bridgeConfigs[i];
        }

        // pre-approve
        VLR.safeApprove(address(SE_VLR), type(uint256).max);
        WETH.safeApprove(address(SE_VLR), type(uint256).max);
    }

    /*//////////////////////////////////////////////////////////////
                              MIGRATE
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMiroMigrator
    function migratePSPtoVLR(uint256 pspAmount, bytes calldata permitData) external whenNotPaused {
        PSP.permit(permitData);

        PSP.safeTransferFrom(msg.sender, BURN_ADDR, pspAmount);
        VLR.safeTransfer(msg.sender, pspAmount);

        emit Migrated(msg.sender, pspAmount);
    }

    /// @inheritdoc IMiroMigrator
    function migrateSePSP1toVLR(uint256 sePSP1Amount, bytes calldata permitData) external whenNotPaused {
        SE_PSP1.permit(permitData);

        SE_PSP1.safeTransferFrom(msg.sender, BURN_ADDR, sePSP1Amount);
        VLR.safeTransfer(msg.sender, sePSP1Amount);

        emit Migrated(msg.sender, sePSP1Amount);
    }

    /// @inheritdoc IMiroMigrator
    function migratePSPtoSeVLR(
        uint256 pspAmount,
        uint256 wethAmount,
        uint256 minBptOut,
        bytes calldata permitData
    ) external payable whenNotPaused {
        PSP.permit(permitData);

        PSP.safeTransferFrom(msg.sender, BURN_ADDR, pspAmount);

        if (msg.value > 0) {
            if (msg.value != wethAmount) {
                revert InsufficientMsgValue();
            }
            ISeVLR(SE_VLR).depositVLRAndEth{ value: msg.value }(pspAmount, minBptOut, msg.sender, "");
        } else {
            WETH.safeTransferFrom(msg.sender, address(this), wethAmount);
            ISeVLR(SE_VLR).depositVLRAndWeth(pspAmount, wethAmount, minBptOut, msg.sender, "");
        }

        emit Migrated(msg.sender, pspAmount);
    }

    /// @inheritdoc IMiroMigrator
    function migrateSePSP1toSeVLR(
        uint256 sePSP1Amount,
        uint256 wethAmount,
        uint256 minBptOut,
        bytes calldata permitData
    ) external payable whenNotPaused {
        SE_PSP1.permit(permitData);

        SE_PSP1.safeTransferFrom(msg.sender, BURN_ADDR, sePSP1Amount);

        if (msg.value > 0) {
            if (msg.value != wethAmount) {
                revert InsufficientMsgValue();
            }
            ISeVLR(SE_VLR).depositVLRAndEth{ value: msg.value }(sePSP1Amount, minBptOut, msg.sender, "");
        } else {
            WETH.safeTransferFrom(msg.sender, address(this), wethAmount);
            ISeVLR(SE_VLR).depositVLRAndWeth(sePSP1Amount, wethAmount, minBptOut, msg.sender, "");
        }

        emit Migrated(msg.sender, sePSP1Amount);
    }

    /// @inheritdoc IMiroMigrator
    function migrateSePSP2toSeVLR(
        uint256 sePSP2Amount,
        uint256 minPspAmount,
        uint256 minWethAmount,
        uint256 minBptOut,
        bytes calldata permitData
    ) external whenNotPaused {
        SE_PSP2.permit(permitData);

        // Unstake sePSP2
        (uint256 pspAmount, uint256 wethAmount) = _unstakeSePSP2(sePSP2Amount, minPspAmount, minWethAmount);

        // Burn PSP
        PSP.safeTransfer(BURN_ADDR, pspAmount);

        // Deposit VLR + WETH -> seVLR with msg.sender as beneficiary, PSP to VLR is 1:1
        ISeVLR(SE_VLR).depositVLRAndWeth(pspAmount, wethAmount, minBptOut, msg.sender, "");

        emit Migrated(msg.sender, pspAmount);
    }

    /*//////////////////////////////////////////////////////////////
                            BRIDGE
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMiroMigrator
    function bridgeVLRAndStake(
        uint256 vlrAmount,
        uint256 wethAmount,
        uint256 destChainId,
        uint256 minDestBptAmount,
        address beneficiary,
        BridgeData calldata vlrBridgeData,
        BridgeData calldata wethBridgeData,
        bytes calldata permitData
    ) external payable whenNotPaused {
        // Make sure destination chain is supported
        if (bridgeConfigs[destChainId].destChainId == 0) {
            revert InvalidDestinationChainId();
        }

        if (beneficiary == address(0)) {
            beneficiary = msg.sender;
        }

        VLR.permit(permitData);
        VLR.safeTransferFrom(msg.sender, address(this), vlrAmount);

        if (msg.value > 0) {
            if (msg.value != wethAmount) {
                revert InsufficientMsgValue();
            }
            WETH.deposit();
        } else {
            WETH.safeTransferFrom(msg.sender, address(this), wethAmount);
        }

        // Get ID for the bridge message
        uint256 id = ISeVLR(SE_VLR).useNextId(beneficiary);

        // Encode the message
        bytes memory message = _encodeBridgeMessage(
            id,
            vlrAmount - vlrBridgeData.relayerFee,
            wethAmount - wethBridgeData.relayerFee,
            minDestBptAmount,
            beneficiary
        );

        // Bridge VLR
        _bridgeAcross(vlrAmount, false, destChainId, vlrBridgeData, message);
        // Bridge WETH
        _bridgeAcross(wethAmount, true, destChainId, wethBridgeData, message);

        emit StakeBridgingInitiated(beneficiary, vlrAmount, wethAmount, destChainId, message);
    }

    /// @inheritdoc IMiroMigrator
    function migrateSePSP2toSeVLRAndBridge(
        uint256 sePSP2Amount,
        uint256 minPspAmount,
        uint256 minWethAmount,
        uint256 destChainId,
        uint256 minDestBptAmount,
        address beneficiary,
        BridgeData calldata vlrBridgeData,
        BridgeData calldata wethBridgeData,
        bytes calldata permitData
    ) external whenNotPaused {
        // Make sure destination chain is supported
        if (bridgeConfigs[destChainId].destChainId == 0) {
            revert InvalidDestinationChainId();
        }

        if (beneficiary == address(0)) {
            beneficiary = msg.sender;
        }

        SE_PSP2.permit(permitData);

        (uint256 vlrAmount, uint256 wethAmount) = _unstakeSePSP2(sePSP2Amount, minPspAmount, minWethAmount);

        // Get ID for the bridge message
        uint256 id = ISeVLR(SE_VLR).useNextId(beneficiary);

        // Burn PSP
        PSP.safeTransfer(BURN_ADDR, vlrAmount);

        // Emit Migrated event
        emit Migrated(beneficiary, vlrAmount);

        // Encode the message
        bytes memory message = _encodeBridgeMessage(
            id,
            vlrAmount - vlrBridgeData.relayerFee,
            wethAmount - wethBridgeData.relayerFee,
            minDestBptAmount,
            beneficiary
        );

        // Bridge VLR
        _bridgeAcross(vlrAmount, false, destChainId, vlrBridgeData, message);
        // Bridge WETH
        _bridgeAcross(wethAmount, true, destChainId, wethBridgeData, message);

        emit StakeBridgingInitiated(beneficiary, vlrAmount, wethAmount, destChainId, message);
    }

    /*//////////////////////////////////////////////////////////////
                                RECLAIM
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMiroMigrator
    function reClaimVLR(address to) external onlyOwner {
        VLR.safeTransfer(to, VLR.getBalance());
    }

    /*//////////////////////////////////////////////////////////////
                                ADMIN
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMiroMigrator
    function setBridgeConfigs(BridgeConfig[] calldata _bridgeConfigs) external onlyOwner {
        for (uint256 i = 0; i < _bridgeConfigs.length; i++) {
            bridgeConfigs[_bridgeConfigs[i].destChainId] = _bridgeConfigs[i];
        }
    }

    /// @inheritdoc IMiroMigrator
    function pause() external onlyOwner {
        _pause();
    }

    /// @inheritdoc IMiroMigrator
    function unpause() external onlyOwner {
        _unpause();
    }

    /*//////////////////////////////////////////////////////////////
                            INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @notice Internal function to unstake sePSP2
    /// @param sePSP2Amount sePSP2 amount to unstake
    /// @return (pspAmount, wethAmount) Withdrawal request id with received PSP and WETH amounts
    function _unstakeSePSP2(
        uint256 sePSP2Amount,
        uint256 minPspAmount,
        uint256 minWethAmount
    ) internal returns (uint256, uint256) {
        SE_PSP2.safeTransferFrom(msg.sender, address(this), sePSP2Amount);

        // The following withdraw request will have this id
        int256 withdrawRequestId = ISePSP2(SE_PSP2).userVsNextID(address(this));

        // Unstake sePSP2
        ISePSP2(SE_PSP2).requestWithdraw(sePSP2Amount);

        // Withdraw sePSP2 -> PSP + WETH
        ISePSP2(SE_PSP2).withdrawPSPAndWeth(withdrawRequestId, minPspAmount, minWethAmount);

        // Just withdrawn
        uint256 wethAmount = WETH.getBalance();
        uint256 pspAmount = PSP.getBalance();

        return (pspAmount, wethAmount);
    }

    /// @notice Internal function to encode bridge staking message
    /// @param id Staking ID
    /// @param vlrAmount Amount of VLR to bridge and stake
    /// @param wethAmount Amount of WETH to bridge and stake
    /// @param minDestBptAmount Minimum amount of BPT token on dest chain to receive
    /// @param beneficiary Staking beneficiary
    /// @return message Encoded data for BridgeStaking contract, including chain ID
    function _encodeBridgeMessage(
        uint256 id,
        uint256 vlrAmount,
        uint256 wethAmount,
        uint256 minDestBptAmount,
        address beneficiary
    ) internal view returns (bytes memory message) {
        return abi.encode(id, block.chainid, vlrAmount, wethAmount, minDestBptAmount, beneficiary);
    }

    /// @notice Internal function to deposit funds to Across Spoke Pool during cross-chain bridging
    /// @param amount Token amount to deposit
    /// @param isWeth Indicates if deposited token is WETH or VLR
    /// @param destChainId Destination chain id for the deposit
    /// @param bridgeData Struct containing data for performing cross-chain bridging
    /// @param message Encoded data for BridgeStaking contract
    function _bridgeAcross(
        uint256 amount,
        bool isWeth,
        uint256 destChainId,
        BridgeData memory bridgeData,
        bytes memory message
    ) internal {
        if (isWeth) {
            WETH.safeApprove(address(SPOKE_POOL), amount);
        } else {
            VLR.safeApprove(address(SPOKE_POOL), amount);
        }

        SPOKE_POOL.depositV3(
            msg.sender,
            bridgeConfigs[destChainId].bridgeStaking,
            isWeth ? address(WETH) : address(VLR),
            isWeth ? bridgeConfigs[destChainId].weth : bridgeConfigs[destChainId].vlr,
            amount,
            amount - bridgeData.relayerFee,
            destChainId,
            bridgeData.exclusiveRelayer,
            bridgeData.quoteTimestamp,
            bridgeData.fillDeadline,
            bridgeData.exclusivityDeadline,
            message
        );
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 6 of 13 : IAcrossSpokePool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @notice Interface for Across SpokePool contracts to perform bridge
interface IAcrossSpokePool {
    /*//////////////////////////////////////////////////////////////
                                DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /// @notice Deposits tokens into the pool
    /// @dev This function allows to deposit tokens on origin chain and receive tokens on destination chain
    /// @param depositor The address of the depositor
    /// @param recipient The address of the recipient
    /// @param inputToken The address of the input token
    /// @param outputToken The address of the output token
    /// @param inputAmount The amount of input tokens to deposit
    /// @param outputAmount The amount of output tokens to receive
    /// @param destinationChainId The ID of the destination chain
    /// @param exclusiveRelayer The address of the exclusive relayer
    /// @param quoteTimestamp The timestamp of the quote
    /// @param fillDeadline The deadline for filling the deposit
    /// @param exclusivityDeadline The deadline for exclusivity
    /// @param message Additional message or data
    function depositV3(
        address depositor,
        address recipient,
        address inputToken,
        address outputToken,
        uint256 inputAmount,
        uint256 outputAmount,
        uint256 destinationChainId,
        address exclusiveRelayer,
        uint32 quoteTimestamp,
        uint32 fillDeadline,
        uint32 exclusivityDeadline,
        bytes calldata message
    ) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @notice Interface for Miro Project Migrator contract
/// @author Laita Labs
interface IMiroMigrator {
    /*//////////////////////////////////////////////////////////////
                             TYPES
    //////////////////////////////////////////////////////////////*/

    /// @notice Struct containing configuration for destination chain for cross-chain bridging
    struct BridgeConfig {
        address vlr;
        address weth;
        address bridgeStaking;
        uint256 destChainId;
    }

    /// @notice Struct containing data for performing cross-chain bridging
    struct BridgeData {
        /// @dev The fee charged by the relayer for this transaction
        uint256 relayerFee;
        /// @dev The address of the relayer that has exclusive rights to process this transaction
        address exclusiveRelayer;
        /// @dev The timestamp when the quote was provided
        uint32 quoteTimestamp;
        /// @dev The deadline by which the transaction must be filled
        uint32 fillDeadline;
        /// @dev The deadline until which the relayer has exclusive rights to process the transaction
        uint32 exclusivityDeadline;
    }

    /*//////////////////////////////////////////////////////////////
                             ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Insufficient msg.value for the operation
    error InsufficientMsgValue();

    /// @notice Given destination chain is not in config
    error InvalidDestinationChainId();

    /*//////////////////////////////////////////////////////////////
                             EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice PSP to VLR migration completed
    /// @param user Beneficiary of the migration
    /// @param amount Amount of PSP tokens migrated to VLR
    event Migrated(address indexed user, uint256 indexed amount);

    /// @notice Stake bridging has been initiated with deposits to Across Pool
    /// @param user Beneficiary of the staking
    /// @param vlrAmount VLR amount to bridge & stake
    /// @param wethAmount WETH amount to bridge & stake
    /// @param destChainId Destination chain
    /// @param message Encoded message for `handleAcrossMessage`
    event StakeBridgingInitiated(
        address indexed user,
        uint256 indexed vlrAmount,
        uint256 indexed wethAmount,
        uint256 destChainId,
        bytes message
    );

    /*//////////////////////////////////////////////////////////////
                              MIGRATE
    //////////////////////////////////////////////////////////////*/

    /// @notice Migrates PSP to VLR 1:1
    /// @param pspAmount Amount of PSP to migrate
    /// @param permitData Optional encoded PSP permit data
    function migratePSPtoVLR(uint256 pspAmount, bytes calldata permitData) external;

    /// @notice Migrates sePSP1 to VLR 1:1
    /// @param sePSP1Amount Amount of sePSP1 to migrate
    /// @param permitData Optional encoded sePSP1 permit data
    function migrateSePSP1toVLR(uint256 sePSP1Amount, bytes calldata permitData) external;

    /// @notice Migrates PSP to VLR and deposits ETH/WETH to seVLR
    /// @param pspAmount Amount of PSP to migrate
    /// @param wethAmount Amount of ETH/WETH to deposit
    /// @param minBptOut Minimum received amount of seVLR
    /// @param permitData Optional encoded PSP permit data
    function migratePSPtoSeVLR(
        uint256 pspAmount,
        uint256 wethAmount,
        uint256 minBptOut,
        bytes calldata permitData
    ) external payable;

    /// @notice Migrates sePSP1 to VLR and deposits ETH/WETH to seVLR
    /// @param sePSP1Amount Amount of sePSP1 to migrate
    /// @param wethAmount Amount of ETH/WETH to deposit
    /// @param minBptOut Minimum received amount of seVLR
    /// @param permitData Optional encoded PSP permit data
    function migrateSePSP1toSeVLR(
        uint256 sePSP1Amount,
        uint256 wethAmount,
        uint256 minBptOut,
        bytes calldata permitData
    ) external payable;

    /// @notice Migrates sePSP2 to seVLR
    /// @param sePSP2Amount Amount of sePSP2 to migrate
    /// @param minPspAmount Minimum PSP amount to unstake
    /// @param minWethAmount Minimum WETH amount to unstake
    /// @param minBptOut Minimum received amount of seVLR
    /// @param permitData Optional encoded sePSP2 permit data
    function migrateSePSP2toSeVLR(
        uint256 sePSP2Amount,
        uint256 minPspAmount,
        uint256 minWethAmount,
        uint256 minBptOut,
        bytes calldata permitData
    ) external;

    /*//////////////////////////////////////////////////////////////
                          BRIDGE
    //////////////////////////////////////////////////////////////*/

    /// @notice Bridges both VLR and WETH and then stakes to seVLR on another supported chain
    /// @param vlrAmount The amount of VLR tokens to stake
    /// @param wethAmount The amount of WETH tokens to bridge
    /// @param destChainId Destination chain
    /// @param minDestBptAmount Minimum amount of BPT token on dest chain to receive
    /// @param beneficiary Address to receive seVLR on destination chain
    /// @param vlrBridgeData Struct containing data for performing cross-chain bridging of VLR using Across Protocol
    /// @param wethBridgeData Struct containing data for performing cross-chain bridging of WETH using Across Protocol
    /// @param permitData Optional encoded VLR permit data
    function bridgeVLRAndStake(
        uint256 vlrAmount,
        uint256 wethAmount,
        uint256 destChainId,
        uint256 minDestBptAmount,
        address beneficiary,
        BridgeData calldata vlrBridgeData,
        BridgeData calldata wethBridgeData,
        bytes calldata permitData
    ) external payable;

    /// @notice Migrates sePSP2 to seVLR and bridges seVLR to another supported chain
    /// @param sePSP2Amount Amount of sePSP2 to migrate
    /// @param minPspAmount Minimum PSP amount to unstake
    /// @param minWethAmount Minimum WETH amount to unstake
    /// @param destChainId Destination chain to receive seVLR on
    /// @param minDestBptAmount Minimum amount of BPT token on dest chain to receive
    /// @param beneficiary Address to receive seVLR on destination chain
    /// @param vlrBridgeData Struct containing data for performing cross-chain bridging of VLR using Across Protocol
    /// @param wethBridgeData Struct containing data for performing cross-chain bridging of WETH using Across Protocol
    /// @param permitData Optional encoded sePSP permit data
    function migrateSePSP2toSeVLRAndBridge(
        uint256 sePSP2Amount,
        uint256 minPspAmount,
        uint256 minWethAmount,
        uint256 destChainId,
        uint256 minDestBptAmount,
        address beneficiary,
        BridgeData calldata vlrBridgeData,
        BridgeData calldata wethBridgeData,
        bytes calldata permitData
    ) external;

    /*//////////////////////////////////////////////////////////////
                              RECLAIM
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows owner to send contract's seVLR balance to a given address
    ///         For emergency use only
    function reClaimVLR(address to) external;

    /*//////////////////////////////////////////////////////////////
                              ADMIN
    //////////////////////////////////////////////////////////////*/

    /// @notice Sets bridge configurations for cross-chain stake bridging
    function setBridgeConfigs(BridgeConfig[] calldata _bridgeConfigs) external;

    /// @notice Pauses migration operations
    function pause() external;

    /// @notice Unpauses migration operations
    function unpause() external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title Interface for sePSP2 Staking Token
interface ISePSP2 is IERC20 {
    /*//////////////////////////////////////////////////////////////
                              VIEW
    //////////////////////////////////////////////////////////////*/

    /// @notice PSP token address
    function PSP() external view returns (address);

    /// @notice WETH token address
    function WETH() external view returns (address);

    /// @notice Mapping to index user's withdrawal request ids. Meant to only be incremented by 1 for each new request
    function userVsNextID(address owner) external returns (int256);

    /*//////////////////////////////////////////////////////////////
                              DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /// @notice Deposits PSP and ETH to join 20WETH-80PSP liquidity pool and receive sePSP2
    /// @param pspAmount PSP token amount to deposit
    /// @param minBptOut Minimum liquidity tokens to receive
    /// @param pspPermit Optional PSP encoded permit data
    function depositPSPAndEth(uint256 pspAmount, uint256 minBptOut, bytes memory pspPermit) external payable;

    /// @notice Deposits PSP and ETH to join 20WETH-80PSP liquidity pool and receive sePSP2
    /// @param pspAmount PSP token amount to deposit
    /// @param wethAmount WETH token amount to deposit
    /// @param minBptOut Minimum liquidity tokens to receive
    /// @param pspPermit Optional PSP encoded permit data
    function depositPSPAndWeth(
        uint256 pspAmount,
        uint256 wethAmount,
        uint256 minBptOut,
        bytes memory pspPermit
    ) external;

    /*//////////////////////////////////////////////////////////////
                         WITHDRAW
    //////////////////////////////////////////////////////////////*/

    /// @notice Creates a withdrawal request for the given amount of asset tokens
    /// @param amount The amount to withdraw
    function requestWithdraw(uint256 amount) external;

    /// @notice Withdraws PSP and WETH requested by given withdrawal request id
    /// @param id Withdrawal request id
    /// @param minPspAmount Minimum PSP token amount to receive
    /// @param minWethAmount Minimum WETH amount to receive
    function withdrawPSPAndWeth(int256 id, uint256 minPspAmount, uint256 minWethAmount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { ITimeLockedERC20 } from "../token/interfaces/ITimeLockedERC20.sol";

/// @title Interface for seVLR Staking Token
/// @author Laita Labs
interface ISeVLR is ITimeLockedERC20 {
    /*//////////////////////////////////////////////////////////////
                            VIEW
    //////////////////////////////////////////////////////////////*/

    /// @notice VLR token address
    function VLR() external view returns (address);

    /// @notice WETH token address
    function WETH() external view returns (address);

    /*//////////////////////////////////////////////////////////////
                            DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /// @notice Deposits VLR and ETH to join 20WETH-80VLR liquidity pool and receive seVLR
    /// @param vlrAmount VLR token amount to deposit
    /// @param minBptOut Minimum liquidity tokens to receive
    /// @param beneficiary Beneficiary of the deposit
    /// @param vlrPermit Optional VLR encoded permit data
    function depositVLRAndEth(
        uint256 vlrAmount,
        uint256 minBptOut,
        address beneficiary,
        bytes memory vlrPermit
    ) external payable;

    /// @notice Deposits VLR and WETH to join 20WETH-80VLR liquidity pool and receive seVLR
    /// @param vlrAmount VLR token amount to deposit
    /// @param wethAmount WETH token amount to deposit
    /// @param minBptOut Minimum liquidity tokens to receive
    /// @param beneficiary Beneficiary of the deposit
    /// @param vlrPermit Optional VLR encoded permit data
    function depositVLRAndWeth(
        uint256 vlrAmount,
        uint256 wethAmount,
        uint256 minBptOut,
        address beneficiary,
        bytes memory vlrPermit
    ) external;

    /*//////////////////////////////////////////////////////////////
                            WITHDRAW
    //////////////////////////////////////////////////////////////*/

    /// @notice Withdraws VLR and ETH requested by given withdrawal request id
    /// @param id Withdrawal request id
    /// @param minVlrAmount Minimum VLR token amount to receive
    /// @param minEthAmount Minimum ETH amount to receive
    /// @return (vlrAmount, ethAmount) Amounts withdrawn
    function withdrawVLRAndEth(
        uint256 id,
        uint256 minVlrAmount,
        uint256 minEthAmount
    ) external returns (uint256, uint256);

    /// @notice Withdraws VLR and ETH requested by multiple withdrawal request id
    /// @param ids Withdrawal request ids
    /// @param minVlrAmount Minimum VLR token amount to receive
    /// @param minEthAmount Minimum ETH amount to receive
    /// @return (vlrAmount, ethAmount) Amounts withdrawn
    function withdrawVLRAndEthMulti(
        uint256[] calldata ids,
        uint256 minVlrAmount,
        uint256 minEthAmount
    ) external returns (uint256, uint256);

    /// @notice Withdraws VLR and WETH requested by given withdrawal request id
    /// @param id Withdrawal request id
    /// @param minVlrAmount Minimum VLR token amount to receive
    /// @param minWethAmount Minimum WETH amount to receive
    /// @return (vlrAmount, wethAmount) Amounts withdrawn
    function withdrawVLRAndWeth(
        uint256 id,
        uint256 minVlrAmount,
        uint256 minWethAmount
    ) external returns (uint256, uint256);

    /// @notice Withdraws VLR and WETH requested by multiple withdrawal request id
    /// @param ids Withdrawal request ids
    /// @param minVlrAmount Minimum VLR token amount to receive
    /// @param minWethAmount Minimum ETH amount to receive
    /// @return (vlrAmount, wethAmount) Amounts withdrawn
    function withdrawVLRAndWethMulti(
        uint256[] calldata ids,
        uint256 minVlrAmount,
        uint256 minWethAmount
    ) external returns (uint256, uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @title ERC20 Utility Library
library ERC20UtilsLib {
    /*//////////////////////////////////////////////////////////////
                               ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when the permit execution fails
    error PermitFailed();

    /*//////////////////////////////////////////////////////////////
                           CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev An address used to represent the native token
    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /*//////////////////////////////////////////////////////////////
                               PERMIT
    //////////////////////////////////////////////////////////////*/

    /// @dev Executes the EIP2612 permit function on the provided token
    /// @param token The address of the token
    /// @param data The permit data
    function permit(address token, bytes calldata data) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            // check the permit length
            switch data.length
            // 0x00 = no permit
            // solhint-disable-next-line no-empty-blocks
            case 0x00 {
                // do nothing
            }
            // 32 * 7 = 224 EIP2612 Permit
            case 0xe0 {
                let x := mload(0x40) // get the free memory pointer
                mstore(x, 0xd505accf00000000000000000000000000000000000000000000000000000000) // store the selector
                calldatacopy(add(x, 0x04), data.offset, 0xe0) // store the args
                pop(call(gas(), token, 0x00, x, 0xe4, 0x00, 0x20)) // call ERC20 permit, skip checking return data
            }
            // Otherwise revert
            default {
                mstore(0x00, 0xb78cb0dd00000000000000000000000000000000000000000000000000000000) // store the selector
                revert(0x00, 0x04) // Revert with PermitFailed error
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                            BALANCE
    //////////////////////////////////////////////////////////////*/

    /// @dev Returns the balance of address(this), works for both ETH and ERC20 tokens
    function getBalance(address token) internal view returns (uint256 balanceOf) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            switch eq(token, ETH_ADDRESS)
            // ETH
            case 0x01 {
                balanceOf := selfbalance()
            }
            // ERC20
            default {
                let x := mload(0x40) // get the free memory pointer
                mstore(x, 0x70a0823100000000000000000000000000000000000000000000000000000000) // store the selector
                mstore(add(x, 0x04), address()) // store the account
                let success := staticcall(gas(), token, x, 0x24, x, 0x20) // call balanceOf
                if success {
                    balanceOf := mload(x)
                } // load the balance
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @title Weth Library
library WethLib {
    /*//////////////////////////////////////////////////////////////
                               DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /// @dev Executes deposit of WETH with the msg.value
    function deposit(address weth) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let x := mload(0x40) // get the free memory pointer
            // Prepare call data for WETH.deposit()

            // Store function selector and
            mstore(x, 0xd0e30db000000000000000000000000000000000000000000000000000000000) // deposit()

            // Perform the external call with the prepared calldata
            // Check the outcome of the call and handle failure
            if iszero(call(gas(), weth, callvalue(), x, 4, 0, 0)) {
                // The call failed; we retrieve the exact error message and revert with it
                returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                revert(0, returndatasize()) // Revert with the error message
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @title TimeLocked ERC20 Interface
/// @author Laita Labs
/// @notice Interface for the TimeLocked ERC20, which allows time locking erc20 tokens
interface ITimeLockedERC20 {
    /*//////////////////////////////////////////////////////////////
                              TYPES
    //////////////////////////////////////////////////////////////*/

    /// @notice Represents different stages of asset token withdrawal
    enum WithdrawStatus {
        /// @dev Default value. Withdrawal was never requested
        UNUSED,
        /// @dev Withdrawal was requested. Asset tokens can be withdrawn when lock duration passes
        UNLOCKING,
        /// @dev Asset tokens were withdrawn
        RELEASED,
        /// @dev Withdrawal was cancelled
        CANCELLED
    }

    /// @notice Withdrawal request data structure containing request details
    struct WithdrawalRequest {
        /// @dev Requested asset token amount
        uint256 amount;
        /// @dev Unix timestamp indicating when the requested amount can be withdrawn
        uint256 releaseTime;
        /// @dev Status of the request. Only requests with `UNLOCKING` status can be withdrawn
        WithdrawStatus status;
    }

    /*//////////////////////////////////////////////////////////////
                              ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Time lock is out of set limits
    error TimelockOutOfRange(uint256 attemptedTimelockDuration);

    /// @notice Trying to cancel request which is not pending
    error CannotCancelWithdrawalRequest(uint256 reqId);

    /// @notice Trying to withdraw request which is not pending
    error CannotWithdraw(uint256 reqId);

    /// @notice Trying to withdraw request before time lock duration passes
    error CannotWithdrawYet(uint256 reqId);

    /*//////////////////////////////////////////////////////////////
                              EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Time lock duration has been changed
    event TimeLockChanged(uint256 oldTimeLock, uint256 newTimeLock);

    /// @notice Withdraw has been requested
    event RequestedUnlocking(uint256 indexed id, address indexed user, uint256 amount);

    /// @notice Locked tokens has been released
    event Withdraw(uint256 indexed id, address indexed user, uint256 amount);

    /// @notice Tokens has been locked
    event Deposited(address indexed user, uint256 amount);

    /// @notice Withdrawal request has been cancelled
    event CancelledWithdrawalRequest(uint256 indexed id, address indexed user, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                              VIEW
    //////////////////////////////////////////////////////////////*/

    /// @notice Role which can bypass timelock during asset tokens withdrawal
    function MIGRATOR_ROLE() external view returns (bytes32);

    /// @notice Asset ERC20 token to lock
    function ASSET() external view returns (address);

    /// @notice Minimum asset tokens lock duration in seconds
    function MIN_TIME_LOCK_DURATION() external view returns (uint256);

    /// @notice Maximum asset tokens lock duration in seconds
    function MAX_TIME_LOCK_DURATION() external view returns (uint256);

    /// @notice Asset tokens lock duration in seconds. Can be adjusted by admin
    function timeLockDuration() external view returns (uint256);

    /// @notice Total amount of asset tokens currently in `UNLOCKING` state
    function unlockingAssets() external view returns (uint256);

    /// @notice Mapping to store user withdrawal requests by id
    function userVsWithdrawals(address user, uint256 id) external view returns (WithdrawalRequest memory);

    /// @notice Mapping to index user's withdrawal request ids. Meant to only be incremented by 1 for each new request
    function userVsNextID(address user) external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                              DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /// @notice Deposits the amount of asset tokens
    /// @param amount The amount to deposit
    /// @param beneficiary The beneficiary of the deposit
    function deposit(uint256 amount, address beneficiary) external;

    /// @notice Deposits the amount of asset tokens with ERC-2612 Permit
    /// @param amount The amount to deposit
    /// @param beneficiary The beneficiary of the deposit

    /// @param amount Encoded permit data
    function depositWithPermit(uint256 amount, address beneficiary, bytes calldata permit) external;

    /*//////////////////////////////////////////////////////////////
                          WITHDRAWAL REQUEST
    //////////////////////////////////////////////////////////////*/

    /// @notice Creates a withdrawal request for the given amount of asset tokens
    /// @param amount The amount to withdraw
    function requestWithdraw(uint256 amount) external;

    /// @notice Cancels a withdrawal request for the given request id
    /// @param id The request id
    function cancelWithdrawalRequest(uint256 id) external;

    /// @notice Cancels withdrawal requests for multiple given request ids
    /// @param ids The request ids
    function cancelMultipleWithdrawalRequests(uint256[] calldata ids) external;

    /*//////////////////////////////////////////////////////////////
                               WITHDRAW
    //////////////////////////////////////////////////////////////*/

    /// @notice Withdraws an amount of asset tokens which were requested by given withdrawal request id
    /// @param id The request id
    function withdraw(uint256 id) external;

    /// @notice Withdraws an amount of asset tokens which were requested by multiple withdrawal request ids
    /// @param ids The request ids
    function withdrawMultiple(uint256[] calldata ids) external;

    /*//////////////////////////////////////////////////////////////
                            MIGRATOR
    //////////////////////////////////////////////////////////////*/

    /// @notice Increments next user id, intended to only be called by migrator
    /// @param user The user to increment the next id for
    /// @return usedId The user id before the increment
    function useNextId(address user) external returns (uint256 usedId);

    /*//////////////////////////////////////////////////////////////
                            ADMIN
    //////////////////////////////////////////////////////////////*/

    /// @notice Pauses token operations, intended to only be called by admin
    function pause() external;

    /// @notice Unpauses token operations, intended to only be called by admin
    function unpause() external;

    /// @notice Changes time lock duration to the given value, intended to only be called by admin
    /// @param newTimeLockDuration New time lock duration, has to be in set duration limits
    function changeTimeLock(uint256 newTimeLockDuration) external;

    /*//////////////////////////////////////////////////////////////
                            VIEW HELPERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Finds any locked IDs in the specified range, intended for off chain use
    /// @param user User whose requests to look for
    /// @param start Index to start the search with. If negative, the search is performed from the end
    /// @param countToCheck Amount of ids to check in the search
    function findUnlockingIDs(
        address user,
        int256 start,
        uint256 countToCheck
    ) external view returns (uint256[] memory ids);
}

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /// @dev The Permit2 approve operation has failed.
    error Permit2ApproveFailed();

    /// @dev The Permit2 lockdown operation has failed.
    error Permit2LockdownFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(
                and(
                    call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
                    lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
                )
            ) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(
                        add(m, 0x94),
                        lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    )
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `amount != 0` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero( // Revert if token does not have code, or if the call fails.
            mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
    function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let addressMask := shr(96, not(0))
            let m := mload(0x40)
            mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
            mstore(add(m, 0x20), and(addressMask, token))
            mstore(add(m, 0x40), and(addressMask, spender))
            mstore(add(m, 0x60), and(addressMask, amount))
            mstore(add(m, 0x80), and(0xffffffffffff, expiration))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Revokes an approval for `token` and `spender` for `address(this)`.
    function permit2Lockdown(address token, address spender) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0xcc53287f) // `Permit2.lockdown`.
            mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
            mstore(add(m, 0x40), 1) // `approvals.length`.
            mstore(add(m, 0x60), shr(96, shl(96, token)))
            mstore(add(m, 0x80), shr(96, shl(96, spender)))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"components":[{"internalType":"address","name":"vlr","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"bridgeStaking","type":"address"},{"internalType":"uint256","name":"destChainId","type":"uint256"}],"internalType":"struct IMiroMigrator.BridgeConfig[]","name":"_bridgeConfigs","type":"tuple[]"},{"internalType":"address","name":"sePSP1","type":"address"},{"internalType":"address","name":"sePSP2","type":"address"},{"internalType":"address","name":"seVLR","type":"address"},{"internalType":"contract IAcrossSpokePool","name":"spokePool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"InsufficientMsgValue","type":"error"},{"inputs":[],"name":"InvalidDestinationChainId","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Migrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"vlrAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"wethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"destChainId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"}],"name":"StakeBridgingInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"PSP","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SE_PSP1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SE_PSP2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SE_VLR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SPOKE_POOL","outputs":[{"internalType":"contract IAcrossSpokePool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VLR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"bridgeConfigs","outputs":[{"internalType":"address","name":"vlr","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"bridgeStaking","type":"address"},{"internalType":"uint256","name":"destChainId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vlrAmount","type":"uint256"},{"internalType":"uint256","name":"wethAmount","type":"uint256"},{"internalType":"uint256","name":"destChainId","type":"uint256"},{"internalType":"uint256","name":"minDestBptAmount","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"},{"components":[{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"address","name":"exclusiveRelayer","type":"address"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"},{"internalType":"uint32","name":"fillDeadline","type":"uint32"},{"internalType":"uint32","name":"exclusivityDeadline","type":"uint32"}],"internalType":"struct IMiroMigrator.BridgeData","name":"vlrBridgeData","type":"tuple"},{"components":[{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"address","name":"exclusiveRelayer","type":"address"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"},{"internalType":"uint32","name":"fillDeadline","type":"uint32"},{"internalType":"uint32","name":"exclusivityDeadline","type":"uint32"}],"internalType":"struct IMiroMigrator.BridgeData","name":"wethBridgeData","type":"tuple"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"bridgeVLRAndStake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pspAmount","type":"uint256"},{"internalType":"uint256","name":"wethAmount","type":"uint256"},{"internalType":"uint256","name":"minBptOut","type":"uint256"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"migratePSPtoSeVLR","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pspAmount","type":"uint256"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"migratePSPtoVLR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sePSP1Amount","type":"uint256"},{"internalType":"uint256","name":"wethAmount","type":"uint256"},{"internalType":"uint256","name":"minBptOut","type":"uint256"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"migrateSePSP1toSeVLR","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sePSP1Amount","type":"uint256"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"migrateSePSP1toVLR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sePSP2Amount","type":"uint256"},{"internalType":"uint256","name":"minPspAmount","type":"uint256"},{"internalType":"uint256","name":"minWethAmount","type":"uint256"},{"internalType":"uint256","name":"minBptOut","type":"uint256"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"migrateSePSP2toSeVLR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sePSP2Amount","type":"uint256"},{"internalType":"uint256","name":"minPspAmount","type":"uint256"},{"internalType":"uint256","name":"minWethAmount","type":"uint256"},{"internalType":"uint256","name":"destChainId","type":"uint256"},{"internalType":"uint256","name":"minDestBptAmount","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"},{"components":[{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"address","name":"exclusiveRelayer","type":"address"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"},{"internalType":"uint32","name":"fillDeadline","type":"uint32"},{"internalType":"uint32","name":"exclusivityDeadline","type":"uint32"}],"internalType":"struct IMiroMigrator.BridgeData","name":"vlrBridgeData","type":"tuple"},{"components":[{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"address","name":"exclusiveRelayer","type":"address"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"},{"internalType":"uint32","name":"fillDeadline","type":"uint32"},{"internalType":"uint32","name":"exclusivityDeadline","type":"uint32"}],"internalType":"struct IMiroMigrator.BridgeData","name":"wethBridgeData","type":"tuple"},{"internalType":"bytes","name":"permitData","type":"bytes"}],"name":"migrateSePSP2toSeVLRAndBridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"reClaimVLR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"vlr","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"bridgeStaking","type":"address"},{"internalType":"uint256","name":"destChainId","type":"uint256"}],"internalType":"struct IMiroMigrator.BridgeConfig[]","name":"_bridgeConfigs","type":"tuple[]"}],"name":"setBridgeConfigs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x61016060405234801561001157600080fd5b50604051612c98380380612c9883398101604081905261003091610439565b338061005657604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61005f816102fa565b506000805460ff60a01b191690556001600160a01b038085166101005283811661012081905290831660e0526040805163f821902d60e01b8152905163f821902d916004808201926020929091908290030181865afa1580156100c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100ea9190610581565b6001600160a01b03166080816001600160a01b03168152505060e0516001600160a01b031663b045d89e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610143573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101679190610581565b6001600160a01b031660a0816001600160a01b03168152505060e0516001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e49190610581565b6001600160a01b0390811660c05281166101405260005b85518110156102b357858181518110610216576102166105a5565b602002602001015160016000888481518110610234576102346105a5565b6020908102919091018101516060908101518352828201939093526040918201600020845181546001600160a01b03199081166001600160a01b039283161783559286015160018084018054861692841692909217909155938601516002830180549094169116179091559290910151600390920191909155016101fb565b5060e05160a0516102d2916001600160a01b039091169060001961034a565b60e05160c0516102f0916001600160a01b039091169060001961034a565b50505050506105bb565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b81601452806034526f095ea7b300000000000000000000000060005260206000604460106000875af1806001600051141661039857803d853b15171061039857633e3f8f736000526004601cfd5b506000603452505050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156103db576103db6103a3565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610409576104096103a3565b604052919050565b6001600160a01b038116811461042657600080fd5b50565b805161043481610411565b919050565b600080600080600060a0868803121561045157600080fd5b85516001600160401b0381111561046757600080fd5b8601601f8101881361047857600080fd5b80516001600160401b03811115610491576104916103a3565b6104a060208260051b016103e1565b8082825260208201915060208360071b85010192508a8311156104c257600080fd5b6020840193505b82841015610539576080848c0312156104e157600080fd5b6104e96103b9565b84516104f481610411565b8152602085015161050481610411565b6020820152604085015161051781610411565b60408201526060858101519082015282526080909301926020909101906104c9565b975061054b9250505060208701610429565b935061055960408701610429565b925061056760608701610429565b915061057560808701610429565b90509295509295909350565b60006020828403121561059357600080fd5b815161059e81610411565b9392505050565b634e487b7160e01b600052603260045260246000fd5b60805160a05160c05160e05161010051610120516101405161257961071f6000396000818161034301528181611a7d01528181611ae30152611b310152600081816102da01528181610b8101528181610ec30152818161177d015281816117d60152818161188a01526119360152600081816104d5015281816107a0015281816107e10152818161082d015261086e0152600081816101d90152818161092101528181610a2c01528181610c0401528181610f9101526112a00152600081816103ca015281816109b3015281816111d701528181611230015281816119b001528181611a5b0152611b890152600081816103fe0152818161069901528181610703015281816107540152818161112a0152818161116b01528181611ac10152611b6301526000818161055c015281816106140152818161065501528181610c8d01528181610e3601528181610e7701528181610f1701526119f301526125796000f3fe6080604052600436106101805760003560e01c80638021fef7116100d6578063d172f2f01161007f578063f2fde38b11610059578063f2fde38b14610517578063f711222514610537578063f821902d1461054a57600080fd5b8063d172f2f014610420578063e736c6ca146104c3578063ee06090b146104f757600080fd5b80638dad50f2116100b05780638dad50f2146103a5578063ad5c4648146103b8578063b045d89e146103ec57600080fd5b80638021fef7146103315780638456cb59146103655780638da5cb5b1461037a57600080fd5b80633c745a4f116101385780636ca55c25116101125780636ca55c25146102c8578063715018a6146102fc5780637854eb6c1461031157600080fd5b80633c745a4f146102655780633f4ba83a146102785780635c975abb1461028d57600080fd5b806321a32ec31161016957806321a32ec3146101c7578063299a278d1461022557806339b5cd5d1461024557600080fd5b806308c8a89d14610185578063174a81ab146101a7575b600080fd5b34801561019157600080fd5b506101a56101a0366004611dd9565b61057e565b005b3480156101b357600080fd5b506101a56101c2366004611e99565b6105f2565b3480156101d357600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561023157600080fd5b506101a5610240366004611f17565b6106f2565b34801561025157600080fd5b506101a5610260366004611e99565b61077e565b6101a5610273366004611f3b565b61080b565b34801561028457600080fd5b506101a5610ad2565b34801561029957600080fd5b5060005474010000000000000000000000000000000000000000900460ff16604051901515815260200161021c565b3480156102d457600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561030857600080fd5b506101a5610ae4565b34801561031d57600080fd5b506101a561032c366004611fae565b610af6565b34801561033d57600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561037157600080fd5b506101a5610e04565b34801561038657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101fb565b6101a56103b3366004611f3b565b610e14565b3480156103c457600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103f857600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561042c57600080fd5b5061048161043b36600461205d565b6001602081905260009182526040909120805491810154600282015460039092015473ffffffffffffffffffffffffffffffffffffffff93841693918216929091169084565b60405161021c949392919073ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b3480156104cf57600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b34801561050357600080fd5b506101a5610512366004612076565b610ea1565b34801561052357600080fd5b506101a5610532366004611f17565b611039565b6101a56105453660046120e0565b61109f565b34801561055657600080fd5b506101fb7f000000000000000000000000000000000000000000000000000000000000000081565b610586611413565b60005b818110156105ed578282828181106105a3576105a3612185565b905060800201600160008585858181106105bf576105bf612185565b90506080020160600135815260200190815260200160002081816105e391906121b4565b5050600101610589565b505050565b6105fa611466565b61063b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b61067f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163361dead8661153c565b6106c073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633856115a3565b604051839033907f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a3505050565b6106fa611413565b61077b8161073d7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166115fc565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691906115a3565b50565b610786611466565b6107c773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b61067f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163361dead8661153c565b610813611466565b61085473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b61089873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163361dead8861153c565b3415610999578334146108d7576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70f16fd6000000000000000000000000000000000000000000000000000000008152600481018690526024810184905233604482015260806064820152600060848201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370f16fd690349060a4016000604051808303818588803b15801561097b57600080fd5b505af115801561098f573d6000803e3d6000fd5b5050505050610a9e565b6109db73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633308761153c565b6040517fc9fdcb4c00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905233606482015260a06084820152600060a48201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063c9fdcb4c9060c401600060405180830381600087803b158015610a8557600080fd5b505af1158015610a99573d6000803e3d6000fd5b505050505b604051859033907f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a35050505050565b610ada611413565b610ae261166e565b565b610aec611413565b610ae260006116eb565b610afe611466565b6000878152600160205260408120600301549003610b48576040517f90eaaa7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516610b67573394505b610ba873ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b600080610bb68c8c8c611760565b6040517f19f1048f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301529294509092506000917f000000000000000000000000000000000000000000000000000000000000000016906319f1048f906024016020604051808303816000875af1158015610c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7191906122ae565b9050610cb673ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001661dead856115a3565b604051839073ffffffffffffffffffffffffffffffffffffffff8a16907f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a36000610d6e82610d0a8a35876122c7565b610d158a35876122c7565b60408051602081019490945246848201526060840192909252608083015260a082018d905273ffffffffffffffffffffffffffffffffffffffff8c1660c0808401919091528151808403909101815260e0909201905290565b9050610d8c8460008d610d86368d90038d018d61231b565b85611a3b565b610da28360018d610d86368c90038c018c61231b565b82848a73ffffffffffffffffffffffffffffffffffffffff167f782334e555e7f5df3383da5b8d85e76ac57c38b6385a8ee43961e2064c5878958e85604051610dec92919061242f565b60405180910390a45050505050505050505050505050565b610e0c611413565b610ae2611c86565b610e1c611466565b610e5d73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b61089873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163361dead8861153c565b610ea9611466565b610eea73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b600080610ef8888888611760565b9092509050610f4073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001661dead846115a3565b6040517fc9fdcb4c00000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044810186905233606482015260a06084820152600060a48201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063c9fdcb4c9060c401600060405180830381600087803b158015610fea57600080fd5b505af1158015610ffe573d6000803e3d6000fd5b50506040518492503391507f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a35050505050505050565b611041611413565b73ffffffffffffffffffffffffffffffffffffffff8116611096576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b61077b816116eb565b6110a7611466565b60008781526001602052604081206003015490036110f1576040517f90eaaa7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516611110573394505b61115173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683836114bb565b61119373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633308c61153c565b3415611216578734146111d2576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112117f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16611cf5565b611258565b61125873ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633308b61153c565b6040517f19f1048f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906319f1048f906024016020604051808303816000875af11580156112eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130f91906122ae565b905060006113868261132288358e6122c7565b61132d88358e6122c7565b60408051602081019490945246848201526060840192909252608083015260a082018b905273ffffffffffffffffffffffffffffffffffffffff8a1660c0808401919091528151808403909101815260e0909201905290565b905061139e8b60008b610d86368b90038b018b61231b565b6113b48a60018b610d86368a90038a018a61231b565b898b8873ffffffffffffffffffffffffffffffffffffffff167f782334e555e7f5df3383da5b8d85e76ac57c38b6385a8ee43961e2064c5878958c856040516113fe92919061242f565b60405180910390a45050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ae2576040517f118cdaa700000000000000000000000000000000000000000000000000000000815233600482015260240161108d565b60005474010000000000000000000000000000000000000000900460ff1615610ae2576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080156114f45760e081146114f9577fb78cb0dd0000000000000000000000000000000000000000000000000000000060005260046000fd5b611536565b6040517fd505accf00000000000000000000000000000000000000000000000000000000815260e08460048301376020600060e4836000895af150505b50505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af1806001600051141661159457803d873b15171061159457637939f4246000526004601cfd5b50600060605260405250505050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af180600160005114166115f157803d853b1517106115f1576390b8ec186000526004601cfd5b506000603452505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee821460018114611664576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152602081602483875afa801561165d57815193505b5050611668565b4791505b50919050565b611676611d37565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000806117a573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633308861153c565b6040517f138504c40000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063138504c4906024016020604051808303816000875af1158015611834573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185891906122ae565b6040517f745400c9000000000000000000000000000000000000000000000000000000008152600481018890529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063745400c990602401600060405180830381600087803b1580156118e357600080fd5b505af11580156118f7573d6000803e3d6000fd5b50506040517fb0d88d7f0000000000000000000000000000000000000000000000000000000081526004810184905260248101889052604481018790527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16925063b0d88d7f9150606401600060405180830381600087803b15801561199157600080fd5b505af11580156119a5573d6000803e3d6000fd5b5050505060006119ea7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166115fc565b90506000611a2d7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166115fc565b989197509095505050505050565b8315611aa757611aa273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000087611d8b565b611b08565b611b0873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000087611d8b565b60008381526001602052604090206002015473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811691637b9392329133911687611b87577f0000000000000000000000000000000000000000000000000000000000000000611ba9565b7f00000000000000000000000000000000000000000000000000000000000000005b88611bd85760008881526001602052604090205473ffffffffffffffffffffffffffffffffffffffff16611c02565b6000888152600160208190526040909120015473ffffffffffffffffffffffffffffffffffffffff165b87518b90611c1090826122c7565b8a8a602001518b604001518c606001518d608001518d6040518d63ffffffff1660e01b8152600401611c4d9c9b9a99989796959493929190612450565b600060405180830381600087803b158015611c6757600080fd5b505af1158015611c7b573d6000803e3d6000fd5b505050505050505050565b611c8e611466565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586116c13390565b6040517fd0e30db000000000000000000000000000000000000000000000000000000000815260008060048334865af1611d33573d6000803e3d6000fd5b5050565b60005474010000000000000000000000000000000000000000900460ff16610ae2576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81601452806034526f095ea7b300000000000000000000000060005260206000604460106000875af180600160005114166115f157803d853b1517106115f157633e3f8f736000526004601cfd5b60008060208385031215611dec57600080fd5b823567ffffffffffffffff811115611e0357600080fd5b8301601f81018513611e1457600080fd5b803567ffffffffffffffff811115611e2b57600080fd5b8560208260071b8401011115611e4057600080fd5b6020919091019590945092505050565b60008083601f840112611e6257600080fd5b50813567ffffffffffffffff811115611e7a57600080fd5b602083019150836020828501011115611e9257600080fd5b9250929050565b600080600060408486031215611eae57600080fd5b83359250602084013567ffffffffffffffff811115611ecc57600080fd5b611ed886828701611e50565b9497909650939450505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461077b57600080fd5b8035611f1281611ee5565b919050565b600060208284031215611f2957600080fd5b8135611f3481611ee5565b9392505050565b600080600080600060808688031215611f5357600080fd5b853594506020860135935060408601359250606086013567ffffffffffffffff811115611f7f57600080fd5b611f8b88828901611e50565b969995985093965092949392505050565b600060a0828403121561166857600080fd5b6000806000806000806000806000806102208b8d031215611fce57600080fd5b8a35995060208b0135985060408b0135975060608b0135965060808b0135955060a08b0135611ffc81611ee5565b945061200b8c60c08d01611f9c565b935061201b8c6101608d01611f9c565b92506102008b013567ffffffffffffffff81111561203857600080fd5b6120448d828e01611e50565b915080935050809150509295989b9194979a5092959850565b60006020828403121561206f57600080fd5b5035919050565b60008060008060008060a0878903121561208f57600080fd5b86359550602087013594506040870135935060608701359250608087013567ffffffffffffffff8111156120c257600080fd5b6120ce89828a01611e50565b979a9699509497509295939492505050565b60008060008060008060008060006102008a8c0312156120ff57600080fd5b8935985060208a0135975060408a0135965060608a0135955060808a013561212681611ee5565b94506121358b60a08c01611f9c565b93506121458b6101408c01611f9c565b92506101e08a013567ffffffffffffffff81111561216257600080fd5b61216e8c828d01611e50565b915080935050809150509295985092959850929598565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81356121bf81611ee5565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617825550602082013561220c81611ee5565b6001820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905550604082013561225d81611ee5565b6002820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905550606082013560038201555050565b6000602082840312156122c057600080fd5b5051919050565b81810381811115612301577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b803563ffffffff81168114611f1257600080fd5b600060a082840312801561232e57600080fd5b5060405160a0810167ffffffffffffffff81118282101715612379577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528235815261238c60208401611f07565b602082015261239d60408401612307565b60408201526123ae60608401612307565b60608201526123bf60808401612307565b60808201529392505050565b6000815180845260005b818110156123f1576020818501810151868301820152016123d5565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b82815260406020820152600061244860408301846123cb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8d16815273ffffffffffffffffffffffffffffffffffffffff8c16602082015273ffffffffffffffffffffffffffffffffffffffff8b16604082015273ffffffffffffffffffffffffffffffffffffffff8a1660608201528860808201528760a08201528660c08201526124f160e082018773ffffffffffffffffffffffffffffffffffffffff169052565b63ffffffff851661010082015263ffffffff841661012082015263ffffffff831661014082015261018061016082015260006125316101808301846123cb565b9e9d505050505050505050505050505056fea2646970667358221220607e101d4c86a22588df957bbeeb78be6a5d561319c107e83acea78d1592c09c64736f6c634300081c003300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000716fbc68e0c761684d9280484243ff094cc5ffab000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c48500000000000000000000000040000320d200c110100638040f10500c8f0010b90000000000000000000000005c7bcd6e7de5423a257d81b442095a1a6ced35c500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c74000000000000000000000000420000000000000000000000000000000000000600000000000000000000000017983b9c5e8905f38a7ea0d0f34f4ee5f34f14dc00000000000000000000000000000000000000000000000000000000000021050000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c7400000000000000000000000042000000000000000000000000000000000000060000000000000000000000001230de36a047abeb36fe0e07f89305a73e74d22d000000000000000000000000000000000000000000000000000000000000000a

Deployed Bytecode

0x6080604052600436106101805760003560e01c80638021fef7116100d6578063d172f2f01161007f578063f2fde38b11610059578063f2fde38b14610517578063f711222514610537578063f821902d1461054a57600080fd5b8063d172f2f014610420578063e736c6ca146104c3578063ee06090b146104f757600080fd5b80638dad50f2116100b05780638dad50f2146103a5578063ad5c4648146103b8578063b045d89e146103ec57600080fd5b80638021fef7146103315780638456cb59146103655780638da5cb5b1461037a57600080fd5b80633c745a4f116101385780636ca55c25116101125780636ca55c25146102c8578063715018a6146102fc5780637854eb6c1461031157600080fd5b80633c745a4f146102655780633f4ba83a146102785780635c975abb1461028d57600080fd5b806321a32ec31161016957806321a32ec3146101c7578063299a278d1461022557806339b5cd5d1461024557600080fd5b806308c8a89d14610185578063174a81ab146101a7575b600080fd5b34801561019157600080fd5b506101a56101a0366004611dd9565b61057e565b005b3480156101b357600080fd5b506101a56101c2366004611e99565b6105f2565b3480156101d357600080fd5b506101fb7f00000000000000000000000040000320d200c110100638040f10500c8f0010b981565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561023157600080fd5b506101a5610240366004611f17565b6106f2565b34801561025157600080fd5b506101a5610260366004611e99565b61077e565b6101a5610273366004611f3b565b61080b565b34801561028457600080fd5b506101a5610ad2565b34801561029957600080fd5b5060005474010000000000000000000000000000000000000000900460ff16604051901515815260200161021c565b3480156102d457600080fd5b506101fb7f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c48581565b34801561030857600080fd5b506101a5610ae4565b34801561031d57600080fd5b506101a561032c366004611fae565b610af6565b34801561033d57600080fd5b506101fb7f0000000000000000000000005c7bcd6e7de5423a257d81b442095a1a6ced35c581565b34801561037157600080fd5b506101a5610e04565b34801561038657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101fb565b6101a56103b3366004611f3b565b610e14565b3480156103c457600080fd5b506101fb7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b3480156103f857600080fd5b506101fb7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c7481565b34801561042c57600080fd5b5061048161043b36600461205d565b6001602081905260009182526040909120805491810154600282015460039092015473ffffffffffffffffffffffffffffffffffffffff93841693918216929091169084565b60405161021c949392919073ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b3480156104cf57600080fd5b506101fb7f000000000000000000000000716fbc68e0c761684d9280484243ff094cc5ffab81565b34801561050357600080fd5b506101a5610512366004612076565b610ea1565b34801561052357600080fd5b506101a5610532366004611f17565b611039565b6101a56105453660046120e0565b61109f565b34801561055657600080fd5b506101fb7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de581565b610586611413565b60005b818110156105ed578282828181106105a3576105a3612185565b905060800201600160008585858181106105bf576105bf612185565b90506080020160600135815260200190815260200160002081816105e391906121b4565b5050600101610589565b505050565b6105fa611466565b61063b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de51683836114bb565b61067f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de5163361dead8661153c565b6106c073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c741633856115a3565b604051839033907f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a3505050565b6106fa611413565b61077b8161073d7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c7473ffffffffffffffffffffffffffffffffffffffff166115fc565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c741691906115a3565b50565b610786611466565b6107c773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000716fbc68e0c761684d9280484243ff094cc5ffab1683836114bb565b61067f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000716fbc68e0c761684d9280484243ff094cc5ffab163361dead8661153c565b610813611466565b61085473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000716fbc68e0c761684d9280484243ff094cc5ffab1683836114bb565b61089873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000716fbc68e0c761684d9280484243ff094cc5ffab163361dead8861153c565b3415610999578334146108d7576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70f16fd6000000000000000000000000000000000000000000000000000000008152600481018690526024810184905233604482015260806064820152600060848201527f00000000000000000000000040000320d200c110100638040f10500c8f0010b973ffffffffffffffffffffffffffffffffffffffff16906370f16fd690349060a4016000604051808303818588803b15801561097b57600080fd5b505af115801561098f573d6000803e3d6000fd5b5050505050610a9e565b6109db73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633308761153c565b6040517fc9fdcb4c00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905233606482015260a06084820152600060a48201527f00000000000000000000000040000320d200c110100638040f10500c8f0010b973ffffffffffffffffffffffffffffffffffffffff169063c9fdcb4c9060c401600060405180830381600087803b158015610a8557600080fd5b505af1158015610a99573d6000803e3d6000fd5b505050505b604051859033907f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a35050505050565b610ada611413565b610ae261166e565b565b610aec611413565b610ae260006116eb565b610afe611466565b6000878152600160205260408120600301549003610b48576040517f90eaaa7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516610b67573394505b610ba873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c4851683836114bb565b600080610bb68c8c8c611760565b6040517f19f1048f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301529294509092506000917f00000000000000000000000040000320d200c110100638040f10500c8f0010b916906319f1048f906024016020604051808303816000875af1158015610c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7191906122ae565b9050610cb673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de51661dead856115a3565b604051839073ffffffffffffffffffffffffffffffffffffffff8a16907f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a36000610d6e82610d0a8a35876122c7565b610d158a35876122c7565b60408051602081019490945246848201526060840192909252608083015260a082018d905273ffffffffffffffffffffffffffffffffffffffff8c1660c0808401919091528151808403909101815260e0909201905290565b9050610d8c8460008d610d86368d90038d018d61231b565b85611a3b565b610da28360018d610d86368c90038c018c61231b565b82848a73ffffffffffffffffffffffffffffffffffffffff167f782334e555e7f5df3383da5b8d85e76ac57c38b6385a8ee43961e2064c5878958e85604051610dec92919061242f565b60405180910390a45050505050505050505050505050565b610e0c611413565b610ae2611c86565b610e1c611466565b610e5d73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de51683836114bb565b61089873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de5163361dead8861153c565b610ea9611466565b610eea73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c4851683836114bb565b600080610ef8888888611760565b9092509050610f4073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de51661dead846115a3565b6040517fc9fdcb4c00000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044810186905233606482015260a06084820152600060a48201527f00000000000000000000000040000320d200c110100638040f10500c8f0010b973ffffffffffffffffffffffffffffffffffffffff169063c9fdcb4c9060c401600060405180830381600087803b158015610fea57600080fd5b505af1158015610ffe573d6000803e3d6000fd5b50506040518492503391507f8b80bd19aea7b735bc6d75db8d6adbe18b28c30d62b3555245eb67b2340caedc90600090a35050505050505050565b611041611413565b73ffffffffffffffffffffffffffffffffffffffff8116611096576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b61077b816116eb565b6110a7611466565b60008781526001602052604081206003015490036110f1576040517f90eaaa7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516611110573394505b61115173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c741683836114bb565b61119373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c741633308c61153c565b3415611216578734146111d2576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112117f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16611cf5565b611258565b61125873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633308b61153c565b6040517f19f1048f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301526000917f00000000000000000000000040000320d200c110100638040f10500c8f0010b9909116906319f1048f906024016020604051808303816000875af11580156112eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130f91906122ae565b905060006113868261132288358e6122c7565b61132d88358e6122c7565b60408051602081019490945246848201526060840192909252608083015260a082018b905273ffffffffffffffffffffffffffffffffffffffff8a1660c0808401919091528151808403909101815260e0909201905290565b905061139e8b60008b610d86368b90038b018b61231b565b6113b48a60018b610d86368a90038a018a61231b565b898b8873ffffffffffffffffffffffffffffffffffffffff167f782334e555e7f5df3383da5b8d85e76ac57c38b6385a8ee43961e2064c5878958c856040516113fe92919061242f565b60405180910390a45050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ae2576040517f118cdaa700000000000000000000000000000000000000000000000000000000815233600482015260240161108d565b60005474010000000000000000000000000000000000000000900460ff1615610ae2576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080156114f45760e081146114f9577fb78cb0dd0000000000000000000000000000000000000000000000000000000060005260046000fd5b611536565b6040517fd505accf00000000000000000000000000000000000000000000000000000000815260e08460048301376020600060e4836000895af150505b50505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af1806001600051141661159457803d873b15171061159457637939f4246000526004601cfd5b50600060605260405250505050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af180600160005114166115f157803d853b1517106115f1576390b8ec186000526004601cfd5b506000603452505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee821460018114611664576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152602081602483875afa801561165d57815193505b5050611668565b4791505b50919050565b611676611d37565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000806117a573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c4851633308861153c565b6040517f138504c40000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c48573ffffffffffffffffffffffffffffffffffffffff169063138504c4906024016020604051808303816000875af1158015611834573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185891906122ae565b6040517f745400c9000000000000000000000000000000000000000000000000000000008152600481018890529091507f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c48573ffffffffffffffffffffffffffffffffffffffff169063745400c990602401600060405180830381600087803b1580156118e357600080fd5b505af11580156118f7573d6000803e3d6000fd5b50506040517fb0d88d7f0000000000000000000000000000000000000000000000000000000081526004810184905260248101889052604481018790527f000000000000000000000000593f39a4ba26a9c8ed2128ac95d109e8e403c48573ffffffffffffffffffffffffffffffffffffffff16925063b0d88d7f9150606401600060405180830381600087803b15801561199157600080fd5b505af11580156119a5573d6000803e3d6000fd5b5050505060006119ea7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff166115fc565b90506000611a2d7f000000000000000000000000cafe001067cdef266afb7eb5a286dcfd277f3de573ffffffffffffffffffffffffffffffffffffffff166115fc565b989197509095505050505050565b8315611aa757611aa273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2167f0000000000000000000000005c7bcd6e7de5423a257d81b442095a1a6ced35c587611d8b565b611b08565b611b0873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c74167f0000000000000000000000005c7bcd6e7de5423a257d81b442095a1a6ced35c587611d8b565b60008381526001602052604090206002015473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005c7bcd6e7de5423a257d81b442095a1a6ced35c5811691637b9392329133911687611b87577f0000000000000000000000004e107a0000db66f0e9fd2039288bf811dd1f9c74611ba9565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b88611bd85760008881526001602052604090205473ffffffffffffffffffffffffffffffffffffffff16611c02565b6000888152600160208190526040909120015473ffffffffffffffffffffffffffffffffffffffff165b87518b90611c1090826122c7565b8a8a602001518b604001518c606001518d608001518d6040518d63ffffffff1660e01b8152600401611c4d9c9b9a99989796959493929190612450565b600060405180830381600087803b158015611c6757600080fd5b505af1158015611c7b573d6000803e3d6000fd5b505050505050505050565b611c8e611466565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586116c13390565b6040517fd0e30db000000000000000000000000000000000000000000000000000000000815260008060048334865af1611d33573d6000803e3d6000fd5b5050565b60005474010000000000000000000000000000000000000000900460ff16610ae2576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81601452806034526f095ea7b300000000000000000000000060005260206000604460106000875af180600160005114166115f157803d853b1517106115f157633e3f8f736000526004601cfd5b60008060208385031215611dec57600080fd5b823567ffffffffffffffff811115611e0357600080fd5b8301601f81018513611e1457600080fd5b803567ffffffffffffffff811115611e2b57600080fd5b8560208260071b8401011115611e4057600080fd5b6020919091019590945092505050565b60008083601f840112611e6257600080fd5b50813567ffffffffffffffff811115611e7a57600080fd5b602083019150836020828501011115611e9257600080fd5b9250929050565b600080600060408486031215611eae57600080fd5b83359250602084013567ffffffffffffffff811115611ecc57600080fd5b611ed886828701611e50565b9497909650939450505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461077b57600080fd5b8035611f1281611ee5565b919050565b600060208284031215611f2957600080fd5b8135611f3481611ee5565b9392505050565b600080600080600060808688031215611f5357600080fd5b853594506020860135935060408601359250606086013567ffffffffffffffff811115611f7f57600080fd5b611f8b88828901611e50565b969995985093965092949392505050565b600060a0828403121561166857600080fd5b6000806000806000806000806000806102208b8d031215611fce57600080fd5b8a35995060208b0135985060408b0135975060608b0135965060808b0135955060a08b0135611ffc81611ee5565b945061200b8c60c08d01611f9c565b935061201b8c6101608d01611f9c565b92506102008b013567ffffffffffffffff81111561203857600080fd5b6120448d828e01611e50565b915080935050809150509295989b9194979a5092959850565b60006020828403121561206f57600080fd5b5035919050565b60008060008060008060a0878903121561208f57600080fd5b86359550602087013594506040870135935060608701359250608087013567ffffffffffffffff8111156120c257600080fd5b6120ce89828a01611e50565b979a9699509497509295939492505050565b60008060008060008060008060006102008a8c0312156120ff57600080fd5b8935985060208a0135975060408a0135965060608a0135955060808a013561212681611ee5565b94506121358b60a08c01611f9c565b93506121458b6101408c01611f9c565b92506101e08a013567ffffffffffffffff81111561216257600080fd5b61216e8c828d01611e50565b915080935050809150509295985092959850929598565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81356121bf81611ee5565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617825550602082013561220c81611ee5565b6001820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905550604082013561225d81611ee5565b6002820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905550606082013560038201555050565b6000602082840312156122c057600080fd5b5051919050565b81810381811115612301577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b803563ffffffff81168114611f1257600080fd5b600060a082840312801561232e57600080fd5b5060405160a0810167ffffffffffffffff81118282101715612379577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528235815261238c60208401611f07565b602082015261239d60408401612307565b60408201526123ae60608401612307565b60608201526123bf60808401612307565b60808201529392505050565b6000815180845260005b818110156123f1576020818501810151868301820152016123d5565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b82815260406020820152600061244860408301846123cb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8d16815273ffffffffffffffffffffffffffffffffffffffff8c16602082015273ffffffffffffffffffffffffffffffffffffffff8b16604082015273ffffffffffffffffffffffffffffffffffffffff8a1660608201528860808201528760a08201528660c08201526124f160e082018773ffffffffffffffffffffffffffffffffffffffff169052565b63ffffffff851661010082015263ffffffff841661012082015263ffffffff831661014082015261018061016082015260006125316101808301846123cb565b9e9d505050505050505050505050505056fea2646970667358221220607e101d4c86a22588df957bbeeb78be6a5d561319c107e83acea78d1592c09c64736f6c634300081c0033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.