XStakingPool

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/XStakingPool.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "./interfaces/IToken.sol";
import "./interfaces/IXStakingPool.sol";
import "./interfaces/IXBRStakingPool.sol";
import "./interfaces/IXStakingFactory.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

/// @title XStakingPool
/// @dev This contract handles individual staking operations,
/// including deposits and withdrawals of different assets.
/// @custom:oz-upgrades-from XStakingPool
contract XStakingPool is
    Initializable,
    ERC20Upgradeable,
    Ownable2StepUpgradeable,
    IXStakingPool,
    ReentrancyGuardUpgradeable
{
    using SafeERC20 for IToken;

    /// @dev 10000 = 100%
    uint256 public constant FEE_DENOMINATOR = 100_00;

    /// @dev Minimal deposit fee value ($1)
    uint256 public constant MIN_FIXED_DEPOSIT_FEE = 1_000000;

    uint256 public constant MAX_FIXED_DEPOSIT_FEE = 1_000_000_000000;
    uint256 public constant MIN_PERCENT_DEPOSIT_FEE = 100;

    /// @notice Reference to the XStakingFactory that deployed this pool.
    IXStakingFactory public xstakingFactory;

    /// @notice Unique identifier for this staking pool.
    uint256 public poolId;

    /// @notice profit sharing fee numerator
    uint256 public profitSharingFeeNumerator;

    /// @notice tokens in the pool
    address[] public tokens;

    /// @notice the allocations of tokens
    uint256[] public allocations;

    /// @notice the sum of array `allocations`
    uint256 public totalAllocation;

    /// @notice the cap of
    uint256 public capitalizationCap;

    /// @notice true - deposit paused, false - deposit not paused
    bool public isDepositPaused;

    /// @notice profit sharing fees
    mapping(address depositToken => uint256) public totalProfitSharingFee;

    /// @notice allocated tokens to user
    mapping(address user => mapping(address token => uint256))
        public userTokenAmount;

    /// @notice Mapping to track the total tokens amount for each token.
    /// @dev Key is the token address, and value is the total amount deposited in the pool.
    mapping(address token => uint256) public totalTokenAmount;

    /// @notice List of tokens that have been added to the pool to prevent duplicates.
    mapping(address => bool) isTokenAlreadyAdded;

    uint256 private tempTotalAmountDepositTokenOut = 0;

    uint256 public depositFeeNumerator = 1_000000;
    bool public isPercentageFee = false;
    mapping(address => uint256) public totalDepositFee;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @notice Initializes the XStakingPool with a specific pool ID and initial owner.
    /// @dev Sets the pool ID and the initial owner. Links the pool to the XStakingFactory.
    /// @param _poolId Unique identifier for the pool.
    /// @param _poolOwner Address of the initial owner of the pool.
    function initialize(
        uint256 _poolId,
        address _poolOwner,
        uint256 _capitalizationCap,
        address[] memory _tokens,
        uint256[] memory _allocations,
        uint256 _profitSharingFeeNumerator,
        uint256[] memory _tokensAmounts,
        address initialDepositToken,
        uint256 initialDepositTokenAmount,
        uint256 _depositFeeNumerator,
        bool _isPercentageFee
    ) public initializer {
        __Ownable_init(_poolOwner);
        __ERC20_init_unchained(
            string.concat("XBR XStakingPool #", Strings.toString(_poolId)),
            string.concat("XBR", Strings.toString(_poolId))
        );
        __ReentrancyGuard_init();
        xstakingFactory = IXStakingFactory(msg.sender); // deployer is XStakingFactory
        poolId = _poolId;
        capitalizationCap = _capitalizationCap;
        uint256 tokenLength = _tokens.length;
        require(
            tokenLength == _allocations.length &&
                tokenLength == _tokensAmounts.length,
            "XStakingPool: invalid length of _tokens and _allocations"
        );

        allocations = _allocations;
        uint256 _totalAllocation = 0;
        address poolOwner = _poolOwner;
        _setProfitSharingFeeNumerator(_profitSharingFeeNumerator);
        _setDepositFeeNumerator(_depositFeeNumerator, _isPercentageFee);

        address token;
        for (uint256 i = 0; i < tokenLength; ) {
            token = _tokens[i];

            require(
                isTokenAlreadyAdded[token] == false,
                "XStakingPool: token already added"
            );

            isTokenAlreadyAdded[token] = true;
            tokens.push(token);

            uint256 tokenAmountBefore = IToken(token).balanceOf(address(this));

            IToken(token).safeTransferFrom(
                msg.sender,
                address(this),
                _tokensAmounts[i]
            );

            uint256 tokenAmountAfter = IToken(token).balanceOf(address(this));

            uint256 incommingAmount = tokenAmountAfter - tokenAmountBefore;
            userTokenAmount[poolOwner][token] += incommingAmount;
            totalTokenAmount[token] += incommingAmount;
            _totalAllocation += _allocations[i];
            unchecked {
                i++;
            }
        }
        for (uint256 i = 0; i < tokenLength; ) {
            emit TokenSwap(
                _tokens[i],
                _tokensAmounts[i],
                (initialDepositTokenAmount * _allocations[i]) / _totalAllocation
            );
            unchecked {
                i++;
            }
        }
        uint256 amountToMint = calcMintAmount(
            initialDepositToken,
            initialDepositTokenAmount
        );
        _mint(poolOwner, amountToMint);
        emit Volume(initialDepositTokenAmount);
        emit Deposit(
            address(this),
            poolOwner,
            initialDepositTokenAmount,
            getUserTokenAmounts(poolOwner)
        );
        emitTokenAmouts();
        totalAllocation = _totalAllocation;
    }

    function claimProfitSharingFee(address depositToken) public onlyOwner {
        totalProfitSharingFee[depositToken] = 0;
        IToken(depositToken).safeTransfer(
            msg.sender,
            totalProfitSharingFee[depositToken]
        );
    }

    function claimDepositFee(address depositToken) public onlyOwner {
        uint256 depositFee = totalDepositFee[depositToken];
        totalDepositFee[depositToken] = 0;
        
        IToken(depositToken).safeTransfer(
            msg.sender,
            depositFee
        );
    }

    function setDepositFeeNumerator(
        uint256 depositFeeNumerator,
        bool isPercentageFee
    ) public onlyOwner {
        _setDepositFeeNumerator(depositFeeNumerator, isPercentageFee);
    }

    /// @notice set the deposit paused
    /// @param _depositPaused true - deposit paused, false - deposit unpaused
    function setPausedDeposit(bool _depositPaused) public onlyOwner {
        isDepositPaused = _depositPaused;

        emit DepositStatusUpdated(_depositPaused);
    }

    /// @notice set the new capitalization cap
    /// @param _capitalizationCap the amount of dollars in capitalization
    function setCapitalizationCap(uint256 _capitalizationCap) public onlyOwner {
        capitalizationCap = _capitalizationCap;
    }

    /// @notice sets profit sharing fee
    /// @param _profitSharingFeeNumerator the numerator of profit sharing fee
    function setProfitSharingFeeNumerator(
        uint256 _profitSharingFeeNumerator
    ) public onlyOwner {
        _setProfitSharingFeeNumerator(_profitSharingFeeNumerator);
    }

    function _setProfitSharingFeeNumerator(
        uint256 _profitSharingFeeNumerator
    ) internal {
        require(
            1_00 <= _profitSharingFeeNumerator &&
                _profitSharingFeeNumerator <= 49_00,
            "XStakingPool: _profitSharingFeeNumerator out of bound"
        );
        profitSharingFeeNumerator = _profitSharingFeeNumerator;
    }

    function execOneInchSwap(
        address oneInchRouter,
        bytes memory oneInchSwapData
    ) internal returns (uint256) {
        (bool success, bytes memory response) = oneInchRouter.call(
            oneInchSwapData
        );
        require(success, "1inch swap failed");
        uint256 amountOut = abi.decode(response, (uint256));
        return amountOut;
    }

    /// @notice deposit the baseToken to pool to `msg.sender`
    /// @param depositToken address of deposit token
    /// @param depositTokenAmount amount of base token
    /// @param oneInchSwapData the data for swap on 1inch
    /// @return the amount of minted Liquidity Pool Token
    function deposit(
        address depositToken,
        uint256 depositTokenAmount,
        bytes[] calldata oneInchSwapData
    ) public returns (uint256) {
        return
            depositTo(
                depositToken,
                msg.sender,
                depositTokenAmount,
                oneInchSwapData
            );
    }

    /// @notice deposit the baseToken to pool
    /// @param depositToken address of deposit token
    /// @param to address of receiver the LP tokens
    /// @param depositTokenAmount amount of base token
    /// @param oneInchSwapData the data for swap on 1inch
    /// @return the amount of minted Liquidity Pool Token
    function depositTo(
        address depositToken,
        address to,
        uint256 depositTokenAmount,
        bytes[] calldata oneInchSwapData
    ) public returns (uint256) {
        _checkIfOwnerHaveLockedTokens();
        require(
            xstakingFactory.isDepositToken(depositToken),
            "XStakingPool: not deposit token"
        );
        require(
            tokens.length == oneInchSwapData.length,
            "XStakingPool: invalid length of tokens and oneInchSwapData"
        );

        require(!isDepositPaused, "XStakingPool: deposit is paused");
        address oneInchRouter = xstakingFactory.oneInchRouter();
        address treasuryWallet = xstakingFactory.treasuryWallet();
        uint256 totalDepositToken = calculateTotalDepositToken(
            depositToken,
            depositTokenAmount
        );
        uint256 depositFee = calculateDepositFee(depositTokenAmount);

        {
            IToken(depositToken).safeTransferFrom(
                msg.sender,
                address(this),
                totalDepositToken + depositFee
            );

            uint256 stakingFee = totalDepositToken - depositTokenAmount;

            IToken(depositToken).safeTransfer(treasuryWallet, stakingFee);
        }

        totalDepositFee[depositToken] += depositFee;

        IToken(depositToken).forceApprove(oneInchRouter, depositTokenAmount);
        _depositTo(
            depositToken,
            to,
            depositTokenAmount,
            oneInchRouter,
            oneInchSwapData
        );
        emit Volume(depositTokenAmount);
        emit Deposit(
            address(this),
            to,
            depositTokenAmount,
            getUserTokenAmounts(to)
        );
        emitTokenAmouts();
        return depositTokenAmount;
    }

    function _depositTo(
        address depositToken,
        address to,
        uint256 depositTokenAmount,
        address oneInchRouter,
        bytes[] calldata oneInchSwapData
    ) internal nonReentrant {
        uint256 len = tokens.length;
        uint256 amountOut;
        uint256 depositTokenAmountAllocated;
        address token;
        uint256 capitalization = 0;
        uint256 totalSwappedDepositTokenAmount = 0;

        for (uint256 i = 0; i < len; ) {
            token = tokens[i];

            uint256 balanceBefore = IToken(token).balanceOf(address(this));

            require(
                oneInchSwapData[i].length >= 4,
                "XStakingPool: Incorrect data length"
            );

            (
                ,
                address receiver,
                address srcToken,
                uint256 srcTokenAmount,
                uint256 minReturnAmount
            ) = decodeSwapData(oneInchSwapData[i]);

            totalSwappedDepositTokenAmount += srcTokenAmount;

            require(
                address(this) == receiver,
                "XStakingPool: swap receiver have to be pool address"
            );

            require(
                depositToken == srcToken,
                "XStakingPool: deposit token does not match with src token in swap data"
            );

            uint256 tokenAmountBefore = IToken(token).balanceOf(address(this));

            execOneInchSwap(oneInchRouter, oneInchSwapData[i]); // [amountOut]=token

            uint256 tokenAmountAfter = IToken(token).balanceOf(address(this));

            amountOut = tokenAmountAfter - tokenAmountBefore;

            require(
                amountOut >= minReturnAmount,
                "XStakingPool: output amount does not match with encoded amount from swap data"
            );

            uint256 balanceAfter = IToken(token).balanceOf(address(this));

            require(
                balanceAfter != balanceBefore,
                "XStakingPool: pool balance was not changed after swap"
            );

            userTokenAmount[to][token] += amountOut;
            totalTokenAmount[token] += amountOut;
            depositTokenAmountAllocated =
                (depositTokenAmount * allocations[i]) /
                totalAllocation;
            capitalization +=
                (totalTokenAmount[token] * depositTokenAmountAllocated) /
                amountOut;
            emit TokenSwap(token, amountOut, depositTokenAmountAllocated);
            unchecked {
                i++;
            }
        }

        require(
            totalSwappedDepositTokenAmount == depositTokenAmount,
            "XStakingPool: swapped tokens amount does not match with sum of amount in every swap"
        );

        _mint(to, calcMintAmount(depositToken, depositTokenAmount));

        if (totalSupply() >= capitalizationCap * 10 ** 12) {
            isDepositPaused = true;

            emit DepositStatusUpdated(true);
        }
        emit PoolCapitalization(address(this), capitalization);
    }

    /// @notice return the allocation tokens for deposit using 1inch
    /// @dev this helper view function uses for forming swap request to 1inch API
    /// @param depositTokenAmount the amount of base token to deposit
    /// @return the array of allocation of depositTokenAmount for swap using 1inch and sum of array elements
    function calcDepositTokenAllocationForDeposit(
        uint256 depositTokenAmount
    ) public view returns (uint256[] memory, uint256) {
        uint256 len = tokens.length;
        uint256[] memory allocatedBaseTokens = new uint256[](len);

        uint256 totalSumOfAllocatedBaseToken;
        for (uint256 i = 0; i < len; ) {
            allocatedBaseTokens[i] =
                (depositTokenAmount * allocations[i]) /
                totalAllocation;
            totalSumOfAllocatedBaseToken += allocatedBaseTokens[i];
            unchecked {
                i++;
            }
        }
        return (allocatedBaseTokens, totalSumOfAllocatedBaseToken);
    }

    function calculateTotalDepositToken(
        address depositToken,
        uint256 depositTokenAmount
    ) public view returns (uint256 totalDepositToken) {
        uint256 stakingFee = xstakingFactory.calculateFeeAmount(
            depositTokenAmount,
            true
        );
        totalDepositToken = depositTokenAmount + stakingFee;
    }

    function calculateDepositFee(
        uint256 depositTokenAmount
    ) public view returns (uint256) {
        if(msg.sender == owner()){
            return 0;
        }
        if (isPercentageFee) {
            return (depositTokenAmount * depositFeeNumerator) / FEE_DENOMINATOR;
        } else {
            return
                depositFeeNumerator != 0
                    ? depositFeeNumerator
                    : MIN_FIXED_DEPOSIT_FEE;
        }
    }

    /// @notice withdraw the base token amount from `from` to `msg.sender`
    /// @param depositToken address of deposit token
    /// @param amountLP the amount of Liquidity Pool token
    /// @param oneInchSwapData the data for swap on 1inch
    function withdraw(
        address depositToken,
        uint256 amountLP,
        bytes[] calldata oneInchSwapData
    ) public returns (uint256) {
        return
            withdrawFrom(
                depositToken,
                msg.sender,
                msg.sender,
                amountLP,
                oneInchSwapData
            );
    }

    function withdrawFrom(
        address depositToken,
        address from,
        address to,
        uint256 amountLP,
        bytes[] calldata oneInchSwapData
    ) internal returns (uint256) {
        require(
            xstakingFactory.isDepositToken(depositToken),
            "XStakingPool: not deposit token"
        );
        uint256 balanceOfLP = balanceOf(from);
        require(amountLP <= balanceOfLP, "XStakingPool: amountLP > balanceOf");

        address oneInchRouter = xstakingFactory.oneInchRouter();
        address treasuryWallet = xstakingFactory.treasuryWallet();
        uint256 totalAmountDepositTokenOut = _withdrawFrom(
            from,
            amountLP,
            oneInchRouter,
            oneInchSwapData
        );
        uint256 unstakingFee;

        uint256 lpDecimals = decimals();
        uint256 depositTokenDecimals = IToken(depositToken).decimals();

        uint256 adaptedAmountLP = amountLP;
        uint256 adaptedTotalAmountDepositTokenOut = totalAmountDepositTokenOut;

        // adapt `adaptedAmountLP` decimals to `depositTokenDecimals`.
        if (lpDecimals > depositTokenDecimals) {
            uint256 scaleDifference = lpDecimals - depositTokenDecimals;
            adaptedAmountLP = adaptedAmountLP / (10 ** scaleDifference);
        } else if (depositTokenDecimals > lpDecimals) {
            uint256 scaleDifference = depositTokenDecimals - lpDecimals;
            adaptedAmountLP = adaptedAmountLP * (10 ** scaleDifference);
        }

        if (adaptedAmountLP < adaptedTotalAmountDepositTokenOut) {
            uint256 profit = adaptedTotalAmountDepositTokenOut -
                adaptedAmountLP;
            uint256 profitSharingFee = (profit * profitSharingFeeNumerator) /
                FEE_DENOMINATOR;
            totalProfitSharingFee[depositToken] += profitSharingFee;
            totalAmountDepositTokenOut -= profitSharingFee;
        }

        unstakingFee = xstakingFactory.calculateFeeAmount(
            totalAmountDepositTokenOut,
            false
        );

        IToken(depositToken).safeTransfer(
            to,
            totalAmountDepositTokenOut - unstakingFee
        );

        IToken(depositToken).safeTransfer(treasuryWallet, unstakingFee);

        emit Volume(totalAmountDepositTokenOut);
        emit Withdraw(
            address(this),
            from,
            totalAmountDepositTokenOut,
            getUserTokenAmounts(from)
        );
        emitTokenAmouts();
        _burn(from, amountLP);
        return totalAmountDepositTokenOut;
    }

    function _withdrawFrom(
        address from,
        uint256 amountLP,
        address oneInchRouter,
        bytes[] calldata oneInchSwapData
    ) internal nonReentrant returns (uint256) {
        address token;
        uint256 capitalization = 0;
        uint256[] memory allocatedTokens = calcAllocatedTokensForWithdraw(
            from,
            amountLP
        );

        for (uint256 i = 0; i < tokens.length; ) {
            bytes memory swapData = oneInchSwapData[i];
            uint256 amountDepositTokenOut;
            token = tokens[i];

            userTokenAmount[from][token] -= allocatedTokens[i];
            totalTokenAmount[token] -= allocatedTokens[i];
            IToken(token).forceApprove(oneInchRouter, allocatedTokens[i]);

            uint256 balanceBefore = IToken(token).balanceOf(address(this));

            if (swapData.length == 0) {
                IToken(token).safeTransfer(from, allocatedTokens[i]);
            } else {
                amountDepositTokenOut = execOneInchSwap(
                    oneInchRouter,
                    oneInchSwapData[i]
                );

                uint256 balanceAfter = IToken(token).balanceOf(address(this));

                require(
                    balanceBefore == balanceAfter + allocatedTokens[i],
                    "XStakingPool: swapped amount does not match with allocated amount"
                );

                require(
                    balanceBefore != IToken(token).balanceOf(address(this)),
                    "XStakingPool: deposit token does not match with src token in swap data"
                );

                require(
                    oneInchSwapData[i].length >= 4,
                    "XStakingPool: Incorrect data length"
                );

                (
                    ,
                    address receiver,
                    ,
                    uint256 srcTokenAmount,
                    uint256 minReturnAmount
                ) = decodeSwapData(oneInchSwapData[i]);

                tempTotalAmountDepositTokenOut += amountDepositTokenOut;

                require(
                    srcTokenAmount == allocatedTokens[i],
                    "XStakingPool: srcTokenAmount does not match with allocatedTokens"
                );

                require(
                    address(this) == receiver,
                    "XStakingPool: swap receiver have to be pool address"
                );

                require(
                    amountDepositTokenOut >= minReturnAmount,
                    "XStakingPool: amountDepositTokenOut less than minReturnAmount"
                );

                capitalization +=
                    (totalTokenAmount[token] * amountDepositTokenOut) /
                    allocatedTokens[i];
                emit TokenSwap(
                    token,
                    allocatedTokens[i],
                    amountDepositTokenOut
                );
            }

            unchecked {
                i++;
            }
        }

        if (totalSupply() < capitalizationCap * 10 ** 12) {
            isDepositPaused = false;

            emit DepositStatusUpdated(false);
        }

        uint256 result = tempTotalAmountDepositTokenOut;
        tempTotalAmountDepositTokenOut = 0;

        emit PoolCapitalization(address(this), capitalization);
        return result;
    }

    /// @notice overrides for handle proper LP tokens transfers
    function _update(
        address from,
        address to,
        uint256 amountLP
    ) internal override {
        super._update(from, to, amountLP);
        if (from == address(0) || to == address(0)) {
            // if mint or burn
            return;
        }

        revert("LP non transferable");
    }

    /// @notice return the allocation tokens for withdraw using 1inch
    /// @dev this helper view function uses for forming swap request to 1inch API
    /// @param amountLP amount of Liquidity Pool token
    /// @return allocatedTokens the array of tokens amount
    function calcAllocatedTokensForWithdraw(
        address user,
        uint256 amountLP
    ) public view returns (uint256[] memory allocatedTokens) {
        require(
            amountLP <= balanceOf(user),
            "XStakingPool: exceeds amount of LP"
        );
        uint256 len = tokens.length;
        allocatedTokens = new uint256[](len);
        address token;
        for (uint256 i = 0; i < len; ) {
            token = tokens[i];
            allocatedTokens[i] =
                (userTokenAmount[user][token] * amountLP) /
                balanceOf(user);
            unchecked {
                i++;
            }
        }
    }

    function emitTokenAmouts() internal {
        uint256 len = tokens.length;
        uint256[] memory tokenAmounts = new uint256[](len);
        for (uint256 i = 0; i < len; ) {
            tokenAmounts[i] = totalTokenAmount[tokens[i]];
            unchecked {
                i++;
            }
        }
        emit TokensAmounts(tokens, tokenAmounts);
    }

    /// @notice total amount of tokens
    function tokensLength() public view returns (uint256) {
        return tokens.length;
    }

    /// @notice the tokens array and it`s allocations
    function getTokensAndAllocation()
        public
        view
        returns (address[] memory, uint256[] memory)
    {
        return (tokens, allocations);
    }

    /// @notice returns capitalization decimals
    function capitalizationDecimals() public pure returns (uint8) {
        return 6;
    }

    /// @notice return LP based on depositTokenAmount
    function calcMintAmount(
        address depositToken,
        uint256 depositTokenAmount
    ) public view returns (uint256) {
        uint8 depositTokenDecimals = IToken(depositToken).decimals();
        if (decimals() >= depositTokenDecimals) {
            return
                depositTokenAmount * 10 ** (decimals() - depositTokenDecimals);
        } else {
            return
                depositTokenAmount / 10 ** (depositTokenDecimals - decimals());
        }
    }

    /// @notice returns user amounts
    function getUserTokenAmounts(
        address user
    ) public view returns (uint256[] memory tokenAmounts) {
        uint256 tokenLen = tokens.length;
        tokenAmounts = new uint256[](tokenLen);
        for (uint256 i = 0; i < tokenLen; i++) {
            tokenAmounts[i] = userTokenAmount[user][tokens[i]];
        }
    }

    function decodeSwapData(
        bytes calldata data
    )
        public
        view
        returns (
            address sender,
            address receiver,
            address srcToken,
            uint256 srcTokenAmount,
            uint256 minReturnAmount
        )
    {
        bytes4 selector;
        assembly {
            selector := calldataload(data.offset)
        }

        /// @dev `0x0502b1c5` - unoswap selector
        if (selector == bytes4(0x0502b1c5)) {
            (srcToken, srcTokenAmount, minReturnAmount, ) = abi.decode(
                data[4:],
                (address, uint256, uint256, uint256[])
            );
            sender = address(this);
            receiver = address(this);
        }
        /// @dev `0x12aa3caf` - swap selector
        else if (selector == bytes4(0x12aa3caf)) {
            (address executor, SwapDescription memory desc, , ) = abi.decode(
                data[4:], // Skip selector (4 bytes)
                (address, SwapDescription, bytes, bytes)
            );

            sender = executor;
            receiver = desc.dstReceiver;
            srcToken = address(desc.srcToken);
            srcTokenAmount = desc.amount;
            minReturnAmount = desc.minReturnAmount;
        } else if (selector == bytes4(0xe449022e)) {
            uint256[] memory pools;
            (srcTokenAmount, minReturnAmount, pools) = abi.decode(
                data[4:], // Skip selector (4 bytes)
                (uint256, uint256, uint256[])
            );

            address token0 = IUniswapV3Pool(address(uint160(pools[0])))
                .token0();

            if (!xstakingFactory.isDepositToken(token0)) {
                srcToken = IUniswapV3Pool(address(uint160(pools[0]))).token1();
            } else {
                srcToken = IUniswapV3Pool(address(uint160(pools[0]))).token0();
            }
            sender = address(this);
            receiver = address(this);
        } else if (selector == bytes4(0x62e238bb)) {
            (
                Order memory order,
                ,
                ,
                uint256 makingAmount,
                uint256 takingAmount,

            ) = abi.decode(
                    data[4:], // Skip selector (4 bytes)
                    (Order, bytes, bytes, uint256, uint256, uint256)
                );

            sender = address(this);
            receiver = address(this);
            srcToken = order.takerAsset;
            srcTokenAmount = takingAmount;
            minReturnAmount = makingAmount;
        } else {
            revert("XStakingFactory: unknown selector");
        }
    }

    function _checkIfOwnerHaveLockedTokens() internal view {
        if (msg.sender != owner()) {
            require(
                balanceOf(owner()) > 0,
                "XStakingPool: Pool is locked such as pool's owner withdrawn all investments"
            );
        }
    }

    function _setDepositFeeNumerator(
        uint256 _depositFeeNumerator,
        bool _isPercentageFee
    ) internal {
        if (_isPercentageFee) {
            require(
                _depositFeeNumerator < FEE_DENOMINATOR &&
                    _depositFeeNumerator >= MIN_PERCENT_DEPOSIT_FEE,
                "XStakingPool: Deposit fee numerator is out of range"
            );
        } else {
            require(
                _depositFeeNumerator >= MIN_FIXED_DEPOSIT_FEE &&
                    _depositFeeNumerator <= MAX_FIXED_DEPOSIT_FEE,
                "XStakingPool: Deposit fee numerator is out of range"
            );
        }

        depositFeeNumerator = _depositFeeNumerator;
        isPercentageFee = _isPercentageFee;

        emit DepositFeeNumeratorSet(_depositFeeNumerator, _isPercentageFee);
    }
}
"
    },
    "contracts/interfaces/IToken.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";

