ETH Price: $2,443.49 (-9.28%)

Contract

0x87F224ad62D90FEe740d27C269647163b2d75499

Overview

ETH Balance

Linea Mainnet LogoLinea Mainnet LogoLinea Mainnet Logo0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
267170772025-12-15 12:29:1047 days ago1765801750
0x87F224ad...3b2d75499
0 ETH
267170772025-12-15 12:29:1047 days ago1765801750
0x87F224ad...3b2d75499
0 ETH
267170772025-12-15 12:29:1047 days ago1765801750
0x87F224ad...3b2d75499
0 ETH
267170772025-12-15 12:29:1047 days ago1765801750
0x87F224ad...3b2d75499
0 ETH
267170772025-12-15 12:29:1047 days ago1765801750
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
267170722025-12-15 12:29:0047 days ago1765801740
0x87F224ad...3b2d75499
0 ETH
266995192025-12-15 2:20:1248 days ago1765765212
0x87F224ad...3b2d75499
0 ETH
266995192025-12-15 2:20:1248 days ago1765765212
0x87F224ad...3b2d75499
0 ETH
266995192025-12-15 2:20:1248 days ago1765765212
0x87F224ad...3b2d75499
0 ETH
266995192025-12-15 2:20:1248 days ago1765765212
0x87F224ad...3b2d75499
0 ETH
266995192025-12-15 2:20:1248 days ago1765765212
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266995132025-12-15 2:20:0048 days ago1765765200
0x87F224ad...3b2d75499
0 ETH
266818422025-12-14 15:59:4248 days ago1765727982
0x87F224ad...3b2d75499
0 ETH
View All Internal Transactions
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RebalancingManager

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 999999 runs

Other Settings:
paris EvmVersion
File 1 of 23 : RebalancingManager.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IEIP6492} from "../interfaces/IEIP6492.sol";
import {EncodeKeyLib} from "../lib/EncodeKeyLib.sol";
import {FeesLib} from "../lib/FeesLib.sol";
import {AccessControl} from "../manager/access/AccessControl.sol";
import {EIP3009Args} from "../manager/args/EIP3009Args.sol";
import {RebalancingArgs} from "../manager/args/RebalancingArgs.sol";
import {RebalancingConfigArgs} from "../manager/args/RebalancingConfigArgs.sol";
import {Operation} from "../manager/operation/Operation.sol";
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";
import {PoolToken} from "../manager/pool/PoolToken.sol";
import {IRebalancer} from "../rebalancer/IRebalancer.sol";
import {RebalancerProxy} from "../rebalancer/RebalancerProxy.sol";
import {IRebalancingManager} from "./IRebalancingManager.sol";

/**
 * @title RebalancingManager
 * @author MetaLend
 * @notice This contract manages the rebalancing of assets across different pools and domains (chains).
 */