interface IToken is IERC20Metadata {


}"
    },
    "contracts/interfaces/IXStakingPool.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

interface IXStakingPool {
    struct SwapDescription {
        IERC20 srcToken;
        IERC20 dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
    }

    struct Order {
        uint256 salt;
        address makerAsset;
        address takerAsset;
        address maker;
        address receiver;
        address allowedSender; // equals to Zero address on public orders
        uint256 makingAmount;
        uint256 takingAmount;
        uint256 offsets;
        bytes interactions; // concat(makerAssetData, takerAssetData, getMakingAmount, getTakingAmount, predicate, permit, preIntercation, postInteraction)
    }

    event TokenSwap(
        address token,
        uint256 tokenAmount,
        uint256 baseTokenAmount
    );

    event TokensAmounts(address[] tokens, uint256[] tokenAmounts);

    event Volume(uint256 amount);

    event PoolCapitalization(address pool, uint256 capitalization);

    event UserCapitalization(
        address pool,
        address user,
        uint256 capitalization
    );

    event Deposit(
        address pool,
        address depositor,
        uint256 amount,
        uint256[] userTokenAmounts
    );

    event Withdraw(
        address pool,
        address depositor,
        uint256 amount,
        uint256[] userTokenAmounts
    );

    event DepositStatusUpdated(bool isPaused);

    function initialize(
        uint256 _poolId,
        address _poolOwner,
        uint256 _capitalizationCap,
        address[] memory _tokens,
        uint256[] memory _allocations,
        uint256 _profitSharingFeeNumerator,
        uint256[] memory _tokensAmounts,
        address initialDepositToken,
        uint256 initialBaseTokenAmount,
        uint256 _depositFeeNumerator,
        bool _isPercentageFee
    ) external;

    function deposit(
        address depositToken,
        uint256 baseTokenAmount,
        bytes[] calldata oneInchSwapData
    ) external returns (uint256);

    function depositTo(
        address depositToken,
        address to,
        uint256 baseTokenAmount,
        bytes[] calldata oneInchSwapData
    ) external returns (uint256);

    function withdraw(
        address depositToken,
        uint256 amountLP,
        bytes[] calldata oneInchSwapData
    ) external returns (uint256);

    event DepositFeeNumeratorSet(uint256 feeNumerator, bool isPercentageFee);
}
"
    },
    "contracts/interfaces/IXBRStakingPool.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IXBRStakingPool {
    event Stake(address user, address lp,  uint256 stakeAmount);
    event Unstake(address user, address lp, uint256 unstakeAmount);
    event ClaimReward(address user, uint256 claimedRewardAmount);
    event Exit(address user, address lp, uint256 exitAmount);

    function getLockedTokensByUserAndPool(address user, address pool) external view returns (uint256);
}"
    },
    "contracts/interfaces/IXStakingFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