contract RebalancingManager is IRebalancingManager, AccessControl {
    /// @notice The name type hash for EIP-712 signatures
    bytes32 public constant NAME_TYPEHASH = keccak256(bytes("MetaLend Rebalancing"));
    /// @notice The version type hash for EIP-712 signatures
    bytes32 public constant VERSION_TYPEHASH = keccak256(bytes("3.0.0"));
    /// @notice The domain type hash for EIP-712 signatures
    bytes32 public constant DOMAIN_TYPEHASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    /// @notice The withdrawal type hash for EIP-712 signatures
    bytes32 public constant WITHDRAWAL_TYPEHASH =
        keccak256("Withdrawal(address token,address poolContract,uint256 amount,uint256 deadline)");
    /// @notice The denominator for the rebalancing fee percentage, used for basis points calculations
    uint256 public constant FEE_DENOMINATOR = 100000;
    /// @notice The maximum rebalancing fee percentage allowed
    uint256 public constant MAX_REBALANCING_FEE_PERCENT = 1000; // 1%
    /// @notice USDC CCTP domain ID for the Linea mainnet
    uint32 public constant LINEA_DOMAIN_ID = 11;
    /// @notice The address of the Linea AAVE USDC aToken contract
    address private constant _LINEA_USDC_AAVE_TOKEN = 0x374D7860c4f2f604De0191298dD393703Cce84f3;

    /// @notice The address of the rebalancer implementation contract
    address private _rebalancerImplementation;
    /// @notice The address of the USDC CCTP token messenger contract
    address private _usdcCctpTokenMessenger;
    /// @notice The address of the USDC CCTP message transmitter contract
    address private _usdcCctpMessageTransmitter;
    /// @notice The gas transaction fee for on behalf of transactions
    /// @dev Discontinued storage slot in favor of _domainFees mapping supporting different fees per operation and different token decimals
    uint256 private _gasTransactionFee;
    /// @notice The address of the USDC token contract
    address private _usdcToken;
    /// @notice The cooldown period for rebalancing
    uint256 public rebalanceCooldown;
    /// @notice Mapping of owner addresses to rebalancer contract addresses
    mapping(address owner => address rebalancer) public rebalancers;
    /// @notice Mapping of destination domains, also specifies the fee on destination domain, supported is when value is greater than 0
    /// @dev Discontinued storage slot in favor of _domainFees mapping supporting different fees per operation and different token decimals
    // solhint-disable-next-line private-vars-leading-underscore
    mapping(uint32 destinationDomain => uint256 supported) private supportedDestinationDomains;
    /// @notice Mapping of rebalancer addresses to their last rebalance timestamps
    /// @dev Discontinued storage slot in favor of _rebalancerLastRebalanceTimestampPerToken which supports cooldown per token
    // solhint-disable-next-line private-vars-leading-underscore
    mapping(address rebalancer => uint256 timestamp) private rebalancerLastRebalanceTimestamp;
    /// @notice Mapping of token addresses to their corresponding pool contracts
    /// @dev Discontinued storage slot in favor of _domainPoolContract mapping which supports multiple pool contracts for the same token
    mapping(address token => mapping(PoolIdentifier pool => address poolContract)) private _tokenToPool;
    /// @notice Mapping of used withdrawal signatures
    mapping(bytes signature => bool used) private _usedWithdrawalSignatures;
    /// @notice Mapping of pool contract addresses to their corresponding token address for each pool on given domain
    /// @dev Discontinued storage slot in favor of _pools mapping which supports multiple tokens for the same pool address (such as AAVE)
    mapping(uint256 destinationDomain => mapping(address poolContract => PoolToken poolToken)) private _domainPoolContract;
    /// @notice The rebalancing fee percentage
    uint256 private _rebalancingFeePercent;
    /// @notice The destination domain for this chain
    uint32 public thisDestinationDomain;
    /// @notice The universal signature validator contract address
    address public universalSigValidator;
    /// @notice Mapping of domain to fees, additionally provides if domain is supported with value > 0 for given identifier (operation)
    /// @dev for constants use fractional human readable format, convert constant by token decimals.
    /// @dev enables future support of percentage based fees as well with a FeeType identifier included in encoded key
    mapping(uint32 domain => mapping(bytes32 identifier => uint256 gasTransactionFee)) private _domainFees;
    /// @notice Mapping of rebalancer address to token address to their last rebalance timestamp, supports cooldown per token
    mapping(address rebalancer => mapping(address token => uint256 timestamp)) private _rebalancerLastRebalanceTimestampPerToken;
    /// @notice Mapping domain to encoded supported pool (pool address, pool identifier, token) to boolean indicating if supported
    mapping(uint256 domain => mapping(bytes32 pool => bool supported)) private _pools;

    /**
     * @inheritdoc IRebalancingManager
     */
    function initialize(
        address admin_,
        address rebalancerImplementation_,
        address tokenMessenger_,
        address messageTransmitter_,
        address usdcToken_,
        uint32 thisDestinationDomain_,
        address universalSigValidator_
    ) external override {
        _initializeAdmin(admin_);
        _rebalancerImplementation = rebalancerImplementation_;
        _usdcCctpTokenMessenger = tokenMessenger_;
        _usdcCctpMessageTransmitter = messageTransmitter_;
        _usdcToken = usdcToken_;
        thisDestinationDomain = thisDestinationDomain_;
        universalSigValidator = universalSigValidator_;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function depositWithAuthorization(
        address token,
        address onBehalfOf,
        uint32 destinationDomain,
        EIP3009Args calldata tranferArgs,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external override onlyOperator {
        // 1. preprocess the deposit operation
        address rebalancerAddress = _preprocessDeposit({
            destinationDomain: destinationDomain,
            rebalancingArgs: rebalancingArgs,
            rebalancingConfigArgs: rebalancingConfigArgs,
            onBehalfOf: onBehalfOf,
            token: token
        });
        // 2. retrieve USDC approval signature components
        (uint8 v, bytes32 r, bytes32 s) = _getSignatureComponents(onBehalfOf, tranferArgs.signature);
        // 3. trigger deposit with authorization on the rebalancer
        IRebalancer(rebalancerAddress).approveAndDeposit({
            destinationDomain: destinationDomain == thisDestinationDomain ? type(uint32).max : destinationDomain,
            pool: rebalancingArgs.pool,
            poolContract: rebalancingArgs.poolContract,
            token: token,
            amount: tranferArgs.value,
            validAfter: tranferArgs.validAfter,
            validBefore: tranferArgs.validBefore,
            nonce: tranferArgs.nonce,
            v: v,
            r: r,
            s: s
        });

        // 4. finally, emit the DepositWithAuthorization event
        emit DepositWithAuthorization(token, onBehalfOf, tranferArgs.value);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function deposit(
        address token,
        address onBehalfOf,
        uint32 destinationDomain,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external override onlyOperator {
        // 1. preprocess the deposit operation
        address rebalancerAddress = _preprocessDeposit({
            destinationDomain: destinationDomain,
            rebalancingArgs: rebalancingArgs,
            rebalancingConfigArgs: rebalancingConfigArgs,
            onBehalfOf: onBehalfOf,
            token: token
        });
        // 2. trigger deposit on the rebalancer
        uint256 amount = IRebalancer(rebalancerAddress).approveAndDeposit({
            destinationDomain: destinationDomain == thisDestinationDomain ? type(uint32).max : destinationDomain,
            pool: rebalancingArgs.pool,
            poolContract: rebalancingArgs.poolContract,
            token: token
        });

        // 3. finally, emit the Deposit event
        emit Deposit(token, onBehalfOf, amount);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function rebalanceUsdcSrc(
        address onBehalfOf,
        uint32 destinationDomain,
        uint256 amount,
        RebalancingArgs calldata rebalancingArgsFrom,
        RebalancingArgs calldata rebalancingArgsTo,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external override onlyOperator {
        // 1. ensure the rebalancer exists for the owner
        address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);

        // 2. validation, domain must be supported, config must be valid, cooldown must be passed
        _ensureDestinationDomainSupported(destinationDomain);
        _ensurePoolContractSupported(thisDestinationDomain, rebalancingArgsFrom.poolContract, rebalancingArgsFrom.pool, _usdcToken);
        // this ensures at least 1 pool is approved for the destination domain
        _verifyPoolsApprovedAndSupported(rebalancingArgsTo, rebalancingConfigArgs, destinationDomain, _usdcToken);
        _verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
        _ensureCooldownPassedAndUpdate(rebalancerAddress, _usdcToken);

        // 3. withdraw and deposit USDC for burn
        IRebalancer(rebalancerAddress).withdrawAndDepositForBurnUsdc(
            amount,
            rebalancingArgsFrom.pool,
            rebalancingArgsFrom.poolContract,
            destinationDomain
        );

        // 4. finally, emit the RebalanceSrc event
        emit RebalanceSrc(onBehalfOf, destinationDomain, rebalancingArgsFrom.pool, _usdcToken);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function rebalanceUsdcDst(
        address onBehalfOf,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs,
        bytes calldata message,
        bytes calldata attestation
    ) external override onlyOperator {
        // 1. validation, config must be valid
        _verifyPoolsApprovedAndSupported(rebalancingArgs, rebalancingConfigArgs, thisDestinationDomain, _usdcToken);
        _verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);

        // 2. get or create the rebalancer for the owner
        address rebalancerAddress = _getOrCreateRebalancer(onBehalfOf);

        // 3. claim the USDC tokens from the CCTP and deposit them into the pool
        IRebalancer(rebalancerAddress).receiveUsdcApproveAndDeposit(
            rebalancingArgs.pool,
            rebalancingArgs.poolContract,
            message,
            attestation
        );

        // 4. finally, emit the RebalanceDst event
        emit RebalanceDst(onBehalfOf, rebalancingArgs.pool, _usdcToken);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function rebalanceThisDomain(
        address onBehalfOf,
        address token,
        RebalancingArgs calldata rebalancingArgsFrom,
        RebalancingArgs calldata rebalancingArgsTo,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external override onlyOperator {
        // 1. ensure the rebalancer exists for the owner
        address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);

        // 2. validation, config must be valid, cooldown must be passed, cannot rebalance to the same pool
        if (rebalancingArgsFrom.poolContract == rebalancingArgsTo.poolContract) {
            revert InvalidOperation();
        }
        _ensurePoolContractSupported(thisDestinationDomain, rebalancingArgsFrom.poolContract, rebalancingArgsFrom.pool, token);
        _verifyPoolsApprovedAndSupported(rebalancingArgsTo, rebalancingConfigArgs, thisDestinationDomain, token);
        _verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
        _ensureCooldownPassedAndUpdate(rebalancerAddress, token);

        // 3. withdraw from the source pool and deposit into the destination pool
        IRebalancer(rebalancerAddress).withdrawAndRedeposit({
            token: token,
            srcPool: rebalancingArgsFrom.pool,
            srcPoolContract: rebalancingArgsFrom.poolContract,
            dstPool: rebalancingArgsTo.pool,
            dstPoolContract: rebalancingArgsTo.poolContract
        });

        // 4. finally, emit the RebalanceSrc and RebalanceDst events
        emit RebalanceSrc(onBehalfOf, thisDestinationDomain, rebalancingArgsFrom.pool, token);
        emit RebalanceDst(onBehalfOf, rebalancingArgsTo.pool, token);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function fundOwner(address onBehalfOf, RebalancingConfigArgs calldata rebalancingConfigArgs) external override onlyOperator {
        // 1. validation, config must be valid, funding cap must be greater than zero, this domain must be Linea domain (11)
        if (rebalancingConfigArgs.fundingCap == 0 || !isLineaDomain()) {
            revert InvalidOperation();
        }
        _verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);

        // 2. ensure the rebalancer exists for the owner
        address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);

        // 3. fund the owner with aTokens (AAVE)
        IRebalancer(rebalancerAddress).fundOwnerWithUsdcAaveToken(rebalancingConfigArgs.fundingCap);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function withdrawFromPool(
        address onBehalfOf,
        address token,
        PoolIdentifier pool,
        address poolContract,
        uint256 amount,
        uint256 deadline,
        bytes calldata signature
    ) external override {
        address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);
        _ensurePoolContractSupported(thisDestinationDomain, poolContract, pool, token);
        // owner withdrawal
        if (signature.length == 0) {
            if (onBehalfOf != msg.sender) {
                revert Unauthorized();
            }
            IRebalancer(rebalancerAddress).withdrawFromPoolToOwner({
                token: token,
                pool: pool,
                poolContract: poolContract,
                amount: amount,
                payFee: false
            });
        } else {
            // on behalf of withdrawal
            _verifyWithdrawalSignature({
                signer: onBehalfOf,
                signature: signature,
                token: token,
                poolContract: poolContract,
                amount: amount,
                deadline: deadline
            });
            IRebalancer(rebalancerAddress).withdrawFromPoolToOwner({
                token: token,
                pool: pool,
                poolContract: poolContract,
                amount: amount,
                payFee: true
            });
        }
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function claimRewardsMorpho(
        address rewardDistributor,
        address rewardToken,
        uint256 claimable,
        bytes32[] calldata proof
    ) external override {
        address rebalancerAddress = _getOrCreateRebalancer(msg.sender);
        IRebalancer(rebalancerAddress).claimRewardsMorpho(rewardDistributor, rewardToken, claimable, proof);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function claimRewards(
        address rewardDistributor,
        address[] calldata rewardTokens,
        uint256[] calldata claimable,
        bytes32[][] calldata proofs
    ) external override {
        address rebalancerAddress = _getOrCreateRebalancer(msg.sender);
        IRebalancer(rebalancerAddress).claimRewards(rewardDistributor, rewardTokens, claimable, proofs);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function claimRewardsAave(address rewardsController, address[] calldata assets) external override {
        address rebalancerAddress = _getOrCreateRebalancer(msg.sender);
        IRebalancer(rebalancerAddress).claimRewardsAave(rewardsController, assets);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function updatePoolContract(
        uint32 destinationDomain,
        address[] calldata poolContracts,
        PoolIdentifier[] calldata pools,
        address[] calldata tokens,
        bool[] calldata supported
    ) external override onlyAdmin {
        if (poolContracts.length != pools.length || poolContracts.length != tokens.length || poolContracts.length != supported.length) {
            revert InvalidInputLength();
        }

        uint256 length = poolContracts.length;
        for (uint256 i = 0; i < length; ++i) {
            bytes32 poolKey = EncodeKeyLib._getEncodedPoolKey(poolContracts[i], pools[i], tokens[i]);
            _pools[destinationDomain][poolKey] = supported[i];
            emit PoolContractUpdated({
                destinationDomain: destinationDomain,
                poolContract: poolContracts[i],
                pool: pools[i],
                token: tokens[i],
                supported: supported[i]
            });
        }
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function updateRebalancerImplementation(address rebalancerImplementation) external override onlyAdmin {
        _rebalancerImplementation = rebalancerImplementation;

        emit RebalancerImplementationUpdated(rebalancerImplementation);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function setDomainFeeForOperation(
        uint32[] calldata domains,
        Operation[] calldata operations,
        uint256[] calldata fees
    ) external override onlyAdmin {
        if (domains.length != operations.length || domains.length != fees.length) {
            revert InvalidInputLength();
        }

        uint256 length = domains.length;
        for (uint256 i = 0; i < length; ) {
            uint32 domain = domains[i];
            Operation operation = operations[i];
            uint256 fee = fees[i];

            if (domain == type(uint32).max) {
                revert InvalidOperation();
            }
            _domainFees[domain][EncodeKeyLib._getEncodedOperationKey(operation)] = fee;

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function setRebalanceFeePercent(uint256 rebalancingFeePercent) external override onlyAdmin {
        if (rebalancingFeePercent > MAX_REBALANCING_FEE_PERCENT) {
            revert InvalidOperation();
        }
        _rebalancingFeePercent = rebalancingFeePercent;

        emit RebalanceFeePercentUpdated(rebalancingFeePercent);
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function setRebalanceCooldown(uint256 cooldown) external override onlyAdmin {
        rebalanceCooldown = cooldown;
    }

    /**
     * @notice Returns the address of the rebalancer implementation contract
     * @dev Use this with _implementation() in the proxy contract to get the implementation address
     * @return rebalancerImplementation The address of the rebalancer implementation contract
     */
    function getRebalancerImplementation() external view override returns (address rebalancerImplementation) {
        rebalancerImplementation = _rebalancerImplementation;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getRebalanceFee(uint256 amount) external view override returns (uint256 rebalanceFee) {
        if (_rebalancingFeePercent == 0 || amount == 0 || isLineaDomain()) {
            return 0;
        }
        rebalanceFee = (amount * _rebalancingFeePercent) / FEE_DENOMINATOR;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getDestinationDomainGasTransactionFeeForOperation(
        uint32 destinationDomain,
        address token,
        Operation operation,
        bool useThisDomain
    ) external view override returns (uint256 gasTransactionFee) {
        gasTransactionFee = FeesLib.convertConstantFeeToRawValue(
            _domainFees[useThisDomain ? thisDestinationDomain : destinationDomain][EncodeKeyLib._getEncodedOperationKey(operation)],
            token
        );
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getUsdcCctpTokenMessenger() external view override returns (address tokenMessenger) {
        tokenMessenger = _usdcCctpTokenMessenger;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getUsdcCctpMessageTransmitter() external view override returns (address messageTransmitter) {
        messageTransmitter = _usdcCctpMessageTransmitter;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getUsdcToken() external view override returns (address usdcToken) {
        usdcToken = _usdcToken;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getPoolContractSupported(
        uint32 destinationDomain,
        address poolContract,
        PoolIdentifier pool,
        address token
    ) external view override returns (bool supported) {
        supported = _pools[destinationDomain][EncodeKeyLib._getEncodedPoolKey(poolContract, pool, token)];
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function getLineaUsdcAaveToken() external pure override returns (address aToken) {
        aToken = _LINEA_USDC_AAVE_TOKEN;
    }

    /**
     * @inheritdoc IRebalancingManager
     */
    function isLineaDomain() public view override returns (bool isLinea) {
        isLinea = thisDestinationDomain == LINEA_DOMAIN_ID;
    }

    /**
     * @notice Internal function to create or get the rebalancer address for a given owner
     * @param owner                The address of the owner
     * @return rebalancerAddress   The address of the rebalancer contract
     */
    function _getOrCreateRebalancer(address owner) private returns (address rebalancerAddress) {
        if (rebalancers[owner] == address(0)) {
            rebalancerAddress = _createRebalancer(owner);
        } else {
            rebalancerAddress = rebalancers[owner];
        }
    }

    /**
     * @notice Internal function to create a new rebalancer contract
     * @dev Reverts if the rebalancer already exists for the owner
     * @param owner                The address of the owner
     * @return rebalancerAddress   The address of the newly created rebalancer contract
     */
    function _createRebalancer(address owner) private returns (address rebalancerAddress) {
        bytes memory bytecode = type(RebalancerProxy).creationCode;
        bytes memory initCode = abi.encodePacked(bytecode, abi.encode(address(this), owner));
        bytes32 salt = keccak256(abi.encodePacked(owner));
        rebalancerAddress = Create2.deploy(0, salt, initCode);
        rebalancers[owner] = rebalancerAddress;
        emit RebalancerDeployed(owner, rebalancerAddress);
    }

    /**
     * @notice Internal function to preprocess the deposit operation
     * @dev This function validates the pools, config and then gets or creates the rebalancer
     * @param destinationDomain       The destination domain for the deposit
     * @param rebalancingArgs         The rebalancing arguments. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     * @param onBehalfOf              The address on behalf of which the deposit is made
     * @param token                   The address of the token being deposited
     * @return rebalancerAddress      The address of the rebalancer contract
     */
    function _preprocessDeposit(
        uint32 destinationDomain,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs,
        address onBehalfOf,
        address token
    ) private returns (address rebalancerAddress) {
        // 1. validate approved pools and pool contracts and ensure the pool (vault) is supported for the token
        _verifyPoolsApprovedAndSupported(rebalancingArgs, rebalancingConfigArgs, destinationDomain, token);
        _verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
        // 2. double check destination domain if different than this domain
        if (destinationDomain != thisDestinationDomain) {
            _ensureDestinationDomainSupported(destinationDomain);
            // 2.1 only USDC is currently supported for cross-domain deposit
            if (token != _usdcToken) {
                revert InvalidOperation();
            }
        }
        // 3. get or create the rebalancer for the owner
        rebalancerAddress = _getOrCreateRebalancer(onBehalfOf);
        // 4. reset cooldown to allow immediate rebalancing for new deposits
        _resetCooldown(rebalancerAddress, token);
    }

    /**
     * @notice Internal function to ensure the cooldown period has passed and update the last rebalance timestamp
     * @param rebalancerAddress   The address of the rebalancer contract
     * @param token               The address of the token for which to check the cooldown
     */
    function _ensureCooldownPassedAndUpdate(address rebalancerAddress, address token) private {
        uint256 lastRebalanceTimestamp = _rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token];
        if (block.timestamp < lastRebalanceTimestamp + rebalanceCooldown) {
            revert RebalanceCooldownNotPassed(block.timestamp, lastRebalanceTimestamp + rebalanceCooldown);
        }
        _rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token] = block.timestamp;

        emit RebalancerLastRebalanceTimestampUpdated(rebalancerAddress, token, block.timestamp);
    }

    /**
     * @notice Internal function to reset the cooldown for a rebalancer
     * @dev This function sets the last rebalance timestamp to zero, effectively resetting the cooldown
     * @param rebalancerAddress   The address of the rebalancer contract
     * @param token               The address of the token for which to reset the cooldown
     */
    function _resetCooldown(address rebalancerAddress, address token) private {
        // Only reset if cooldown has NOT passed yet to avoid unnecessary storage writes
        if (block.timestamp < _rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token] + rebalanceCooldown) {
            _rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token] = 0;

            emit RebalancerLastRebalanceTimestampUpdated(rebalancerAddress, token, 0);
        }
    }

    /**
     * @notice Internal function to verify the withdrawal signature
     * @dev Reverts if the signature is invalid. The message hash is created by hashing the token address, amount and chain ID.
     * @param signer      The address of the signer
     * @param signature   The signature to verify
     * @param token       The address of the token
     * @param poolContract The address of the pool contract
     * @param amount      The amount to verify against
     * @param deadline    The deadline for the signature
     */
    function _verifyWithdrawalSignature(
        address signer,
        bytes memory signature,
        address token,
        address poolContract,
        uint256 amount,
        uint256 deadline
    ) private {
        if (block.timestamp > deadline) {
            revert InvalidDeadline(deadline);
        }
        if (_usedWithdrawalSignatures[signature]) {
            revert SignatureUsedAlready(signature);
        }
        _usedWithdrawalSignatures[signature] = true;

        bytes32 structHash = keccak256(abi.encode(WITHDRAWAL_TYPEHASH, token, poolContract, amount, deadline));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _getDomainSeparator(), structHash));
        _verifySignature(signer, signature, digest);
    }

    /**
     * @notice Internal function to verify the signature of the rebalancing configuration
     * @dev Reverts if the signature is invalid. The message hash is created by hashing the approved pools, pool contracts, and domains.
     * @param signer                  The address of the signer
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     */
    function _verifyConfigSignature(address signer, RebalancingConfigArgs calldata rebalancingConfigArgs) private {
        bytes32 messageHash = rebalancingConfigArgs.fundingCap == 0
            ? keccak256(
                abi.encode(
                    rebalancingConfigArgs.approvedPools,
                    rebalancingConfigArgs.approvedPoolContracts,
                    rebalancingConfigArgs.approvedDestinationDomains
                )
            )
            : keccak256(
                abi.encode(
                    rebalancingConfigArgs.approvedPools,
                    rebalancingConfigArgs.approvedPoolContracts,
                    rebalancingConfigArgs.approvedDestinationDomains,
                    rebalancingConfigArgs.fundingCap
                )
            );

        bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(messageHash);
        _verifySignature(signer, rebalancingConfigArgs.signature, ethSignedMessageHash);
    }

    /**
     * @notice Internal helper function to verify the signature
     * @dev Reverts if the signature is invalid.
     * @param signer      The address of the signer
     * @param signature   The signature to verify
     * @param digest      The digest to verify against
     */
    function _verifySignature(address signer, bytes memory signature, bytes32 digest) private {
        if (!IEIP6492(universalSigValidator).isValidSig(signer, digest, signature)) {
            revert InvalidSignature(signer, signature);
        }
    }

    /**
     *  @notice Internal function to get the EIP-712 domain separator
     *  @dev This function constructs the domain separator used for EIP-712 signatures.
     *  @return domainSeparator The EIP-712 domain separator
     */
    function _getDomainSeparator() private view returns (bytes32 domainSeparator) {
        domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, NAME_TYPEHASH, VERSION_TYPEHASH, block.chainid, address(this)));
    }

    /**
     * @notice Internal function to ensure the rebalancer exists for the given owner
     * @dev Reverts if the rebalancer does not exist
     * @param owner                The address of the owner
     * @return rebalancerAddress   The address of the rebalancer contract
     */
    function _getRebalancerEnsureCreated(address owner) private view returns (address rebalancerAddress) {
        rebalancerAddress = rebalancers[owner];
        if (rebalancerAddress == address(0)) {
            revert RebalancerNotFound(owner);
        }
    }

    /**
     * @notice Internal function to ensure the destination domain is supported
     * @dev Reverts if the destination domain is not supported
     * @param destinationDomain The destination domain to check
     */
    function _ensureDestinationDomainSupported(uint32 destinationDomain) private view {
        if (
            destinationDomain == thisDestinationDomain ||
            _domainFees[destinationDomain][EncodeKeyLib._getEncodedOperationKey(Operation.REBALANCE_DST_DOMAIN)] == 0
        ) {
            revert DestinationDomainNotSupported(destinationDomain);
        }
    }

    /**
     * @notice Internal function to ensure the pool contract is supported
     * @dev Reverts if the pool contract is not supported
     * @param destinationDomain   The destination domain to check
     * @param poolContract        The address of the pool contract to check
     * @param expectedPool        The expected pool identifier for the contract
     * @param expectedToken       The expected token address for the contract
     */
    function _ensurePoolContractSupported(
        uint32 destinationDomain,
        address poolContract,
        PoolIdentifier expectedPool,
        address expectedToken
    ) private view {
        bool isSupported = _pools[destinationDomain][EncodeKeyLib._getEncodedPoolKey(poolContract, expectedPool, expectedToken)];
        if (!isSupported) {
            revert PoolContractNotSupported(poolContract);
        }
    }

    /**
     * @notice Internal function to verify the approved pools and pool contracts
     * @dev Reverts if any of the following conditions are not met:
     *  1. The pool must be approved in the rebalancing arguments.
     *  2. The pool contract must be approved for the pool in the rebalancing arguments.
     *  3. The destination domain must be approved in the rebalancing arguments.
     *  4. The pool contract must be supported for the token.
     * @param rebalancingArgs         The rebalancing arguments. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     * @param destinationDomain       The destination domain to verify against
     * @param token                   The token address to verify against
     */
    function _verifyPoolsApprovedAndSupported(
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs,
        uint32 destinationDomain,
        address token
    ) private view {
        // 1. validate approved pools and pool contracts
        _verifyConfig(rebalancingArgs, rebalancingConfigArgs, destinationDomain);
        // 2. ensure the pool (vault) is supported for the token
        _ensurePoolContractSupported(destinationDomain, rebalancingArgs.poolContract, rebalancingArgs.pool, token);
    }

    /**
     * @notice Internal function to verify the rebalancing configuration
     * @dev Reverts if any of the following conditions are not met:
     *  1. The pool must be approved in the rebalancing arguments.
     *  2. The pool contract must be approved for the pool in the rebalancing arguments.
     *  3. The destination domain must be approved in the rebalancing arguments.
     * @param rebalancingArgs         The rebalancing arguments. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     * @param destinationDomain       The destination domain to verify against
     */
    function _verifyConfig(
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs,
        uint32 destinationDomain
    ) private pure {
        if (
            rebalancingConfigArgs.approvedPools.length != rebalancingConfigArgs.approvedPoolContracts.length ||
            rebalancingConfigArgs.approvedPools.length != rebalancingConfigArgs.approvedDestinationDomains.length
        ) {
            revert InvalidInputLength();
        }

        uint256 length = rebalancingConfigArgs.approvedPools.length;
        for (uint256 i = 0; i < length; ) {
            PoolIdentifier pool = rebalancingConfigArgs.approvedPools[i];
            address poolContract = rebalancingConfigArgs.approvedPoolContracts[i];
            uint32 domain = rebalancingConfigArgs.approvedDestinationDomains[i];

            if (pool == rebalancingArgs.pool && poolContract == rebalancingArgs.poolContract && domain == destinationDomain) {
                return;
            }

            unchecked {
                ++i;
            }
        }

        revert InvalidOperation();
    }

    /**
     * @notice Internal function to get the signature components from the signature
     * @param signer      The address of the signer
     * @param signature   The signature to extract components from
     * @return v          The v component of the signature
     * @return r          The r component of the signature
     * @return s          The s component of the signature
     */
    function _getSignatureComponents(address signer, bytes memory signature) private pure returns (uint8 v, bytes32 r, bytes32 s) {
        if (signature.length != 65) {
            revert InvalidSignature(signer, signature);
        }

        assembly ("memory-safe") {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
     * function and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Not enough balance for performing a CREATE2 deploy.
     */
    error Create2InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev There's no code to deploy.
     */
    error Create2EmptyBytecode();

    /**
     * @dev The deployment failed.
     */
    error Create2FailedDeployment();

    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        if (address(this).balance < amount) {
            revert Create2InsufficientBalance(address(this).balance, amount);
        }
        if (bytecode.length == 0) {
            revert Create2EmptyBytecode();
        }
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        if (addr == address(0)) {
            revert Create2FailedDeployment();
        }
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

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

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

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

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

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title IEIP6492
 * @author MetaLend
 * @notice Minimal interface for EIP-6492 signature validation.
 * @dev This interface allows for the validation of signatures according to EIP-6492.
 */
interface IEIP6492 {
    /**
     * @notice Validates a signature against a given hash.
     * @dev This function can revert if the underlying call reverts.
     * @param _signer      The address of the signer.
     * @param _hash        The hash to validate against.
     * @param _signature   The signature to validate.
     * @return bool Returns true if the signature is valid, false otherwise.
     */
    function isValidSig(address _signer, bytes32 _hash, bytes calldata _signature) external returns (bool);
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {Operation} from "../manager/operation/Operation.sol";
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";

/**
 * @title EncodeKeyLib
 * @author MetaLend
 * @notice Library for encoding keys used in mappings
 * @dev This library provides functions to encode keys for mapping bytes32 identifiers
 */
library EncodeKeyLib {
    /**
     * @notice External function to get the encoded pool key
     * @param poolContract   The address of the pool contract
     * @param pool           The pool identifier
     * @param token          The address of the token
     * @return encodedKey    The encoded pool key
     */
    function _getEncodedPoolKey(address poolContract, PoolIdentifier pool, address token) internal pure returns (bytes32 encodedKey) {
        encodedKey = keccak256(abi.encodePacked(poolContract, pool, token));
    }

    /**
     * @notice External function to get the encoded operation key
     * @param operation     The operation type
     * @return encodedKey   The encoded operation key
     */
    function _getEncodedOperationKey(Operation operation) internal pure returns (bytes32 encodedKey) {
        encodedKey = keccak256(abi.encodePacked(operation));
    }
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title IERC20
 * @author MetaLend
 * @notice Minimal interface for ERC20 tokens to get decimals
 * @dev This interface is used to interact with ERC20 tokens to retrieve their decimal places
 */
interface IERC20 {
    function decimals() external view returns (uint8);
}

/**
 * @title FeesLib
 * @author MetaLend
 * @notice Library for managing fees
 * @dev This library provides extension helper functions for fee management
 */
library FeesLib {
    uint256 private constant _DENOMINATOR = 1000; // supports milli-tokens (0.001)

    /**
     * @notice Convert a fractional human-readable fee to raw token units
     * @param constantFee   The numerator for the fee (e.g. `1` if fee = 0.001 tokens with denominator = 1000)
     * @param token         The ERC20 token address
     * @return rawValue     The fee converted into token smallest units
     */
    function convertConstantFeeToRawValue(uint256 constantFee, address token) external view returns (uint256 rawValue) {
        uint8 decimals = IERC20(token).decimals();
        uint256 base = 10 ** decimals;

        // Scale fee proportionally to token decimals
        rawValue = (constantFee * base) / _DENOMINATOR;
    }
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

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

/**
 * @title AccessControl
 * @author MetaLend
 * @notice Contract module that provides access control mechanisms
 */
abstract contract AccessControl is IAccessControl {
    /// @notice Operator role identifier
    bytes32 private constant _OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    /// @notice Admin address
    address private _admin;

    /// @notice Operator role mapping
    mapping(address account => bytes32 role) public roles;

    /// @notice Modifier to restrict access to admin
    modifier onlyAdmin() {
        if (msg.sender != _admin) revert Unauthorized();
        _;
    }

    /// @notice Modifier to restrict access to operator
    modifier onlyOperator() {
        if (roles[msg.sender] != _OPERATOR_ROLE) revert Unauthorized();
        _;
    }

    /**
     * @inheritdoc IAccessControl
     */
    function setAdmin(address newAdmin) external override onlyAdmin {
        require(newAdmin != address(0), "AccessControl: new admin is the zero address");
        address oldAdmin = _admin;
        _admin = newAdmin;
        emit AdminChanged(oldAdmin, newAdmin);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function addOperator(address operator) external override onlyAdmin {
        require(operator != address(0), "AccessControl: operator is the zero address");
        roles[operator] = _OPERATOR_ROLE;
        emit OperatorAdded(operator);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function removeOperator(address operator) external override onlyAdmin {
        delete roles[operator];
        emit OperatorRemoved(operator);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function getAdmin() external view override returns (address admin) {
        admin = _admin;
    }

    /**
     * @inheritdoc IAccessControl
     */
    function isOperator(address operator) external view override returns (bool isOpr) {
        isOpr = roles[operator] == _OPERATOR_ROLE;
    }

    /**
     * @notice Initialize the admin address
     * @param admin_ The address of the admin
     */
    function _initializeAdmin(address admin_) internal {
        require(_admin == address(0), "AccessControl: admin already initialized");
        require(admin_ != address(0), "AccessControl: admin is the zero address");
        _admin = admin_;
    }
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title IAccessControl
 * @author MetaLend
 * @notice Interface for access control mechanisms
 */
interface IAccessControl {
    /**
     * @notice Emitted when admin is changed
     * @param oldAdmin Previous admin address
     * @param newAdmin New admin address
     */
    event AdminChanged(address indexed oldAdmin, address indexed newAdmin);

    /**
     * @notice Emitted when operator is added
     * @param operator Operator address
     */
    event OperatorAdded(address indexed operator);

    /**
     * @notice Emitted when operator is removed
     * @param operator Operator address
     */
    event OperatorRemoved(address indexed operator);

    /// @notice Thrown when caller is not authorized
    error Unauthorized();

    /**
     * @notice Set admin address
     * @param newAdmin New admin address
     */
    function setAdmin(address newAdmin) external;

    /**
     * @notice Add operator
     * @param operator Operator address
     */
    function addOperator(address operator) external;

    /**
     * @notice Remove operator
     * @param operator Operator address
     */
    function removeOperator(address operator) external;

    /**
     * @notice Get the admin address
     * @return admin The admin address
     */
    function getAdmin() external view returns (address admin);

    /**
     * @notice Check if the address is an operator
     * @param operator Operator address
     * @return isOpr True if the address is an operator
     */
    function isOperator(address operator) external view returns (bool isOpr);
}

File 13 of 23 : EIP3009Args.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title EIP3009Args
 * @author MetaLend
 * @notice This struct is used to pass arguments for EIP-3009 authorization.
 */
struct EIP3009Args {
    /// @notice The amount of tokens to transfer.
    uint256 value;
    /// @notice After which timestamp the authorization is valid.
    uint256 validAfter;
    /// @notice Before which timestamp the authorization is valid.
    uint256 validBefore;
    /// @notice Unique nonce for the authorization.
    bytes32 nonce;
    /// @notice The signature of the authorization.
    bytes signature;
}

File 14 of 23 : RebalancingArgs.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {PoolIdentifier} from "../pool/PoolIdentifier.sol";

/**
 * @title RebalancingArgs
 * @author MetaLend
 * @notice This struct is used to pass arguments related to a pool and its pool (vault) contract.
 * @dev It is used in rebalancing operations to specify the pool and pool contract.
 *  The `poolContract` is the address of the pool (vault) contract associated with the pool to interact with.
 *  This struct is typically used in conjunction with rebalancing operations to ensure that
 *  the correct pool and its associated contract are targeted for the operation.
 */
struct RebalancingArgs {
    /// @notice The identifier of the pool
    PoolIdentifier pool;
    /// @notice The address of the pool (vault) contract associated with the pool.
    address poolContract;
}

File 15 of 23 : RebalancingConfigArgs.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {PoolIdentifier} from "../pool/PoolIdentifier.sol";

/**
 * @title RebalancingConfigArgs
 * @author MetaLend
 * @notice This struct is used to pass configurations related to rebalancing operations.
 * @dev Use this for validating rebalancing operations by specifying approved pools, pool contracts, and destination domains.
 *  The `approvedPools` is an array of `PoolIdentifier` that represents the pools that are approved for the operation.
 *  The `approvedPoolContracts` is an array of addresses representing the pool (vault) contracts that are approved for the operation.
 *  The `approvedDestinationDomains` is an array of `uint32` representing the domains that are approved for the operation.
 *  The `fundingCap` is a `uint256` that represents maximum configured spendable cap in USDC based on aTokens (AAVE)
 *  in user's wallet on Linea domain (11). This is a special usecase where funds are redirected to Linea domain (11) to top up the
 *  wallet for spending with a MetaMask card. In this case the funds leave the rebalancer and are transferred to the owner.
 *  The `signature` is a bytes array that contains the signature for the approved pools,
 *  pool contracts, destination domains and `fundingCap` (if applicable).
 *  This signature is created by signing the concatenated bytes of the approved pools, pool contracts, and destination domains.
 *  Additionally, the `fundingCap` is included in the signature if it is greater than zero.
 *  This struct is typically used in conjunction with rebalancing operations to ensure that the operation
 *  is valid and authorized for the specified pool, pool contract, destination domain and funding cap.
 *  The length of `approvedPools`, `approvedPoolContracts`, and `approvedDestinationDomains` must match.
 *  Each entry by index in `approvedPools`, `approvedPoolContracts`, and `approvedDestinationDomains`
 *  corresponds to the approved pool, pool contract, and destination domain respectively.
 */
struct RebalancingConfigArgs {
    /// @notice The list of approved pools for an operation
    PoolIdentifier[] approvedPools;
    /// @notice The list of all approved pool (vault) contracts for an operation.
    address[] approvedPoolContracts;
    /// @notice The list of all approved destination domains for an operation.
    /// @dev Even for same domain operations, this must be defined to ensure the signature is valid.
    uint32[] approvedDestinationDomains;
    /// @notice The maximum configured spendable cap in USDC based on aTokens (AAVE) in user's wallet on Linea domain (11).
    uint256 fundingCap;
    /// @notice The signature for the approved pools, pool contracts, and destination domains.
    /// @dev Created by signing the concatenated bytes of the approved pools, pool contracts, and destination domains.
    bytes signature;
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {EIP3009Args} from "../manager/args/EIP3009Args.sol";
import {RebalancingArgs} from "../manager/args/RebalancingArgs.sol";
import {RebalancingConfigArgs} from "../manager/args/RebalancingConfigArgs.sol";
import {Operation} from "../manager/operation/Operation.sol";
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";
import {IRebalancingManagerCore} from "./IRebalancingManagerCore.sol";

/**
 * @title IRebalancingManager
 * @author MetaLend
 * @notice This interface defines the functions for the RebalancingManager contract.
 */
interface IRebalancingManager is IRebalancingManagerCore {
    /**
     * @notice Emitted when a deposit with authorization is made to the pool.
     * @param token        The address of the token being deposited.
     * @param onBehalfOf   The address on behalf of which the deposit is made.
     * @param amount       The amount of tokens deposited.
     */
    event DepositWithAuthorization(address indexed token, address indexed onBehalfOf, uint256 indexed amount);

    /**
     * @notice Emitted when a deposit is made to the pool.
     * @param token        The address of the token being deposited.
     * @param onBehalfOf   The address on behalf of which the deposit is made.
     * @param amount       The amount of tokens deposited.
     */
    event Deposit(address indexed token, address indexed onBehalfOf, uint256 indexed amount);

    /**
     * @notice Emitted when a rebalance is initiated from the source domain.
     * @param onBehalfOf          The address on behalf of which the rebalance is made.
     * @param destinationDomain   The destination domain for the rebalance.
     * @param pool                The pool identifier for the rebalance.
     * @param token               The address of the token being rebalanced.
     */
    event RebalanceSrc(address indexed onBehalfOf, uint32 indexed destinationDomain, PoolIdentifier indexed pool, address token);

    /**
     * @notice Emitted when a rebalance is initiated to the destination domain.
     * @param onBehalfOf   The address on behalf of which the rebalance is made.
     * @param pool         The pool identifier for the rebalance.
     * @param token        The address of the token being rebalanced.
     */
    event RebalanceDst(address indexed onBehalfOf, PoolIdentifier indexed pool, address token);

    /**
     * @notice Emitted when Rebalancer contract is deployed.
     * @param owner        The address of the owner of the Rebalancer contract.
     * @param rebalancer   The address of the deployed Rebalancer contract.
     */
    event RebalancerDeployed(address indexed owner, address indexed rebalancer);

    /**
     * @notice Emitted when pool contract is updated to a specific pool and token.
     * @param destinationDomain   The domain supporting the pool contract
     * @param poolContract        The address of the pool contract being updated.
     * @param pool                The pool identifier for which the contract is being updated.
     * @param token               The address of the token associated with the pool.
     * @param supported           Boolean indicating whether the pool contract is supported or not.
     */
    event PoolContractUpdated(uint32 destinationDomain, address indexed poolContract, PoolIdentifier pool, address token, bool supported);

    /**
     * @notice Emitted when a rebalancer's last rebalance timestamp is updated.
     * @param rebalancer               The address of the rebalancer.
     * @param token                    The address of the token for which the timestamp is updated.
     * @param lastRebalanceTimestamp   The last rebalance timestamp.
     */
    event RebalancerLastRebalanceTimestampUpdated(
        address indexed rebalancer,
        address indexed token,
        uint256 indexed lastRebalanceTimestamp
    );

    /**
     * @notice Emitted when the implementation address of the rebalancer contract is updated.
     * @param rebalancerImplementation The address of the new rebalancer implementation contract.
     */
    event RebalancerImplementationUpdated(address indexed rebalancerImplementation);

    /**
     * @notice Emitted when the rebalance fee percent is updated.
     * @param rebalanceFeePercent The new rebalance fee percent.
     */
    event RebalanceFeePercentUpdated(uint256 indexed rebalanceFeePercent);

    /// @notice Thrown when Rebalancer contract is not found.
    error RebalancerNotFound(address owner);

    /// @notice Thrown when pool contract is not supported.
    error PoolContractNotSupported(address poolContract);

    /// @notice Thrown when the destination domain is not supported.
    error DestinationDomainNotSupported(uint32 destinationDomain);

    /// @notice Thrown when the signature is invalid.
    error InvalidSignature(address signer, bytes signature);

    /// @notice Thrown when the deadline for the transaction has passed.
    error InvalidDeadline(uint256 deadline);

    /// @notice Thrown when signature is used already.
    error SignatureUsedAlready(bytes signature);

    /// @notice Thrown when the cooldown period for rebalancing has not passed.
    error RebalanceCooldownNotPassed(uint256 lastRebalanceTimestamp, uint256 rebalanceCooldown);

    /// @notice Thrown when the input length is invalid.
    error InvalidInputLength();

    /// @notice Thrown when the operation is not allowed.
    error InvalidOperation();

    /**
     * @notice Initializes the RebalancingManager contract.
     * @param admin_                      The address of the admin.
     * @param rebalancerImplementation_   The address of the rebalancer implementation contract.
     * @param tokenMessenger_             The address of the token messenger contract.
     * @param messageTransmitter_         The address of the message transmitter contract.
     * @param usdcToken_                  The address of the USDC token contract.
     * @param thisDestinationDomain_      The destination domain for this contract.
     * @param universalSigValidator_      The address of the universal signature validator contract.
     */
    function initialize(
        address admin_,
        address rebalancerImplementation_,
        address tokenMessenger_,
        address messageTransmitter_,
        address usdcToken_,
        uint32 thisDestinationDomain_,
        address universalSigValidator_
    ) external;

    /**
     * @notice Gets or deploys a Rebalancer contract for the specified owner. Then calls the rebalancer to deposit tokens to a yield.
     *  When destination domain is other than this domain, the deposit is made directly to bridge for rebalance.
     * @param token                   The address of the token being deposited. Currently only USDC is supported.
     * @param onBehalfOf              The address on behalf of which the deposit is made. Also the payer.
     * @param destinationDomain       The destination domain for the deposit.
     * @param tranferArgs             The transfer arguments for the EIP-3009 transfer. See `EIP3009Args` struct for details.
     * @param rebalancingArgs         The rebalancing arguments for the deposit. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     */
    function depositWithAuthorization(
        address token,
        address onBehalfOf,
        uint32 destinationDomain,
        EIP3009Args calldata tranferArgs,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external;

    /**
     * @notice Gets or deploys a Rebalancer contract for the specified owner. Then calls the rebalancer to deposit tokens to a yield.
     *  When destination domain is other than this domain, the deposit is made directly to bridge for rebalance.
     * @dev The rebalancer contract must be pre-approved to spend the tokens on behalf of the owner.
     *  This is used for deposits with contract wallets. USDC token does not support EIP-6492 signature validation.
     *  That means contract wallets that are not deployed yet cannot have their signatures validated by USDC token.
     * @param token                   The address of the token being deposited. Currently only USDC is supported.
     * @param onBehalfOf              The address on behalf of which the deposit is made. Also the payer.
     * @param destinationDomain       The destination domain for the deposit.
     * @param rebalancingArgs         The rebalancing arguments for the deposit. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     */
    function deposit(
        address token,
        address onBehalfOf,
        uint32 destinationDomain,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external;

    /**
     * @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to rebalance tokens from the source domain.
     * @dev In this case the Rebalancer must already exist.
     * @param onBehalfOf              The address on behalf of which the rebalance is made.
     * @param destinationDomain       The destination domain for the rebalance.
     * @param amount                  The amount of tokens being rebalanced. Use MAX_UINT256 for full amount.
     * @param rebalancingArgsFrom     The rebalancing arguments for the rebalance. See `RebalancingArgs` struct for details.
     * @param rebalancingArgsTo       The rebalancing arguments for the destination. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     */
    function rebalanceUsdcSrc(
        address onBehalfOf,
        uint32 destinationDomain,
        uint256 amount,
        RebalancingArgs calldata rebalancingArgsFrom,
        RebalancingArgs calldata rebalancingArgsTo,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external;

    /**
     * @notice Gets or deploys a Rebalancer contract for the specified owner. Then calls the rebalancer to rebalance on destination domain.
     * @param onBehalfOf              The address on behalf of which the rebalance is made.
     * @param rebalancingArgs         The rebalancing arguments for the deposit. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     * @param message                 The message emitted by MessageSent event during depositForBurn.
     * @param attestation             The attestation retrieved from the CCTP API.
     */
    function rebalanceUsdcDst(
        address onBehalfOf,
        RebalancingArgs calldata rebalancingArgs,
        RebalancingConfigArgs calldata rebalancingConfigArgs,
        bytes calldata message,
        bytes calldata attestation
    ) external;

    /**
     * @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to rebalance token on this domain.
     * @dev In this case the Rebalancer must already exist.
     * @param onBehalfOf              The address on behalf of which the rebalance is made.
     * @param rebalancingArgsFrom     The rebalancing arguments for the source pool. See `RebalancingArgs` struct for details.
     * @param rebalancingArgsTo       The rebalancing arguments for the destination pool. See `RebalancingArgs` struct for details.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     */
    function rebalanceThisDomain(
        address onBehalfOf,
        address token,
        RebalancingArgs calldata rebalancingArgsFrom,
        RebalancingArgs calldata rebalancingArgsTo,
        RebalancingConfigArgs calldata rebalancingConfigArgs
    ) external;

    /**
     * @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to fund the owner tokens.
     * @dev In this case the Rebalancer must already exist. Can be called only on Linea domain (11).
     * @param onBehalfOf              The address on behalf of which the funding is made.
     * @param rebalancingConfigArgs   The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
     */
    function fundOwner(address onBehalfOf, RebalancingConfigArgs calldata rebalancingConfigArgs) external;

    /**
     * @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to withdraw tokens from specified pool.
     * @dev In this case the Rebalancer must already exist. Callable by either the owner or by operator on their behalf.
     * @param onBehalfOf     The address on behalf of which the withdrawal is made.
     * @param token          The address of the token being withdrawn.
     * @param pool           The pool identifier from which the tokens are withdrawn.
     * @param poolContract   The address of the pool contract from which the tokens are withdrawn.
     * @param amount         The amount of tokens withdrawn.
     * @param deadline       The timestamp before which the withdrawal is valid. Needed only when done by the operator.
     * @param signature      The signature for the authorization. Necessary only when done by the operator.
     */
    function withdrawFromPool(
        address onBehalfOf,
        address token,
        PoolIdentifier pool,
        address poolContract,
        uint256 amount,
        uint256 deadline,
        bytes calldata signature
    ) external;

    /**
     * @notice Claims rewards from Morpho vaults.
     * @dev Callable by only the owner.
     * @param rewardDistributor   The address of the reward distributor contract.
     * @param rewardToken         The address of the reward token contract.
     * @param claimable           The overall claimable amount of token rewards.
     * @param proof               The Merkle proof array that verifies the claim's validity.
     */
    function claimRewardsMorpho(address rewardDistributor, address rewardToken, uint256 claimable, bytes32[] calldata proof) external;

    /**
     * @notice Claims rewards. Rewards are from Morpho, Merkl, and any other similar reward distributor.
     * @dev Callable by only the owner.
     * @param rewardDistributor    The address of the reward distributor contracts.
     * @param rewardTokens         The addresses of the reward token contracts.
     * @param claimable            The overall claimable amounts of token rewards.
     * @param proofs               The Merkle proof arrays that verify the claims' validity.
     */
    function claimRewards(
        address rewardDistributor,
        address[] calldata rewardTokens,
        uint256[] calldata claimable,
        bytes32[][] calldata proofs
    ) external;

    /**
     * @notice Claims rewards from Aave lending pool.
     * @dev Callable by only the owner.
     * @param rewardsController   The address of the Aave rewards controller contract.
     * @param assets              The list of asset addresses to claim rewards for.
     */
    function claimRewardsAave(address rewardsController, address[] calldata assets) external;

    /**
     * @notice Updates the pool contract addresses to specified pools and associated tokens.
     * @dev This function is only callable by the admin.
     *  The length of `poolContracts`, `pools`, `tokens` and `supported` must be the same.
     * @param destinationDomain   The destination domain for which the pool contracts are being updated.
     * @param poolContracts       The addresses of the pool contracts to be updated.
     * @param pools               The pool identifiers corresponding to the pool contracts.
     * @param tokens              The addresses of the tokens for the pools.
     * @param supported           The boolean array indicating whether the pool contract is supported or not.
     */
    function updatePoolContract(
        uint32 destinationDomain,
        address[] calldata poolContracts,
        PoolIdentifier[] calldata pools,
        address[] calldata tokens,
        bool[] calldata supported
    ) external;

    /**
     * @notice Updates the implementation address of the rebalancer contract.
     * @dev This function is only callable by the admin.
     * @param rebalancerImplementation  The address of the new rebalancer implementation contract.
     */
    function updateRebalancerImplementation(address rebalancerImplementation) external;

    /**
     * @notice Updates the fees for operations on specified domains. Effectively supporting the domain.
     * @dev This function is only callable by the admin.
     *  The value must be a fractional human-readable fee with 3 decimal precision (e.g. `1` if fee = 0.001 tokens with denominator = 1000).
     *  The length of `domains`, `operations` and `fees` must be the same.
     * @param domains              The destination domains for which the fees are being updated.
     * @param operations           The operations for which the fees are being updated.
     * @param fees                 The new fees for the specified domains and operations
     *  > 0 if the operation on destination domain is supported, 0 if not.
     */
    function setDomainFeeForOperation(uint32[] calldata domains, Operation[] calldata operations, uint256[] calldata fees) external;

    /**
     * @notice Updates the rebalance fee percent.
     * @dev This function is only callable by the admin.
     * @param rebalanceFeePercent The new rebalance fee percent.
     */
    function setRebalanceFeePercent(uint256 rebalanceFeePercent) external;

    /**
     * @notice Sets the rebalance cooldown period.
     * @dev This function is only callable by the admin.
     * @param cooldown The new cooldown period for rebalancing in seconds.
     */
    function setRebalanceCooldown(uint256 cooldown) external;

    /**
     * @notice Gets the amount of rebalance fee for a given amount.
     * @dev Ignored on Linea domain.
     * @param amount The amount of tokens for which the rebalance fee is being calculated.
     * @return rebalanceFee The calculated rebalance fee for the specified amount.
     */
    function getRebalanceFee(uint256 amount) external view returns (uint256 rebalanceFee);

    /**
     * @notice Retrieves the transaction fee for operation on a specific destination domain.
     * @param destinationDomain   The destination domain for which the gas transaction fee is being queried.
     * @param token               The address of the token for which the gas transaction fee is being queried.
     * @param operation           The operation for which the gas transaction fee is being queried.
     * @param useThisDomain       Boolean indicating whether to use this domain's fee for same domain operations.
     *  In this case the destinationDomain is ignored.
     */
    function getDestinationDomainGasTransactionFeeForOperation(
        uint32 destinationDomain,
        address token,
        Operation operation,
        bool useThisDomain
    ) external view returns (uint256 gasTransactionFee);

    /**
     * @notice Retrieves the token messenger contract address.
     * @return tokenMessenger The address of the token messenger contract.
     */
    function getUsdcCctpTokenMessenger() external view returns (address tokenMessenger);

    /**
     * @notice Retrieves the message transmitter contract address.
     * @return messageTransmitter The address of the message transmitter contract.
     */
    function getUsdcCctpMessageTransmitter() external view returns (address messageTransmitter);

    /**
     * @notice Retrieves the USDC token contract address.
     * @return usdcToken The address of the USDC token contract.
     */
    function getUsdcToken() external view returns (address usdcToken);

    /**
     * @notice Retrieves the pool and token for given pool contract.
     * @param destinationDomain   The destination domain for which the pool contracts is queried.
     * @param poolContract        The address of the pool contract for which the pool and token are queried.
     * @param pool                The pool identifier for which the contract is being queried.
     * @param token               The address of the token associated with the pool contract being queried.
     * @return supported          True if the pool contract is supported, false otherwise.
     */
    function getPoolContractSupported(
        uint32 destinationDomain,
        address poolContract,
        PoolIdentifier pool,
        address token
    ) external view returns (bool supported);

    /**
     * @notice Checks if the current domain is Linea.
     * @return isLinea True if the current domain is Linea, false otherwise.
     */
    function isLineaDomain() external view returns (bool isLinea);

    /**
     * @notice Retrieves the Linea AAVE USDC aToken contract address.
     * @return aToken The address of the Linea AAVE USDC aToken contract.
     */
    function getLineaUsdcAaveToken() external pure returns (address aToken);
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title IRebalancingManagerCore
 * @author MetaLend
 * @notice Core rebalancing manager interface to read rebalancer implementation
 */
interface IRebalancingManagerCore {
    /**
     * @notice Returns the address of the rebalancer implementation contract
     * @dev Use this with _implementation() in the proxy contract to get the implementation address
     * @return rebalancerImplementation The address of the rebalancer implementation contract
     */
    function getRebalancerImplementation() external view returns (address rebalancerImplementation);
}

File 18 of 23 : Operation.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title Operation
 * @author MetaLend
 * @notice Enum representing operations.
 * @dev This enum is used to identify different supported operations in the system.
 */
enum Operation {
    DEPOSIT,
    WITHDRAW,
    REBALANCE_THIS_DOMAIN,
    REBALANCE_SRC_DOMAIN,
    REBALANCE_DST_DOMAIN,
    FUND_OWNER
}

File 19 of 23 : PoolIdentifier.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title PoolIdentifier
 * @author MetaLend
 * @notice Enum representing different pool identifiers.
 * @dev This enum is used to identify different supported pools in the system.
 */
enum PoolIdentifier {
    AAVE_POOL,
    MORPHO_POOL,
    EULER_POOL
}

File 20 of 23 : PoolToken.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

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

/**
 * @title PoolToken
 * @author MetaLend
 * @notice Struct representing a token in a specific pool.
 * @dev This struct is used to associate a token with its corresponding pool identifier.
 *  Used while mapping pool contracts to their tokens.
 */
struct PoolToken {
    /// @notice The identifier of the pool this token belongs to.
    PoolIdentifier pool;
    /// @notice The address of the underlying token of this pool.
    address token;
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";

/**
 * @title IRebalancer
 * @author MetaLend
 * @notice This interface defines the functions for the Rebalancer contract.
 */
interface IRebalancer {
    /**
     * @notice Emitted when the Rebalancer contract pays the rebalance fee.
     * @param token    The address of the token used to pay the rebalance fee.
     * @param owner    The address of the owner who pays the rebalance fee.
     * @param amount   The amount of tokens paid as the rebalance fee.
     */
    event PayRebalanceFee(address indexed token, address indexed owner, uint256 amount);

    /**
     * @notice Emitted when the Rebalancer contract funds the owner with tokens.
     * @param owner    The address of the owner who receives the tokens.
     * @param token    The address of the token transferred to the owner.
     * @param amount   The amount of tokens transferred to the owner.
     */
    event FundOwner(address indexed owner, address indexed token, uint256 amount);

    /**
     * @notice Emitted when deposit to the pool fails and the Rebalancer contract withdraws tokens to the owner.
     * @param token    The address of the token that failed to deposit.
     * @param amount   The amount of tokens that failed to deposit.
     */
    event OnDepositFailureWithdraw(address indexed token, uint256 amount);

    /// @notice Thrown when caller is not authorized
    error Unauthorized();

    /// @notice Thrown if there is not enough tokens to cover the gas transaction fee
    error InsufficientAmountForGasTransactionFee();

    /// @notice Thrown if there is not enough allowance for transferring tokens
    error ERC20InsufficientAllowance(address token);

    /// @notice Thrown if the funding cannot be completed
    error FundingUnavailable();

    /**
     * @notice Approves the pool and deposits the specified amount of tokens.
     * @dev This function is called by the RebalancingManager contract.
     * @param destinationDomain   The domain to which the tokens will be bridged. For same domain, use MAX_UINT32.
     * @param pool                The identifier of the pool to deposit into.
     * @param poolContract        The address of the pool contract to approve and deposit into.
     * @param token               The address of the token to deposit.
     * @param amount              The amount of tokens to deposit.
     * @param validAfter          The timestamp after which the approval is valid.
     * @param validBefore         The timestamp before which the approval is valid.
     * @param nonce               The nonce for the approval.
     * @param v                   v of approval signature
     * @param r                   r of approval signature
     * @param s                   s of approval signature
     */
    function approveAndDeposit(
        uint32 destinationDomain,
        PoolIdentifier pool,
        address poolContract,
        address token,
        uint256 amount,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Approves the pool and deposits the specified amount of tokens.
     * @dev This function is called by the RebalancingManager contract.
     *  This rebalancer must be pre-approved to spend the tokens.
     * @param destinationDomain   The domain to which the tokens will be bridged. For same domain, use MAX_UINT32.
     * @param pool                The identifier of the pool to deposit into.
     * @param poolContract        The address of the pool contract to approve and deposit into.
     * @param token               The address of the token to deposit.
     * @return amount             The amount of tokens deposited.
     */
    function approveAndDeposit(
        uint32 destinationDomain,
        PoolIdentifier pool,
        address poolContract,
        address token
    ) external returns (uint256 amount);

    /**
     * @notice Receives USDC tokens from bridge, approves the pool contract, and deposits the tokens.
     * @dev This function is called by the RebalancingManager contract.
     * @param pool          The identifier of the pool to deposit into.
     * @param poolContract  The address of the pool contract to approve and deposit into.
     * @param message       The message emitted by MessageSent event during depositForBurn.
     * @param attestation   The attestation retrieved from the CCTP API.
     */
    function receiveUsdcApproveAndDeposit(
        PoolIdentifier pool,
        address poolContract,
        bytes calldata message,
        bytes calldata attestation
    ) external;

    /**
     * @notice Withdraws USDC from pool and deposits it for burning on the specified destination domain.
     * @dev This function is called by the RebalancingManager contract.
     * @param amount              The amount of USDC to withdraw and deposit for burning.
     * @param pool                The identifier of the pool to withdraw from.
     * @param poolContract        The address of the pool contract to withdraw from.
     * @param destinationDomain   The domain to which the USDC will be bridged.
     */
    function withdrawAndDepositForBurnUsdc(uint256 amount, PoolIdentifier pool, address poolContract, uint32 destinationDomain) external;

    /**
     * @notice Withdraws tokens from the source pool and redeposits them into the destination pool on the same domain.
     * @dev This function is called by the RebalancingManager contract.
     * @param token              The address of the token to withdraw and deposit.
     * @param srcPool            The identifier of the source pool to withdraw from.
     * @param srcPoolContract    The address of the source pool contract to withdraw from.
     * @param dstPool            The identifier of the destination pool to deposit into.
     * @param dstPoolContract    The address of the destination pool contract to deposit into.
     */
    function withdrawAndRedeposit(
        address token,
        PoolIdentifier srcPool,
        address srcPoolContract,
        PoolIdentifier dstPool,
        address dstPoolContract
    ) external;

    /**
     * @notice Funds the owner with aTokens (AAVE).
     * @dev This function is called by the RebalancingManager contract.
     * @param fundingCap The maximum configured spendable cap in USDC based on aTokens (AAVE) in user's wallet on Linea domain (11).
     */
    function fundOwnerWithUsdcAaveToken(uint256 fundingCap) external;

    /**
     * @notice Withdraws the specified amount of tokens from pool and transfers them to the owner.
     * @dev This function is called by the RebalancingManager contract.
     *  If amount is MAX_UINT256, it withdraws all available tokens.
     * @param token          The address of the token to withdraw.
     * @param pool           The identifier of the pool to withdraw from.
     * @param poolContract   The address of the pool contract to withdraw from.
     * @param amount         The amount of tokens to withdraw.
     * @param payFee         Whether to pay the gas transaction fee. This depends if the tx is called on behalf of the owner or not.
     */
    function withdrawFromPoolToOwner(address token, PoolIdentifier pool, address poolContract, uint256 amount, bool payFee) external;

    /**
     * @notice Claims rewards from Morpho vaults.
     * @dev This function is called by the RebalancingManager contract.
     * @param rewardDistributor   The address of the reward distributor contract.
     * @param rewardToken         The address of the reward token contract.
     * @param claimable           The overall claimable amount of token rewards.
     * @param proof               The Merkle proof array that verifies the claim's validity.
     */
    function claimRewardsMorpho(address rewardDistributor, address rewardToken, uint256 claimable, bytes32[] calldata proof) external;

    /**
     * @notice Claims rewards. Rewards are from Morpho, Merkl, and any other similar reward distributor.
     * @dev This function is called by the RebalancingManager contract. The rewardToken array must be unique addresses.
     * @param rewardDistributor   The address of the reward distributor contract.
     * @param rewardToken         The addresses of the reward token contracts.
     * @param claimable           The overall claimable amounts of token rewards.
     * @param proof               The Merkle proof arrays that verify the claims' validity.
     */
    function claimRewards(
        address rewardDistributor,
        address[] calldata rewardToken,
        uint256[] calldata claimable,
        bytes32[][] calldata proof
    ) external;

    /**
     * @notice Claims rewards from Aave lending pool.
     * @dev This function is called by the RebalancingManager contract.
     * @param rewardsController   The address of the Aave rewards controller contract.
     * @param assets              The list of asset addresses to claim rewards for.
     */
    function claimRewardsAave(address rewardsController, address[] calldata assets) external;

    /**
     * @notice Returns the address of the owner of the Rebalancer contract.
     * @return owner The address of the owner.
     */
    function getOwner() external view returns (address owner);
}

File 22 of 23 : RebalancerCore.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
 * @title RebalancerCore
 * @author MetaLend
 * @notice Abstract contract that provides default storage layout for proxy and implementation contract
 */
abstract contract RebalancerCore {
    /// @notice Manager contract providing rebalancing functionality and access control and implementation
    address internal _rebalancingManager;

    /// @notice Address of the owner of this rebalancer
    address internal _owner;

    /**
     * @notice Emitted when the rebalancer is initialized
     * @param rebalancer   Address of this rebalancer
     * @param manager      Address of the rebalancing manager
     * @param owner        Address of the owner of this rebalancer
     */
    event RebalancerInitialized(address indexed rebalancer, address indexed manager, address indexed owner);
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
import {IRebalancingManagerCore} from "../manager/IRebalancingManagerCore.sol";
import {RebalancerCore} from "./RebalancerCore.sol";

/**
 * @title RebalancerProxy
 * @author MetaLend
 * @notice Proxy contract for the rebalancer
 * @dev This contract is created to be used as a proxy for the rebalancer implementation managed by the rebalancing manager
 */
contract RebalancerProxy is RebalancerCore, Proxy {
    /**
     * @notice Constructor for the RebalancerProxy contract
     * @param manager_   The address of the rebalancing manager
     * @param owner_     The address of the owner of the rebalancer
     */
    constructor(address manager_, address owner_) {
        _rebalancingManager = manager_;
        _owner = owner_;
        emit RebalancerInitialized(address(this), _rebalancingManager, _owner);
    }

    /**
     * @notice Returns the address of the implementation contract
     * @dev This is used by the proxy `_fallback` function to delegate calls to the implementation contract
     */
    function _implementation() internal view override returns (address implementation) {
        return IRebalancingManagerCore(_rebalancingManager).getRebalancerImplementation();
    }
}

Settings
{
  "metadata": {
    "bytecodeHash": "none",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {
    "contracts/manager/RebalancingManager.sol": {
      "FeesLib": "0xdceac80c8429140450b45697aae1ccb8dc921ccb"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"Create2EmptyBytecode","type":"error"},{"inputs":[],"name":"Create2FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"Create2InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"}],"name":"DestinationDomainNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"InvalidDeadline","type":"error"},{"inputs":[],"name":"InvalidInputLength","type":"error"},{"inputs":[],"name":"InvalidOperation","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"InvalidSignature","type":"error"},{"inputs":[{"internalType":"address","name":"poolContract","type":"address"}],"name":"PoolContractNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"lastRebalanceTimestamp","type":"uint256"},{"internalType":"uint256","name":"rebalanceCooldown","type":"uint256"}],"name":"RebalanceCooldownNotPassed","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"RebalancerNotFound","type":"error"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"SignatureUsedAlready","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositWithAuthorization","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"indexed":true,"internalType":"address","name":"poolContract","type":"address"},{"indexed":false,"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"supported","type":"bool"}],"name":"PoolContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RebalanceDst","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"rebalanceFeePercent","type":"uint256"}],"name":"RebalanceFeePercentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"indexed":true,"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RebalanceSrc","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"}],"name":"RebalancerDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancerImplementation","type":"address"}],"name":"RebalancerImplementationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"lastRebalanceTimestamp","type":"uint256"}],"name":"RebalancerLastRebalanceTimestampUpdated","type":"event"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINEA_DOMAIN_ID","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REBALANCING_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAWAL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardDistributor","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"claimable","type":"uint256[]"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardsController","type":"address"},{"internalType":"address[]","name":"assets","type":"address[]"}],"name":"claimRewardsAave","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardDistributor","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"claimRewardsMorpho","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct EIP3009Args","name":"tranferArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"depositWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"fundOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdmin","outputs":[{"internalType":"address","name":"admin","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum Operation","name":"operation","type":"uint8"},{"internalType":"bool","name":"useThisDomain","type":"bool"}],"name":"getDestinationDomainGasTransactionFeeForOperation","outputs":[{"internalType":"uint256","name":"gasTransactionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLineaUsdcAaveToken","outputs":[{"internalType":"address","name":"aToken","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"address","name":"poolContract","type":"address"},{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"token","type":"address"}],"name":"getPoolContractSupported","outputs":[{"internalType":"bool","name":"supported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getRebalanceFee","outputs":[{"internalType":"uint256","name":"rebalanceFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRebalancerImplementation","outputs":[{"internalType":"address","name":"rebalancerImplementation","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdcCctpMessageTransmitter","outputs":[{"internalType":"address","name":"messageTransmitter","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdcCctpTokenMessenger","outputs":[{"internalType":"address","name":"tokenMessenger","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdcToken","outputs":[{"internalType":"address","name":"usdcToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin_","type":"address"},{"internalType":"address","name":"rebalancerImplementation_","type":"address"},{"internalType":"address","name":"tokenMessenger_","type":"address"},{"internalType":"address","name":"messageTransmitter_","type":"address"},{"internalType":"address","name":"usdcToken_","type":"address"},{"internalType":"uint32","name":"thisDestinationDomain_","type":"uint32"},{"internalType":"address","name":"universalSigValidator_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isLineaDomain","outputs":[{"internalType":"bool","name":"isLinea","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"isOpr","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalanceCooldown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsFrom","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsTo","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"rebalanceThisDomain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"attestation","type":"bytes"}],"name":"rebalanceUsdcDst","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsFrom","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsTo","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"rebalanceUsdcSrc","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"rebalancers","outputs":[{"internalType":"address","name":"rebalancer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"roles","outputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"domains","type":"uint32[]"},{"internalType":"enum Operation[]","name":"operations","type":"uint8[]"},{"internalType":"uint256[]","name":"fees","type":"uint256[]"}],"name":"setDomainFeeForOperation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cooldown","type":"uint256"}],"name":"setRebalanceCooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rebalancingFeePercent","type":"uint256"}],"name":"setRebalanceFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"thisDestinationDomain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"universalSigValidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"address[]","name":"poolContracts","type":"address[]"},{"internalType":"enum PoolIdentifier[]","name":"pools","type":"uint8[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"bool[]","name":"supported","type":"bool[]"}],"name":"updatePoolContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancerImplementation","type":"address"}],"name":"updateRebalancerImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"withdrawFromPool","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50614c3b806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620003195760003560e01c8063923ca9a411620001a5578063b3de272d11620000f5578063db1c4ded11620000a3578063f95aea4c116200007a578063f95aea4c1462000803578063fa62ebaa146200081a578063fdfd5a25146200083957600080fd5b8063db1c4ded14620007a8578063e9e833e614620007c3578063eb39e9ba14620007da57600080fd5b8063b68a659211620000d8578063b68a6592146200076b578063bd2f7172146200078a578063d73792a9146200079d57600080fd5b8063b3de272d146200074a578063b5fa9dd2146200075457600080fd5b8063a44d57a01162000153578063ab08c5241162000136578063ab08c5241462000705578063ac8a584a146200071c578063ac98d9b0146200073357600080fd5b8063a44d57a014620006ad578063a9fda0eb14620006e657600080fd5b80639358a04111620001885780639358a041146200065c5780639870d7fe146200067357806399374642146200068a57600080fd5b8063923ca9a414620005e857806392765861146200064557600080fd5b8063468f8f52116200026d5780636e9960c3116200021b5780638057739911620001f25780638057739914620005a157806381ca52cf14620005c857806382e5bec314620005df57600080fd5b80636e9960c31462000554578063704b6c0214620005735780637e22e6eb146200058a57600080fd5b806351f0caef116200025057806351f0caef14620004d85780636aa9541414620004e25780636d70f7ae14620004f957600080fd5b8063468f8f5214620004aa57806351c2569814620004c157600080fd5b806324703dbc11620002cb5780633b44390a11620002ae5780633b44390a14620003f65780633f1b7020146200043657806344f2c5aa146200044d57600080fd5b806324703dbc14620003c85780633acbc1e914620003df57600080fd5b80630c3ee3ac11620003005780630c3ee3ac146200034e5780631ffe1422146200037857806320606b7014620003a057600080fd5b80630538153d146200031e578063091373601462000337575b600080fd5b620003356200032f36600462003610565b62000861565b005b62000335620003483660046200369e565b62000b72565b620003656200035f366004620036ec565b62000c34565b6040519081526020015b60405180910390f35b6200038f620003893660046200375b565b62000d2c565b60405190151581526020016200036f565b620003657f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b62000335620003d936600462003807565b62000d6c565b62000335620003f036600462003860565b62000e0e565b60065473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016200036f565b6200033562000447366004620038b4565b62000f77565b60408051808201909152601481527f4d6574614c656e6420526562616c616e63696e67000000000000000000000000602090910152620003657f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d3181565b62000335620004bb36600462003949565b6200103f565b62000335620004d2366004620039ac565b62001100565b620003656103e881565b62000335620004f336600462003a51565b620012fd565b6200038f6200050a36600462003949565b73ffffffffffffffffffffffffffffffffffffffff166000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291490565b60005473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200058436600462003949565b620015de565b620003356200059b36600462003b37565b6200174e565b600f54620005b29063ffffffff1681565b60405163ffffffff90911681526020016200036f565b62000335620005d93660046200369e565b620018fa565b620005b2600b81565b60408051808201909152600581527f332e302e30000000000000000000000000000000000000000000000000000000602090910152620003657fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a81565b620003356200065636600462003bdb565b62001951565b620003356200066d36600462003ca0565b62001b1e565b620003356200068436600462003949565b62001ccb565b620003656200069b36600462003949565b60016020526000908152604090205481565b62000410620006be36600462003949565b60086020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200071636600462003d12565b62001e33565b620003356200072d36600462003949565b62002031565b620003356200074436600462003dc7565b620020d4565b6200036560075481565b62000365620007653660046200369e565b6200217c565b60045473ffffffffffffffffffffffffffffffffffffffff1662000410565b6200038f600f5463ffffffff16600b1490565b62000365620186a081565b73374d7860c4f2f604de0191298dd393703cce84f362000410565b62000335620007d436600462003e3e565b620021dc565b600f546200041090640100000000900473ffffffffffffffffffffffffffffffffffffffff1681565b620003356200081436600462003ee2565b6200228a565b60035473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003657fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f046081565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620008ca576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000620008d786620024d0565b9050620008eb604084016020850162003949565b73ffffffffffffffffffffffffffffffffffffffff1662000913604086016020870162003949565b73ffffffffffffffffffffffffffffffffffffffff160362000961576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54620009979063ffffffff1662000981604087016020880162003949565b62000990602088018862003f6c565b886200254e565b600f54620009b0908490849063ffffffff1688620025df565b620009bc86836200261f565b620009c881866200276a565b73ffffffffffffffffffffffffffffffffffffffff811663fe4c85bc86620009f4602088018862003f6c565b62000a066040890160208a0162003949565b62000a15602089018962003f6c565b62000a2760408a0160208b0162003949565b6040518663ffffffff1660e01b815260040162000a4995949392919062003fd0565b600060405180830381600087803b15801562000a6457600080fd5b505af115801562000a79573d6000803e3d6000fd5b5062000a8d92505050602085018562003f6c565b600281111562000aa15762000aa162003f8a565b600f5460405173ffffffffffffffffffffffffffffffffffffffff888116825263ffffffff909216918916907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a462000b09602084018462003f6c565b600281111562000b1d5762000b1d62003f8a565b60405173ffffffffffffffffffffffffffffffffffffffff87811682528816907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a3505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000bc4576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e881111562000c01576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e81905560405181907f267f35adfb1c3a400666ab8eb7414e5d1cda481952b8b8fcad28d003a7874c6890600090a250565b600073dceac80c8429140450b45697aae1ccb8dc921ccb6337404955601060008562000c61578862000c6b565b600f5463ffffffff165b63ffffffff1663ffffffff168152602001908152602001600020600062000c92876200286a565b815260200190815260200160002054866040518363ffffffff1660e01b815260040162000cdf92919091825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b602060405180830381865af415801562000cfd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d23919062004028565b95945050505050565b63ffffffff841660009081526012602052604081208162000d4f8686866200289c565b815260208101919091526040016000205460ff1695945050505050565b600062000d7933620028d4565b6040517f24703dbc00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8216906324703dbc9062000dd4908790879087906004016200409a565b600060405180830381600087803b15801562000def57600080fd5b505af115801562000e04573d6000803e3d6000fd5b5050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462000e77576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060810135158062000e99575062000e97600f5463ffffffff16600b1490565b155b1562000ed1576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000edd82826200261f565b600062000eea83620024d0565b6040517fbc0348e50000000000000000000000000000000000000000000000000000000081526060840135600482015290915073ffffffffffffffffffffffffffffffffffffffff82169063bc0348e590602401600060405180830381600087803b15801562000f5957600080fd5b505af115801562000f6e573d6000803e3d6000fd5b50505050505050565b62000f82876200293a565b6002805473ffffffffffffffffffffffffffffffffffffffff9788167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054968816968216969096179095556004805494871694861694909417909355600680549286169290941691909117909255600f805491909316640100000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911663ffffffff9092169190911717905550565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001091576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517fb08982e7405e1df5c0f03355316cd1611d894a67dbaed99e5fa87307f54f171190600090a250565b60006200110d89620024d0565b600f54909150620011279063ffffffff1687898b6200254e565b6000829003620012185773ffffffffffffffffffffffffffffffffffffffff8916331462001181576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620011de908b908b908b908b90600090600401620040cc565b600060405180830381600087803b158015620011f957600080fd5b505af11580156200120e573d6000803e3d6000fd5b50505050620012f2565b620012608984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508d92508b91508a90508962002ace565b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620012bd908b908b908b908b90600190600401620040cc565b600060405180830381600087803b158015620012d857600080fd5b505af1158015620012ed573d6000803e3d6000fd5b505050505b505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200134f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86851415806200135f5750868314155b806200136b5750868114155b15620013a3576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660005b81811015620015d1576000620014408b8b84818110620013cb57620013cb62004119565b9050602002016020810190620013e2919062003949565b8a8a85818110620013f757620013f762004119565b90506020020160208101906200140e919062003f6c565b89898681811062001423576200142362004119565b90506020020160208101906200143a919062003949565b6200289c565b905084848381811062001457576200145762004119565b90506020020160208101906200146e919062004148565b63ffffffff8d166000908152601260209081526040808320858452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558a8a83818110620014d057620014d062004119565b9050602002016020810190620014e7919062003949565b73ffffffffffffffffffffffffffffffffffffffff167f5310df464b3c7a93f2147cf6f6d5eec46c7b7be22fc612af20e6f1a301e1f6088d8b8b8681811062001534576200153462004119565b90506020020160208101906200154b919062003f6c565b8a8a8781811062001560576200156062004119565b905060200201602081019062001577919062003949565b8989888181106200158c576200158c62004119565b9050602002016020810190620015a3919062004148565b604051620015b5949392919062004168565b60405180910390a250620015c981620041e0565b9050620013a7565b5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001630576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116620016d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f416363657373436f6e74726f6c3a206e65772061646d696e206973207468652060448201527f7a65726f2061646472657373000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f9190a35050565b60005473ffffffffffffffffffffffffffffffffffffffff163314620017a0576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483141580620017b05750848114155b15620017e8576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b8181101562000e045760008888838181106200180c576200180c62004119565b90506020020160208101906200182391906200421b565b905060008787848181106200183c576200183c62004119565b905060200201602081019062001853919062004239565b905060008686858181106200186c576200186c62004119565b90506020020135905063ffffffff80168363ffffffff1603620018bb576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff831660009081526010602052604081208291620018dd856200286a565b8152602081019190915260400160002055505050600101620017ec565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200194c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600755565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620019ba576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54600654620019ec918891889163ffffffff169073ffffffffffffffffffffffffffffffffffffffff16620025df565b620019f887866200261f565b600062001a0588620028d4565b905073ffffffffffffffffffffffffffffffffffffffff811663c2543bb362001a3260208a018a62003f6c565b62001a4460408b0160208c0162003949565b888888886040518763ffffffff1660e01b815260040162001a6b96959493929190620042a0565b600060405180830381600087803b15801562001a8657600080fd5b505af115801562001a9b573d6000803e3d6000fd5b5062001aaf92505050602088018862003f6c565b600281111562001ac35762001ac362003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff9182168152908a16907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a35050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001b87576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001b98848484888a62002d97565b600f5490915060009073ffffffffffffffffffffffffffffffffffffffff8316906371f6876a9063ffffffff88811691161462001bd6578662001bdc565b63ffffffff5b62001beb602088018862003f6c565b62001bfd6040890160208a0162003949565b8b6040518563ffffffff1660e01b815260040162001c1f949392919062004303565b6020604051808303816000875af115801562001c3f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001c65919062004028565b9050808673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f6260405160405180910390a450505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001d1d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811662001dc2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f416363657373436f6e74726f6c3a206f70657261746f7220697320746865207a60448201527f65726f20616464726573730000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff81166000818152600160205260408082207f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9299055517fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9190a250565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001e9c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001ead858484898b62002d97565b90506000808062001f028962001ec760808a018a6200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062002e3f92505050565b600f54929550909350915073ffffffffffffffffffffffffffffffffffffffff85169063368815ca9063ffffffff8b811691161462001f42578962001f48565b63ffffffff5b62001f5760208a018a62003f6c565b62001f6960408b0160208c0162003949565b8e8c600001358d602001358e604001358f606001358c8c8c6040518c63ffffffff1660e01b815260040162001fa99b9a99989796959493929190620043b6565b600060405180830381600087803b15801562001fc457600080fd5b505af115801562001fd9573d6000803e3d6000fd5b50506040518935925073ffffffffffffffffffffffffffffffffffffffff808d1692508d16907fa87989ef6d00dca941c1e1d69df9aab1058d404c2a8996492375a61f1d1d3b4990600090a450505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002083576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600081815260016020526040808220829055517f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d9190a250565b6000620020e133620028d4565b6040517fac98d9b000000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063ac98d9b09062002140908990899089908990899060040162004481565b600060405180830381600087803b1580156200215b57600080fd5b505af115801562002170573d6000803e3d6000fd5b50505050505050505050565b6000600e54600014806200218e575081155b80620021a85750620021a8600f5463ffffffff16600b1490565b15620021b657506000919050565b620186a0600e5483620021ca9190620044ce565b620021d69190620044e8565b92915050565b6000620021e933620028d4565b6040517fe9e833e600000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063e9e833e6906200224c908b908b908b908b908b908b908b9060040162004524565b600060405180830381600087803b1580156200226757600080fd5b505af11580156200227c573d6000803e3d6000fd5b505050505050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620022f3576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200230087620024d0565b90506200230d8662002ea2565b600f546200235b9063ffffffff166200232d604087016020880162003949565b6200233c602088018862003f6c565b60065473ffffffffffffffffffffffffffffffffffffffff166200254e565b600654620023859084908490899073ffffffffffffffffffffffffffffffffffffffff16620025df565b6200239187836200261f565b600654620023b790829073ffffffffffffffffffffffffffffffffffffffff166200276a565b73ffffffffffffffffffffffffffffffffffffffff81166336fd374586620023e3602088018862003f6c565b620023f56040890160208a0162003949565b8a6040518563ffffffff1660e01b815260040162002417949392919062004651565b600060405180830381600087803b1580156200243257600080fd5b505af115801562002447573d6000803e3d6000fd5b506200245b92505050602085018562003f6c565b60028111156200246f576200246f62003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff918216815263ffffffff8916918a16907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a450505050505050565b73ffffffffffffffffffffffffffffffffffffffff808216600090815260086020526040902054168062002549576040517f2b1991ae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401620016d0565b919050565b63ffffffff8416600090815260126020526040812081620025718686866200289c565b815260208101919091526040016000205460ff16905080620025d8576040517fb1b1f01400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401620016d0565b5050505050565b620025ec84848462002f30565b620026198262002603604087016020880162003949565b62002612602088018862003f6c565b846200254e565b50505050565b60006060820135156200268f5762002638828062004698565b62002647602085018562004698565b62002656604087018762004698565b876060013560405160200162002673979695949392919062004780565b60405160208183030381529060405280519060200120620026e7565b6200269b828062004698565b620026aa602085018562004698565b620026b9604087018762004698565b604051602001620026d096959493929190620047d7565b604051602081830303815290604052805190602001205b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c902090915062002619846200272d60808601866200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925062003170915050565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260116020908152604080832093851683529290522054600754620027ac908262004819565b42101562002801574260075482620027c5919062004819565b6040517f4be8f90500000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401620016d0565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526011602090815260408083209487168084529490915280822042908190559051909392917fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd4080590991a4505050565b6000816040516020016200287f91906200482f565b604051602081830303815290604052805190602001209050919050565b6000838383604051602001620028b59392919062004853565b6040516020818303038152906040528051906020012090509392505050565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152600860205260408120549091166200290e57620021d6826200325b565b5073ffffffffffffffffffffffffffffffffffffffff9081166000908152600860205260409020541690565b60005473ffffffffffffffffffffffffffffffffffffffff1615620029e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20616c726561647920696e6960448201527f7469616c697a65640000000000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff811662002a87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401620016d0565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b8042111562002b0d576040517f277b2d7400000000000000000000000000000000000000000000000000000000815260048101829052602401620016d0565b600c8560405162002b1f9190620048d9565b9081526040519081900360200190205460ff161562002b6e57846040517fbf42d003000000000000000000000000000000000000000000000000000000008152600401620016d0919062004943565b6001600c8660405162002b829190620048d9565b90815260408051918290036020908101832080549415157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909516949094179093557fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f04608383015273ffffffffffffffffffffffffffffffffffffffff87811683830152861660608301526080820185905260a08083018590528151808403909101815260c090920190528051910120600062002d37604080518082018252601481527f4d6574614c656e6420526562616c616e63696e6700000000000000000000000060209182015281518083018352600581527f332e302e300000000000000000000000000000000000000000000000000000009082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d31818401527fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a60608201524660808201523060a0808301919091528351808303909101815260c0909101909252815191012090565b6040517f1901000000000000000000000000000000000000000000000000000000000000602082015260228101919091526042810183905260620160405160208183030381529060405280519060200120905062000e0488888362003170565b600062002da785858885620025df565b62002db383856200261f565b600f5463ffffffff87811691161462002e265762002dd18662002ea2565b60065473ffffffffffffffffffffffffffffffffffffffff83811691161462002e26576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62002e3183620028d4565b905062000d2381836200341c565b6000806000835160411462002e865784846040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b5050506020810151604082015160609092015160001a93909250565b600f5463ffffffff8281169116148062002eec575063ffffffff811660009081526010602052604081209062002ed960046200286a565b8152602001908152602001600020546000145b1562002f2d576040517fd3616c3700000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401620016d0565b50565b62002f3f602083018362004698565b905062002f4d838062004698565b905014158062002f7a575062002f67604083018362004698565b905062002f75838062004698565b905014155b1562002fb2576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062002fc0838062004698565b9050905060005b818110156200313d57600062002fde858062004698565b8381811062002ff15762002ff162004119565b905060200201602081019062003008919062003f6c565b905060006200301b602087018762004698565b848181106200302e576200302e62004119565b905060200201602081019062003045919062003949565b9050600062003058604088018862004698565b858181106200306b576200306b62004119565b90506020020160208101906200308291906200421b565b905062003093602089018962003f6c565b6002811115620030a757620030a762003f8a565b836002811115620030bc57620030bc62003f8a565b148015620031065750620030d76040890160208a0162003949565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b80156200311e57508563ffffffff168163ffffffff16145b156200312e575050505050505050565b83600101935050505062002fc7565b506040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f546040517f98ef1ed800000000000000000000000000000000000000000000000000000000815264010000000090910473ffffffffffffffffffffffffffffffffffffffff16906398ef1ed890620031d39086908590879060040162004991565b6020604051808303816000875af1158015620031f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620032199190620049c8565b620032565782826040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b505050565b600080604051806020016200327090620035b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604081815230602083015273ffffffffffffffffffffffffffffffffffffffff8616908201529091506000908290606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526200330d9291602001620049e8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606087901b16602083015291506000906034016040516020818303038152906040528051906020012090506200339660008284620034ce565b73ffffffffffffffffffffffffffffffffffffffff86811660008181526008602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948616948517905551939750919290917f3b2ac627f2dda3cc16038f312aeeda34474e3770c24737309735bb3911bdcf8291a3505050919050565b60075473ffffffffffffffffffffffffffffffffffffffff8084166000908152601160209081526040808320938616835292905220546200345e919062004819565b421015620034ca5773ffffffffffffffffffffffffffffffffffffffff80831660008181526011602090815260408083209486168084529490915280822082905551909291907fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd40805909908490a45b5050565b60008347101562003515576040517fe4bbecac00000000000000000000000000000000000000000000000000000000815247600482015260248101859052604401620016d0565b815160000362003551576040517f4ca249dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116620035aa576040517f741752c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b6102138062004a1c83390190565b803573ffffffffffffffffffffffffffffffffffffffff811681146200254957600080fd5b600060408284031215620035f757600080fd5b50919050565b600060a08284031215620035f757600080fd5b600080600080600060e086880312156200362957600080fd5b6200363486620035bf565b94506200364460208701620035bf565b9350620036558760408801620035e4565b9250620036668760808801620035e4565b915060c086013567ffffffffffffffff8111156200368357600080fd5b6200369188828901620035fd565b9150509295509295909350565b600060208284031215620036b157600080fd5b5035919050565b803563ffffffff811681146200254957600080fd5b8035600681106200254957600080fd5b801515811462002f2d57600080fd5b600080600080608085870312156200370357600080fd5b6200370e85620036b8565b93506200371e60208601620035bf565b92506200372e60408601620036cd565b915060608501356200374081620036dd565b939692955090935050565b8035600381106200254957600080fd5b600080600080608085870312156200377257600080fd5b6200377d85620036b8565b93506200378d60208601620035bf565b92506200379d604086016200374b565b9150620037ad60608601620035bf565b905092959194509250565b60008083601f840112620037cb57600080fd5b50813567ffffffffffffffff811115620037e457600080fd5b6020830191508360208260051b85010111156200380057600080fd5b9250929050565b6000806000604084860312156200381d57600080fd5b6200382884620035bf565b9250602084013567ffffffffffffffff8111156200384557600080fd5b6200385386828701620037b8565b9497909650939450505050565b600080604083850312156200387457600080fd5b6200387f83620035bf565b9150602083013567ffffffffffffffff8111156200389c57600080fd5b620038aa85828601620035fd565b9150509250929050565b600080600080600080600060e0888a031215620038d057600080fd5b620038db88620035bf565b9650620038eb60208901620035bf565b9550620038fb60408901620035bf565b94506200390b60608901620035bf565b93506200391b60808901620035bf565b92506200392b60a08901620036b8565b91506200393b60c08901620035bf565b905092959891949750929550565b6000602082840312156200395c57600080fd5b620035aa82620035bf565b60008083601f8401126200397a57600080fd5b50813567ffffffffffffffff8111156200399357600080fd5b6020830191508360208285010111156200380057600080fd5b60008060008060008060008060e0898b031215620039c957600080fd5b620039d489620035bf565b9750620039e460208a01620035bf565b9650620039f460408a016200374b565b955062003a0460608a01620035bf565b94506080890135935060a0890135925060c089013567ffffffffffffffff81111562003a2f57600080fd5b62003a3d8b828c0162003967565b999c989b5096995094979396929594505050565b600080600080600080600080600060a08a8c03121562003a7057600080fd5b62003a7b8a620036b8565b985060208a013567ffffffffffffffff8082111562003a9957600080fd5b62003aa78d838e01620037b8565b909a50985060408c013591508082111562003ac157600080fd5b62003acf8d838e01620037b8565b909850965060608c013591508082111562003ae957600080fd5b62003af78d838e01620037b8565b909650945060808c013591508082111562003b1157600080fd5b5062003b208c828d01620037b8565b915080935050809150509295985092959850929598565b6000806000806000806060878903121562003b5157600080fd5b863567ffffffffffffffff8082111562003b6a57600080fd5b62003b788a838b01620037b8565b9098509650602089013591508082111562003b9257600080fd5b62003ba08a838b01620037b8565b9096509450604089013591508082111562003bba57600080fd5b5062003bc989828a01620037b8565b979a9699509497509295939492505050565b600080600080600080600060c0888a03121562003bf757600080fd5b62003c0288620035bf565b965062003c138960208a01620035e4565b9550606088013567ffffffffffffffff8082111562003c3157600080fd5b62003c3f8b838c01620035fd565b965060808a013591508082111562003c5657600080fd5b62003c648b838c0162003967565b909650945060a08a013591508082111562003c7e57600080fd5b5062003c8d8a828b0162003967565b989b979a50959850939692959293505050565b600080600080600060c0868803121562003cb957600080fd5b62003cc486620035bf565b945062003cd460208701620035bf565b935062003ce460408701620036b8565b925062003cf58760608801620035e4565b915060a086013567ffffffffffffffff8111156200368357600080fd5b60008060008060008060e0878903121562003d2c57600080fd5b62003d3787620035bf565b955062003d4760208801620035bf565b945062003d5760408801620036b8565b9350606087013567ffffffffffffffff8082111562003d7557600080fd5b62003d838a838b01620035fd565b945062003d948a60808b01620035e4565b935060c089013591508082111562003dab57600080fd5b5062003dba89828a01620035fd565b9150509295509295509295565b60008060008060006080868803121562003de057600080fd5b62003deb86620035bf565b945062003dfb60208701620035bf565b935060408601359250606086013567ffffffffffffffff81111562003e1f57600080fd5b62003e2d88828901620037b8565b969995985093965092949392505050565b60008060008060008060006080888a03121562003e5a57600080fd5b62003e6588620035bf565b9650602088013567ffffffffffffffff8082111562003e8357600080fd5b62003e918b838c01620037b8565b909850965060408a013591508082111562003eab57600080fd5b62003eb98b838c01620037b8565b909650945060608a013591508082111562003ed357600080fd5b5062003c8d8a828b01620037b8565b600080600080600080610100878903121562003efd57600080fd5b62003f0887620035bf565b955062003f1860208801620036b8565b94506040870135935062003f308860608901620035e4565b925062003f418860a08901620035e4565b915060e087013567ffffffffffffffff81111562003f5e57600080fd5b62003dba89828a01620035fd565b60006020828403121562003f7f57600080fd5b620035aa826200374b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6003811062003fcc5762003fcc62003f8a565b9052565b73ffffffffffffffffffffffffffffffffffffffff868116825260a082019062003ffe602084018862003fb9565b808616604084015262004015606084018662003fb9565b8084166080840152509695505050505050565b6000602082840312156200403b57600080fd5b5051919050565b8183526000602080850194508260005b858110156200408f5773ffffffffffffffffffffffffffffffffffffffff6200407b83620035bf565b168752958201959082019060010162004052565b509495945050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152600062000d2360408301848662004042565b73ffffffffffffffffffffffffffffffffffffffff868116825260a0820190620040fa602084018862003fb9565b9490941660408201526060810192909252151560809091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156200415b57600080fd5b8135620035aa81620036dd565b63ffffffff851681526080810162004184602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff84166040830152821515606083015295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203620042145762004214620041b1565b5060010190565b6000602082840312156200422e57600080fd5b620035aa82620036b8565b6000602082840312156200424c57600080fd5b620035aa82620036cd565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b620042ac818862003fb9565b73ffffffffffffffffffffffffffffffffffffffff86166020820152608060408201526000620042e160808301868862004257565b8281036060840152620042f681858762004257565b9998505050505050505050565b63ffffffff85168152608081016200431f602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff808516604084015280841660608401525095945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126200438457600080fd5b83018035915067ffffffffffffffff821115620043a057600080fd5b6020019150368190038213156200380057600080fd5b63ffffffff8c1681526101608101620043d3602083018d62003fb9565b73ffffffffffffffffffffffffffffffffffffffff9a8b1660408301529890991660608a0152608089019690965260a088019490945260c087019290925260e086015260ff166101008501526101208401526101409092019190915292915050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156200446857600080fd5b8260051b80836020870137939093016020019392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152620044c360808301848662004435565b979650505050505050565b8082028115828204841417620021d657620021d6620041b1565b6000826200451f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b73ffffffffffffffffffffffffffffffffffffffff88168152600060206080818401526200455760808401898b62004042565b83810360408501526200456c81888a62004435565b84810360608601528581529050818101600586811b830184018860005b898110156200463d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c3603018112620045ec57600080fd5b8b01878101903567ffffffffffffffff8111156200460957600080fd5b80861b36038213156200461b57600080fd5b6200462885828462004435565b96890196945050509086019060010162004589565b50909e9d5050505050505050505050505050565b8481526080810162004667602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff8416604083015263ffffffff8316606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112620046ce57600080fd5b83018035915067ffffffffffffffff821115620046ea57600080fd5b6020019150600581901b36038213156200380057600080fd5b8183526000602080850194508260005b858110156200408f5762004732876200472c846200374b565b62003fb9565b958201959082019060010162004713565b8183526000602080850194508260005b858110156200408f5763ffffffff6200476c83620036b8565b168752958201959082019060010162004753565b6080815260006200479660808301898b62004703565b8281036020840152620047ab81888a62004042565b90508281036040840152620047c281868862004743565b91505082606083015298975050505050505050565b606081526000620047ed60608301888a62004703565b82810360208401526200480281878962004042565b90508281036040840152620042f681858762004743565b80820180821115620021d657620021d6620041b1565b60006006831062004844576200484462003f8a565b5060f89190911b815260010190565b60007fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808660601b1683526003851062004891576200489162003f8a565b60f89490941b60148301525060609190911b9091166015820152602901919050565b60005b83811015620048d0578181015183820152602001620048b6565b50506000910152565b60008251620048ed818460208701620048b3565b9190910192915050565b6000815180845262004911816020860160208601620048b3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000620035aa6020830184620048f7565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000620049896040830184620048f7565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600062000d236060830184620048f7565b600060208284031215620049db57600080fd5b8151620035aa81620036dd565b60008351620049fc818460208801620048b3565b83519083019062004a12818360208801620048b3565b0194935050505056fe608060405234801561001057600080fd5b5060405161021338038061021383398101604081905261002f916100ac565b600080546001600160a01b038085166001600160a01b03199283168117845560018054928616929093168217909255604051909230917f5de57f6bdaa5661f65214dfcfe60688ade6759f45b42d2db77b8139e0571e9089190a450506100df565b80516001600160a01b03811681146100a757600080fd5b919050565b600080604083850312156100bf57600080fd5b6100c883610090565b91506100d660208401610090565b90509250929050565b610125806100ee6000396000f3fe608060405261000c61000e565b005b61001e610019610020565b6100b7565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9fda0eb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561008e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b291906100db565b905090565b3660008037600080366000845af43d6000803e8080156100d6573d6000f35b3d6000fd5b6000602082840312156100ed57600080fd5b815173ffffffffffffffffffffffffffffffffffffffff8116811461011157600080fd5b939250505056fea164736f6c6343000814000aa164736f6c6343000814000a

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620003195760003560e01c8063923ca9a411620001a5578063b3de272d11620000f5578063db1c4ded11620000a3578063f95aea4c116200007a578063f95aea4c1462000803578063fa62ebaa146200081a578063fdfd5a25146200083957600080fd5b8063db1c4ded14620007a8578063e9e833e614620007c3578063eb39e9ba14620007da57600080fd5b8063b68a659211620000d8578063b68a6592146200076b578063bd2f7172146200078a578063d73792a9146200079d57600080fd5b8063b3de272d146200074a578063b5fa9dd2146200075457600080fd5b8063a44d57a01162000153578063ab08c5241162000136578063ab08c5241462000705578063ac8a584a146200071c578063ac98d9b0146200073357600080fd5b8063a44d57a014620006ad578063a9fda0eb14620006e657600080fd5b80639358a04111620001885780639358a041146200065c5780639870d7fe146200067357806399374642146200068a57600080fd5b8063923ca9a414620005e857806392765861146200064557600080fd5b8063468f8f52116200026d5780636e9960c3116200021b5780638057739911620001f25780638057739914620005a157806381ca52cf14620005c857806382e5bec314620005df57600080fd5b80636e9960c31462000554578063704b6c0214620005735780637e22e6eb146200058a57600080fd5b806351f0caef116200025057806351f0caef14620004d85780636aa9541414620004e25780636d70f7ae14620004f957600080fd5b8063468f8f5214620004aa57806351c2569814620004c157600080fd5b806324703dbc11620002cb5780633b44390a11620002ae5780633b44390a14620003f65780633f1b7020146200043657806344f2c5aa146200044d57600080fd5b806324703dbc14620003c85780633acbc1e914620003df57600080fd5b80630c3ee3ac11620003005780630c3ee3ac146200034e5780631ffe1422146200037857806320606b7014620003a057600080fd5b80630538153d146200031e578063091373601462000337575b600080fd5b620003356200032f36600462003610565b62000861565b005b62000335620003483660046200369e565b62000b72565b620003656200035f366004620036ec565b62000c34565b6040519081526020015b60405180910390f35b6200038f620003893660046200375b565b62000d2c565b60405190151581526020016200036f565b620003657f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b62000335620003d936600462003807565b62000d6c565b62000335620003f036600462003860565b62000e0e565b60065473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016200036f565b6200033562000447366004620038b4565b62000f77565b60408051808201909152601481527f4d6574614c656e6420526562616c616e63696e67000000000000000000000000602090910152620003657f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d3181565b62000335620004bb36600462003949565b6200103f565b62000335620004d2366004620039ac565b62001100565b620003656103e881565b62000335620004f336600462003a51565b620012fd565b6200038f6200050a36600462003949565b73ffffffffffffffffffffffffffffffffffffffff166000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291490565b60005473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200058436600462003949565b620015de565b620003356200059b36600462003b37565b6200174e565b600f54620005b29063ffffffff1681565b60405163ffffffff90911681526020016200036f565b62000335620005d93660046200369e565b620018fa565b620005b2600b81565b60408051808201909152600581527f332e302e30000000000000000000000000000000000000000000000000000000602090910152620003657fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a81565b620003356200065636600462003bdb565b62001951565b620003356200066d36600462003ca0565b62001b1e565b620003356200068436600462003949565b62001ccb565b620003656200069b36600462003949565b60016020526000908152604090205481565b62000410620006be36600462003949565b60086020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200071636600462003d12565b62001e33565b620003356200072d36600462003949565b62002031565b620003356200074436600462003dc7565b620020d4565b6200036560075481565b62000365620007653660046200369e565b6200217c565b60045473ffffffffffffffffffffffffffffffffffffffff1662000410565b6200038f600f5463ffffffff16600b1490565b62000365620186a081565b73374d7860c4f2f604de0191298dd393703cce84f362000410565b62000335620007d436600462003e3e565b620021dc565b600f546200041090640100000000900473ffffffffffffffffffffffffffffffffffffffff1681565b620003356200081436600462003ee2565b6200228a565b60035473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003657fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f046081565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620008ca576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000620008d786620024d0565b9050620008eb604084016020850162003949565b73ffffffffffffffffffffffffffffffffffffffff1662000913604086016020870162003949565b73ffffffffffffffffffffffffffffffffffffffff160362000961576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54620009979063ffffffff1662000981604087016020880162003949565b62000990602088018862003f6c565b886200254e565b600f54620009b0908490849063ffffffff1688620025df565b620009bc86836200261f565b620009c881866200276a565b73ffffffffffffffffffffffffffffffffffffffff811663fe4c85bc86620009f4602088018862003f6c565b62000a066040890160208a0162003949565b62000a15602089018962003f6c565b62000a2760408a0160208b0162003949565b6040518663ffffffff1660e01b815260040162000a4995949392919062003fd0565b600060405180830381600087803b15801562000a6457600080fd5b505af115801562000a79573d6000803e3d6000fd5b5062000a8d92505050602085018562003f6c565b600281111562000aa15762000aa162003f8a565b600f5460405173ffffffffffffffffffffffffffffffffffffffff888116825263ffffffff909216918916907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a462000b09602084018462003f6c565b600281111562000b1d5762000b1d62003f8a565b60405173ffffffffffffffffffffffffffffffffffffffff87811682528816907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a3505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000bc4576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e881111562000c01576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e81905560405181907f267f35adfb1c3a400666ab8eb7414e5d1cda481952b8b8fcad28d003a7874c6890600090a250565b600073dceac80c8429140450b45697aae1ccb8dc921ccb6337404955601060008562000c61578862000c6b565b600f5463ffffffff165b63ffffffff1663ffffffff168152602001908152602001600020600062000c92876200286a565b815260200190815260200160002054866040518363ffffffff1660e01b815260040162000cdf92919091825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b602060405180830381865af415801562000cfd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d23919062004028565b95945050505050565b63ffffffff841660009081526012602052604081208162000d4f8686866200289c565b815260208101919091526040016000205460ff1695945050505050565b600062000d7933620028d4565b6040517f24703dbc00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8216906324703dbc9062000dd4908790879087906004016200409a565b600060405180830381600087803b15801562000def57600080fd5b505af115801562000e04573d6000803e3d6000fd5b5050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462000e77576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060810135158062000e99575062000e97600f5463ffffffff16600b1490565b155b1562000ed1576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000edd82826200261f565b600062000eea83620024d0565b6040517fbc0348e50000000000000000000000000000000000000000000000000000000081526060840135600482015290915073ffffffffffffffffffffffffffffffffffffffff82169063bc0348e590602401600060405180830381600087803b15801562000f5957600080fd5b505af115801562000f6e573d6000803e3d6000fd5b50505050505050565b62000f82876200293a565b6002805473ffffffffffffffffffffffffffffffffffffffff9788167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054968816968216969096179095556004805494871694861694909417909355600680549286169290941691909117909255600f805491909316640100000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911663ffffffff9092169190911717905550565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001091576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517fb08982e7405e1df5c0f03355316cd1611d894a67dbaed99e5fa87307f54f171190600090a250565b60006200110d89620024d0565b600f54909150620011279063ffffffff1687898b6200254e565b6000829003620012185773ffffffffffffffffffffffffffffffffffffffff8916331462001181576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620011de908b908b908b908b90600090600401620040cc565b600060405180830381600087803b158015620011f957600080fd5b505af11580156200120e573d6000803e3d6000fd5b50505050620012f2565b620012608984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508d92508b91508a90508962002ace565b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620012bd908b908b908b908b90600190600401620040cc565b600060405180830381600087803b158015620012d857600080fd5b505af1158015620012ed573d6000803e3d6000fd5b505050505b505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200134f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86851415806200135f5750868314155b806200136b5750868114155b15620013a3576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660005b81811015620015d1576000620014408b8b84818110620013cb57620013cb62004119565b9050602002016020810190620013e2919062003949565b8a8a85818110620013f757620013f762004119565b90506020020160208101906200140e919062003f6c565b89898681811062001423576200142362004119565b90506020020160208101906200143a919062003949565b6200289c565b905084848381811062001457576200145762004119565b90506020020160208101906200146e919062004148565b63ffffffff8d166000908152601260209081526040808320858452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558a8a83818110620014d057620014d062004119565b9050602002016020810190620014e7919062003949565b73ffffffffffffffffffffffffffffffffffffffff167f5310df464b3c7a93f2147cf6f6d5eec46c7b7be22fc612af20e6f1a301e1f6088d8b8b8681811062001534576200153462004119565b90506020020160208101906200154b919062003f6c565b8a8a8781811062001560576200156062004119565b905060200201602081019062001577919062003949565b8989888181106200158c576200158c62004119565b9050602002016020810190620015a3919062004148565b604051620015b5949392919062004168565b60405180910390a250620015c981620041e0565b9050620013a7565b5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001630576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116620016d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f416363657373436f6e74726f6c3a206e65772061646d696e206973207468652060448201527f7a65726f2061646472657373000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f9190a35050565b60005473ffffffffffffffffffffffffffffffffffffffff163314620017a0576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483141580620017b05750848114155b15620017e8576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b8181101562000e045760008888838181106200180c576200180c62004119565b90506020020160208101906200182391906200421b565b905060008787848181106200183c576200183c62004119565b905060200201602081019062001853919062004239565b905060008686858181106200186c576200186c62004119565b90506020020135905063ffffffff80168363ffffffff1603620018bb576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff831660009081526010602052604081208291620018dd856200286a565b8152602081019190915260400160002055505050600101620017ec565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200194c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600755565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620019ba576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54600654620019ec918891889163ffffffff169073ffffffffffffffffffffffffffffffffffffffff16620025df565b620019f887866200261f565b600062001a0588620028d4565b905073ffffffffffffffffffffffffffffffffffffffff811663c2543bb362001a3260208a018a62003f6c565b62001a4460408b0160208c0162003949565b888888886040518763ffffffff1660e01b815260040162001a6b96959493929190620042a0565b600060405180830381600087803b15801562001a8657600080fd5b505af115801562001a9b573d6000803e3d6000fd5b5062001aaf92505050602088018862003f6c565b600281111562001ac35762001ac362003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff9182168152908a16907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a35050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001b87576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001b98848484888a62002d97565b600f5490915060009073ffffffffffffffffffffffffffffffffffffffff8316906371f6876a9063ffffffff88811691161462001bd6578662001bdc565b63ffffffff5b62001beb602088018862003f6c565b62001bfd6040890160208a0162003949565b8b6040518563ffffffff1660e01b815260040162001c1f949392919062004303565b6020604051808303816000875af115801562001c3f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001c65919062004028565b9050808673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f6260405160405180910390a450505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001d1d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811662001dc2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f416363657373436f6e74726f6c3a206f70657261746f7220697320746865207a60448201527f65726f20616464726573730000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff81166000818152600160205260408082207f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9299055517fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9190a250565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001e9c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001ead858484898b62002d97565b90506000808062001f028962001ec760808a018a6200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062002e3f92505050565b600f54929550909350915073ffffffffffffffffffffffffffffffffffffffff85169063368815ca9063ffffffff8b811691161462001f42578962001f48565b63ffffffff5b62001f5760208a018a62003f6c565b62001f6960408b0160208c0162003949565b8e8c600001358d602001358e604001358f606001358c8c8c6040518c63ffffffff1660e01b815260040162001fa99b9a99989796959493929190620043b6565b600060405180830381600087803b15801562001fc457600080fd5b505af115801562001fd9573d6000803e3d6000fd5b50506040518935925073ffffffffffffffffffffffffffffffffffffffff808d1692508d16907fa87989ef6d00dca941c1e1d69df9aab1058d404c2a8996492375a61f1d1d3b4990600090a450505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002083576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600081815260016020526040808220829055517f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d9190a250565b6000620020e133620028d4565b6040517fac98d9b000000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063ac98d9b09062002140908990899089908990899060040162004481565b600060405180830381600087803b1580156200215b57600080fd5b505af115801562002170573d6000803e3d6000fd5b50505050505050505050565b6000600e54600014806200218e575081155b80620021a85750620021a8600f5463ffffffff16600b1490565b15620021b657506000919050565b620186a0600e5483620021ca9190620044ce565b620021d69190620044e8565b92915050565b6000620021e933620028d4565b6040517fe9e833e600000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063e9e833e6906200224c908b908b908b908b908b908b908b9060040162004524565b600060405180830381600087803b1580156200226757600080fd5b505af11580156200227c573d6000803e3d6000fd5b505050505050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620022f3576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200230087620024d0565b90506200230d8662002ea2565b600f546200235b9063ffffffff166200232d604087016020880162003949565b6200233c602088018862003f6c565b60065473ffffffffffffffffffffffffffffffffffffffff166200254e565b600654620023859084908490899073ffffffffffffffffffffffffffffffffffffffff16620025df565b6200239187836200261f565b600654620023b790829073ffffffffffffffffffffffffffffffffffffffff166200276a565b73ffffffffffffffffffffffffffffffffffffffff81166336fd374586620023e3602088018862003f6c565b620023f56040890160208a0162003949565b8a6040518563ffffffff1660e01b815260040162002417949392919062004651565b600060405180830381600087803b1580156200243257600080fd5b505af115801562002447573d6000803e3d6000fd5b506200245b92505050602085018562003f6c565b60028111156200246f576200246f62003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff918216815263ffffffff8916918a16907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a450505050505050565b73ffffffffffffffffffffffffffffffffffffffff808216600090815260086020526040902054168062002549576040517f2b1991ae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401620016d0565b919050565b63ffffffff8416600090815260126020526040812081620025718686866200289c565b815260208101919091526040016000205460ff16905080620025d8576040517fb1b1f01400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401620016d0565b5050505050565b620025ec84848462002f30565b620026198262002603604087016020880162003949565b62002612602088018862003f6c565b846200254e565b50505050565b60006060820135156200268f5762002638828062004698565b62002647602085018562004698565b62002656604087018762004698565b876060013560405160200162002673979695949392919062004780565b60405160208183030381529060405280519060200120620026e7565b6200269b828062004698565b620026aa602085018562004698565b620026b9604087018762004698565b604051602001620026d096959493929190620047d7565b604051602081830303815290604052805190602001205b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c902090915062002619846200272d60808601866200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925062003170915050565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260116020908152604080832093851683529290522054600754620027ac908262004819565b42101562002801574260075482620027c5919062004819565b6040517f4be8f90500000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401620016d0565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526011602090815260408083209487168084529490915280822042908190559051909392917fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd4080590991a4505050565b6000816040516020016200287f91906200482f565b604051602081830303815290604052805190602001209050919050565b6000838383604051602001620028b59392919062004853565b6040516020818303038152906040528051906020012090509392505050565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152600860205260408120549091166200290e57620021d6826200325b565b5073ffffffffffffffffffffffffffffffffffffffff9081166000908152600860205260409020541690565b60005473ffffffffffffffffffffffffffffffffffffffff1615620029e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20616c726561647920696e6960448201527f7469616c697a65640000000000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff811662002a87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401620016d0565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b8042111562002b0d576040517f277b2d7400000000000000000000000000000000000000000000000000000000815260048101829052602401620016d0565b600c8560405162002b1f9190620048d9565b9081526040519081900360200190205460ff161562002b6e57846040517fbf42d003000000000000000000000000000000000000000000000000000000008152600401620016d0919062004943565b6001600c8660405162002b829190620048d9565b90815260408051918290036020908101832080549415157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909516949094179093557fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f04608383015273ffffffffffffffffffffffffffffffffffffffff87811683830152861660608301526080820185905260a08083018590528151808403909101815260c090920190528051910120600062002d37604080518082018252601481527f4d6574614c656e6420526562616c616e63696e6700000000000000000000000060209182015281518083018352600581527f332e302e300000000000000000000000000000000000000000000000000000009082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d31818401527fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a60608201524660808201523060a0808301919091528351808303909101815260c0909101909252815191012090565b6040517f1901000000000000000000000000000000000000000000000000000000000000602082015260228101919091526042810183905260620160405160208183030381529060405280519060200120905062000e0488888362003170565b600062002da785858885620025df565b62002db383856200261f565b600f5463ffffffff87811691161462002e265762002dd18662002ea2565b60065473ffffffffffffffffffffffffffffffffffffffff83811691161462002e26576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62002e3183620028d4565b905062000d2381836200341c565b6000806000835160411462002e865784846040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b5050506020810151604082015160609092015160001a93909250565b600f5463ffffffff8281169116148062002eec575063ffffffff811660009081526010602052604081209062002ed960046200286a565b8152602001908152602001600020546000145b1562002f2d576040517fd3616c3700000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401620016d0565b50565b62002f3f602083018362004698565b905062002f4d838062004698565b905014158062002f7a575062002f67604083018362004698565b905062002f75838062004698565b905014155b1562002fb2576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062002fc0838062004698565b9050905060005b818110156200313d57600062002fde858062004698565b8381811062002ff15762002ff162004119565b905060200201602081019062003008919062003f6c565b905060006200301b602087018762004698565b848181106200302e576200302e62004119565b905060200201602081019062003045919062003949565b9050600062003058604088018862004698565b858181106200306b576200306b62004119565b90506020020160208101906200308291906200421b565b905062003093602089018962003f6c565b6002811115620030a757620030a762003f8a565b836002811115620030bc57620030bc62003f8a565b148015620031065750620030d76040890160208a0162003949565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b80156200311e57508563ffffffff168163ffffffff16145b156200312e575050505050505050565b83600101935050505062002fc7565b506040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f546040517f98ef1ed800000000000000000000000000000000000000000000000000000000815264010000000090910473ffffffffffffffffffffffffffffffffffffffff16906398ef1ed890620031d39086908590879060040162004991565b6020604051808303816000875af1158015620031f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620032199190620049c8565b620032565782826040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b505050565b600080604051806020016200327090620035b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604081815230602083015273ffffffffffffffffffffffffffffffffffffffff8616908201529091506000908290606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526200330d9291602001620049e8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606087901b16602083015291506000906034016040516020818303038152906040528051906020012090506200339660008284620034ce565b73ffffffffffffffffffffffffffffffffffffffff86811660008181526008602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948616948517905551939750919290917f3b2ac627f2dda3cc16038f312aeeda34474e3770c24737309735bb3911bdcf8291a3505050919050565b60075473ffffffffffffffffffffffffffffffffffffffff8084166000908152601160209081526040808320938616835292905220546200345e919062004819565b421015620034ca5773ffffffffffffffffffffffffffffffffffffffff80831660008181526011602090815260408083209486168084529490915280822082905551909291907fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd40805909908490a45b5050565b60008347101562003515576040517fe4bbecac00000000000000000000000000000000000000000000000000000000815247600482015260248101859052604401620016d0565b815160000362003551576040517f4ca249dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116620035aa576040517f741752c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b6102138062004a1c83390190565b803573ffffffffffffffffffffffffffffffffffffffff811681146200254957600080fd5b600060408284031215620035f757600080fd5b50919050565b600060a08284031215620035f757600080fd5b600080600080600060e086880312156200362957600080fd5b6200363486620035bf565b94506200364460208701620035bf565b9350620036558760408801620035e4565b9250620036668760808801620035e4565b915060c086013567ffffffffffffffff8111156200368357600080fd5b6200369188828901620035fd565b9150509295509295909350565b600060208284031215620036b157600080fd5b5035919050565b803563ffffffff811681146200254957600080fd5b8035600681106200254957600080fd5b801515811462002f2d57600080fd5b600080600080608085870312156200370357600080fd5b6200370e85620036b8565b93506200371e60208601620035bf565b92506200372e60408601620036cd565b915060608501356200374081620036dd565b939692955090935050565b8035600381106200254957600080fd5b600080600080608085870312156200377257600080fd5b6200377d85620036b8565b93506200378d60208601620035bf565b92506200379d604086016200374b565b9150620037ad60608601620035bf565b905092959194509250565b60008083601f840112620037cb57600080fd5b50813567ffffffffffffffff811115620037e457600080fd5b6020830191508360208260051b85010111156200380057600080fd5b9250929050565b6000806000604084860312156200381d57600080fd5b6200382884620035bf565b9250602084013567ffffffffffffffff8111156200384557600080fd5b6200385386828701620037b8565b9497909650939450505050565b600080604083850312156200387457600080fd5b6200387f83620035bf565b9150602083013567ffffffffffffffff8111156200389c57600080fd5b620038aa85828601620035fd565b9150509250929050565b600080600080600080600060e0888a031215620038d057600080fd5b620038db88620035bf565b9650620038eb60208901620035bf565b9550620038fb60408901620035bf565b94506200390b60608901620035bf565b93506200391b60808901620035bf565b92506200392b60a08901620036b8565b91506200393b60c08901620035bf565b905092959891949750929550565b6000602082840312156200395c57600080fd5b620035aa82620035bf565b60008083601f8401126200397a57600080fd5b50813567ffffffffffffffff8111156200399357600080fd5b6020830191508360208285010111156200380057600080fd5b60008060008060008060008060e0898b031215620039c957600080fd5b620039d489620035bf565b9750620039e460208a01620035bf565b9650620039f460408a016200374b565b955062003a0460608a01620035bf565b94506080890135935060a0890135925060c089013567ffffffffffffffff81111562003a2f57600080fd5b62003a3d8b828c0162003967565b999c989b5096995094979396929594505050565b600080600080600080600080600060a08a8c03121562003a7057600080fd5b62003a7b8a620036b8565b985060208a013567ffffffffffffffff8082111562003a9957600080fd5b62003aa78d838e01620037b8565b909a50985060408c013591508082111562003ac157600080fd5b62003acf8d838e01620037b8565b909850965060608c013591508082111562003ae957600080fd5b62003af78d838e01620037b8565b909650945060808c013591508082111562003b1157600080fd5b5062003b208c828d01620037b8565b915080935050809150509295985092959850929598565b6000806000806000806060878903121562003b5157600080fd5b863567ffffffffffffffff8082111562003b6a57600080fd5b62003b788a838b01620037b8565b9098509650602089013591508082111562003b9257600080fd5b62003ba08a838b01620037b8565b9096509450604089013591508082111562003bba57600080fd5b5062003bc989828a01620037b8565b979a9699509497509295939492505050565b600080600080600080600060c0888a03121562003bf757600080fd5b62003c0288620035bf565b965062003c138960208a01620035e4565b9550606088013567ffffffffffffffff8082111562003c3157600080fd5b62003c3f8b838c01620035fd565b965060808a013591508082111562003c5657600080fd5b62003c648b838c0162003967565b909650945060a08a013591508082111562003c7e57600080fd5b5062003c8d8a828b0162003967565b989b979a50959850939692959293505050565b600080600080600060c0868803121562003cb957600080fd5b62003cc486620035bf565b945062003cd460208701620035bf565b935062003ce460408701620036b8565b925062003cf58760608801620035e4565b915060a086013567ffffffffffffffff8111156200368357600080fd5b60008060008060008060e0878903121562003d2c57600080fd5b62003d3787620035bf565b955062003d4760208801620035bf565b945062003d5760408801620036b8565b9350606087013567ffffffffffffffff8082111562003d7557600080fd5b62003d838a838b01620035fd565b945062003d948a60808b01620035e4565b935060c089013591508082111562003dab57600080fd5b5062003dba89828a01620035fd565b9150509295509295509295565b60008060008060006080868803121562003de057600080fd5b62003deb86620035bf565b945062003dfb60208701620035bf565b935060408601359250606086013567ffffffffffffffff81111562003e1f57600080fd5b62003e2d88828901620037b8565b969995985093965092949392505050565b60008060008060008060006080888a03121562003e5a57600080fd5b62003e6588620035bf565b9650602088013567ffffffffffffffff8082111562003e8357600080fd5b62003e918b838c01620037b8565b909850965060408a013591508082111562003eab57600080fd5b62003eb98b838c01620037b8565b909650945060608a013591508082111562003ed357600080fd5b5062003c8d8a828b01620037b8565b600080600080600080610100878903121562003efd57600080fd5b62003f0887620035bf565b955062003f1860208801620036b8565b94506040870135935062003f308860608901620035e4565b925062003f418860a08901620035e4565b915060e087013567ffffffffffffffff81111562003f5e57600080fd5b62003dba89828a01620035fd565b60006020828403121562003f7f57600080fd5b620035aa826200374b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6003811062003fcc5762003fcc62003f8a565b9052565b73ffffffffffffffffffffffffffffffffffffffff868116825260a082019062003ffe602084018862003fb9565b808616604084015262004015606084018662003fb9565b8084166080840152509695505050505050565b6000602082840312156200403b57600080fd5b5051919050565b8183526000602080850194508260005b858110156200408f5773ffffffffffffffffffffffffffffffffffffffff6200407b83620035bf565b168752958201959082019060010162004052565b509495945050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152600062000d2360408301848662004042565b73ffffffffffffffffffffffffffffffffffffffff868116825260a0820190620040fa602084018862003fb9565b9490941660408201526060810192909252151560809091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156200415b57600080fd5b8135620035aa81620036dd565b63ffffffff851681526080810162004184602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff84166040830152821515606083015295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203620042145762004214620041b1565b5060010190565b6000602082840312156200422e57600080fd5b620035aa82620036b8565b6000602082840312156200424c57600080fd5b620035aa82620036cd565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b620042ac818862003fb9565b73ffffffffffffffffffffffffffffffffffffffff86166020820152608060408201526000620042e160808301868862004257565b8281036060840152620042f681858762004257565b9998505050505050505050565b63ffffffff85168152608081016200431f602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff808516604084015280841660608401525095945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126200438457600080fd5b83018035915067ffffffffffffffff821115620043a057600080fd5b6020019150368190038213156200380057600080fd5b63ffffffff8c1681526101608101620043d3602083018d62003fb9565b73ffffffffffffffffffffffffffffffffffffffff9a8b1660408301529890991660608a0152608089019690965260a088019490945260c087019290925260e086015260ff166101008501526101208401526101409092019190915292915050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156200446857600080fd5b8260051b80836020870137939093016020019392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152620044c360808301848662004435565b979650505050505050565b8082028115828204841417620021d657620021d6620041b1565b6000826200451f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b73ffffffffffffffffffffffffffffffffffffffff88168152600060206080818401526200455760808401898b62004042565b83810360408501526200456c81888a62004435565b84810360608601528581529050818101600586811b830184018860005b898110156200463d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c3603018112620045ec57600080fd5b8b01878101903567ffffffffffffffff8111156200460957600080fd5b80861b36038213156200461b57600080fd5b6200462885828462004435565b96890196945050509086019060010162004589565b50909e9d5050505050505050505050505050565b8481526080810162004667602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff8416604083015263ffffffff8316606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112620046ce57600080fd5b83018035915067ffffffffffffffff821115620046ea57600080fd5b6020019150600581901b36038213156200380057600080fd5b8183526000602080850194508260005b858110156200408f5762004732876200472c846200374b565b62003fb9565b958201959082019060010162004713565b8183526000602080850194508260005b858110156200408f5763ffffffff6200476c83620036b8565b168752958201959082019060010162004753565b6080815260006200479660808301898b62004703565b8281036020840152620047ab81888a62004042565b90508281036040840152620047c281868862004743565b91505082606083015298975050505050505050565b606081526000620047ed60608301888a62004703565b82810360208401526200480281878962004042565b90508281036040840152620042f681858762004743565b80820180821115620021d657620021d6620041b1565b60006006831062004844576200484462003f8a565b5060f89190911b815260010190565b60007fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808660601b1683526003851062004891576200489162003f8a565b60f89490941b60148301525060609190911b9091166015820152602901919050565b60005b83811015620048d0578181015183820152602001620048b6565b50506000910152565b60008251620048ed818460208701620048b3565b9190910192915050565b6000815180845262004911816020860160208601620048b3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000620035aa6020830184620048f7565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000620049896040830184620048f7565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600062000d236060830184620048f7565b600060208284031215620049db57600080fd5b8151620035aa81620036dd565b60008351620049fc818460208801620048b3565b83519083019062004a12818360208801620048b3565b0194935050505056fe608060405234801561001057600080fd5b5060405161021338038061021383398101604081905261002f916100ac565b600080546001600160a01b038085166001600160a01b03199283168117845560018054928616929093168217909255604051909230917f5de57f6bdaa5661f65214dfcfe60688ade6759f45b42d2db77b8139e0571e9089190a450506100df565b80516001600160a01b03811681146100a757600080fd5b919050565b600080604083850312156100bf57600080fd5b6100c883610090565b91506100d660208401610090565b90509250929050565b610125806100ee6000396000f3fe608060405261000c61000e565b005b61001e610019610020565b6100b7565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9fda0eb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561008e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b291906100db565b905090565b3660008037600080366000845af43d6000803e8080156100d6573d6000f35b3d6000fd5b6000602082840312156100ed57600080fd5b815173ffffffffffffffffffffffffffffffffffffffff8116811461011157600080fd5b939250505056fea164736f6c6343000814000aa164736f6c6343000814000a

Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

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.