interface IXStakingFactory {
    /// @notice argument for oneInch swap function.
    struct SwapDescription {
        IERC20 srcToken;
        IERC20 dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
    }

    struct Order {
        uint256 salt;
        address makerAsset;
        address takerAsset;
        address maker;
        address receiver;
        address allowedSender; // equals to Zero address on public orders
        uint256 makingAmount;
        uint256 takingAmount;
        uint256 offsets;
        bytes interactions; // concat(makerAssetData, takerAssetData, getMakingAmount, getTakingAmount, predicate, permit, preIntercation, postInteraction)
    }

    function FEE_DENOMINATOR() external view returns (uint256);

    function treasuryWallet() external view returns (address);

    function oneInchRouter() external view returns (address);

    function getDepositToken(uint8 index) external view returns (address);

    function getXStakingPools() external view returns (address[] memory);

    function isDepositToken(address token) external view returns (bool);

    function isXStakingPool(address pool) external view returns (bool);

    function decodeSwapData(
        bytes calldata data
    )
        external
        returns (
            address sender,
            address receiver,
            address srcToken,
            uint256 srcTokenAmount,
            uint256 minReturnAmount
        );

    function calculateFeeAmount(
        uint256 amount,
        bool isDeposit
    ) external view returns (uint256);

    event DeployPool(
        address deployer,
        address pool,
        uint256 poolId,
        address[] tokens,
        uint256[] allocations,
        string description,
        uint256 capitalizationCap,
        uint256 profitSharingFeeNumerator,
        uint256 depositFeeNumerator,
        bool isPercentageFee
    );
}
"
    },
    "node_modules/@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
"
    },
    "node_modules/@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
    IUniswapV3PoolImmutables,
    IUniswapV3PoolState,
    IUniswapV3PoolDerivedState,
    IUniswapV3PoolActions,
    IUniswapV3PoolOwnerActions,
    IUniswapV3PoolEvents
{

}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

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

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
    struct ReentrancyGuardStorage {
        uint256 _status;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
        assembly {
            $.slot := ReentrancyGuardStorageLocation
        }
    }

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        $._status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if ($._status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        $._status = ENTERED;
    }

    function _nonReentrantAfter() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        $._status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        return $._status == ENTERED;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
    /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
    struct ERC20Storage {
        mapping(address account => uint256) _balances;

        mapping(address account => mapping(address spender => uint256)) _allowances;

        uint256 _totalSupply;

        string _name;
        string _symbol;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;

    function _getERC20Storage() private pure returns (ERC20Storage storage $) {
        assembly {
            $.slot := ERC20StorageLocation
        }
    }

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        ERC20Storage storage $ = _getERC20Storage();
        $._name = name_;
        $._symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address 

Tags:
ERC20, Multisig, Mintable, Burnable, Swap, Liquidity, Staking, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x0598da39505f5154c8cb3f6bb972b72969cb4fb1|verified:true|block:23671239|tx:0xb8960ab20c721cff9cd6acf26a1c24a269993c598d96f8135ee69f6e33def503|first_check:1761642121

Submitted on: 2025-10-28 10:02:03

Comments

Log in to comment.

No comments yet.