ETH Price: $2,415.54 (-4.45%)
Gas: 0.26 Gwei

Transaction Decoder

Block:
19626748 at Apr-10-2024 05:34:11 PM +UTC
Transaction Fee:
0.002449679739717596 ETH $5.92
Gas Used:
93,647 Gas / 26.158656868 Gwei

Emitted Events:

125 Stone.Transfer( from=[Sender] 0x660e5dac34b916b8f060b817cbe8660ba02bbc4f, to=[Receiver] StoneVault, value=249447000000000000 )
126 StoneVault.InitiateWithdraw( account=[Sender] 0x660e5dac34b916b8f060b817cbe8660ba02bbc4f, shares=249447000000000000, round=91 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
5.906788991322930981 Eth5.906793673672930981 Eth0.00000468235
0x660E5Dac...ba02Bbc4F
0.200893334383899972 Eth
Nonce: 813
0.198443654644182376 Eth
Nonce: 814
0.002449679739717596
0x71229856...2b145bD3C
0xA62F9C5a...d81B90572
(StakeStone: Stone Vault)

Execution Trace

StoneVault.requestWithdraw( _shares=249447000000000000 )
  • Stone.balanceOf( account=0x660E5Dac34b916B8f060b817cBE8660ba02Bbc4F ) => ( 249447000000000000 )
  • Stone.transferFrom( from=0x660E5Dac34b916B8f060b817cBE8660ba02Bbc4F, to=0xA62F9C5af106FeEE069F38dE51098D9d81B90572, amount=249447000000000000 ) => ( True )
    File 1 of 2: StoneVault
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
    import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
    import {Minter} from "./token/Minter.sol";
    import {Stone} from "./token/Stone.sol";
    import {AssetsVault} from "./AssetsVault.sol";
    import {StrategyController} from "./strategies/StrategyController.sol";
    import {VaultMath} from "./libraries/VaultMath.sol";
    contract StoneVault is ReentrancyGuard, Ownable {
        uint256 internal constant MULTIPLIER = 1e18;
        uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
        uint256 internal constant MAXMIUM_FEE_RATE = ONE_HUNDRED_PERCENT / 100; // 1%
        uint256 internal constant MINIMUM_REBASE_INTERVAL = 7 * 24 * 60 * 60;
        uint256 public constant VERSION = 1;
        uint256 public rebaseTimeInterval = 24 * 60 * 60;
        address public immutable minter;
        address public immutable stone;
        address payable public immutable strategyController;
        address payable public immutable assetsVault;
        address public proposal;
        address public feeRecipient;
        uint256 public latestRoundID;
        uint256 public withdrawableAmountInPast;
        uint256 public withdrawingSharesInPast;
        uint256 public withdrawingSharesInRound;
        uint256 public withdrawFeeRate;
        uint256 public rebaseTime;
        mapping(uint256 => uint256) public roundPricePerShare;
        mapping(uint256 => uint256) public settlementTime;
        mapping(address => UserReceipt) public userReceipts;
        struct UserReceipt {
            uint256 withdrawRound;
            uint256 withdrawShares;
            uint256 withdrawableAmount;
        }
        event Deposit(
            address indexed account,
            uint256 amount,
            uint256 mint,
            uint256 round
        );
        event InitiateWithdraw(
            address indexed account,
            uint256 shares,
            uint256 round
        );
        event CancelWithdraw(
            address indexed account,
            uint256 amount,
            uint256 round
        );
        event Withdrawn(address indexed account, uint256 amount, uint256 round);
        event WithdrawnFromStrategy(
            address indexed account,
            uint256 amount,
            uint256 actualAmount,
            uint256 round
        );
        event RollToNextRound(
            uint256 round,
            uint256 vaultIn,
            uint256 vaultOut,
            uint256 sharePrice
        );
        event StragetyAdded(address strategy);
        event StragetyDestroyed(address strategy);
        event StragetyCleared(address strategy);
        event PortfolioConfigUpdated(address[] strategies, uint256[] ratios);
        event FeeCharged(address indexed account, uint256 amount);
        event SetWithdrawFeeRate(uint256 oldRate, uint256 newRate);
        event SetFeeRecipient(address oldAddr, address newAddr);
        event SetRebaseInterval(uint256 interval);
        modifier onlyProposal() {
            require(proposal == msg.sender, "not proposal");
            _;
        }
        constructor(
            address _minter,
            address _proposal,
            address payable _assetsVault,
            address[] memory _strategies,
            uint256[] memory _ratios
        ) {
            require(
                _minter != address(0) &&
                    _proposal != address(0) &&
                    _assetsVault != address(0),
                "ZERO ADDRESS"
            );
            uint256 length = _strategies.length;
            for (uint256 i; i < length; i++) {
                require(_strategies[i] != address(0), "ZERO ADDRESS");
            }
            minter = _minter;
            proposal = _proposal;
            assetsVault = _assetsVault;
            feeRecipient = msg.sender;
            StrategyController controller = new StrategyController(
                _assetsVault,
                _strategies,
                _ratios
            );
            strategyController = payable(address(controller));
            stone = Minter(_minter).stone();
            roundPricePerShare[0] = MULTIPLIER;
            latestRoundID = 0;
        }
        function deposit()
            external
            payable
            nonReentrant
            returns (uint256 mintAmount)
        {
            mintAmount = _depositFor(msg.value, msg.sender);
        }
        function depositFor(
            address _user
        ) external payable nonReentrant returns (uint256 mintAmount) {
            mintAmount = _depositFor(msg.value, _user);
        }
        function _depositFor(
            uint256 _amount,
            address _user
        ) internal returns (uint256 mintAmount) {
            require(_amount != 0, "too small");
            uint256 sharePrice;
            uint256 currSharePrice = currentSharePrice();
            if (latestRoundID == 0) {
                sharePrice = MULTIPLIER;
            } else {
                uint256 latestSharePrice = roundPricePerShare[latestRoundID - 1];
                sharePrice = latestSharePrice > currSharePrice
                    ? latestSharePrice
                    : currSharePrice;
            }
            mintAmount = (_amount * MULTIPLIER) / sharePrice;
            AssetsVault(assetsVault).deposit{value: address(this).balance}();
            Minter(minter).mint(_user, mintAmount);
            emit Deposit(_user, _amount, mintAmount, latestRoundID);
        }
        function requestWithdraw(uint256 _shares) external nonReentrant {
            require(_shares != 0, "too small");
            require(latestRoundID != 0, "should withdraw instantly");
            Stone stoneToken = Stone(stone);
            Minter stoneMinter = Minter(minter);
            require(stoneToken.balanceOf(msg.sender) >= _shares, "exceed balance");
            TransferHelper.safeTransferFrom(
                stone,
                msg.sender,
                address(this),
                _shares
            );
            withdrawingSharesInRound = withdrawingSharesInRound + _shares;
            UserReceipt storage receipt = userReceipts[msg.sender];
            if (receipt.withdrawRound == latestRoundID) {
                receipt.withdrawShares = receipt.withdrawShares + _shares;
            } else if (receipt.withdrawRound == 0) {
                receipt.withdrawShares = _shares;
                receipt.withdrawRound = latestRoundID;
            } else {
                // Withdraw previous round share first
                uint256 withdrawAmount = VaultMath.sharesToAsset(
                    receipt.withdrawShares,
                    roundPricePerShare[receipt.withdrawRound]
                );
                stoneMinter.burn(address(this), receipt.withdrawShares);
                withdrawingSharesInPast =
                    withdrawingSharesInPast -
                    receipt.withdrawShares;
                receipt.withdrawShares = _shares;
                receipt.withdrawableAmount =
                    receipt.withdrawableAmount +
                    withdrawAmount;
                receipt.withdrawRound = latestRoundID;
            }
            emit InitiateWithdraw(msg.sender, _shares, latestRoundID);
        }
        function cancelWithdraw(uint256 _shares) external nonReentrant {
            require(_shares != 0, "too small");
            UserReceipt storage receipt = userReceipts[msg.sender];
            require(receipt.withdrawRound == latestRoundID, "no pending withdraw");
            require(receipt.withdrawShares >= _shares, "exceed pending withdraw");
            receipt.withdrawShares = receipt.withdrawShares - _shares;
            TransferHelper.safeTransfer(stone, msg.sender, _shares);
            if (receipt.withdrawShares == 0) {
                receipt.withdrawRound = 0;
            }
            withdrawingSharesInRound = withdrawingSharesInRound - _shares;
            emit CancelWithdraw(msg.sender, _shares, latestRoundID);
        }
        function instantWithdraw(
            uint256 _amount,
            uint256 _shares
        ) external nonReentrant returns (uint256 actualWithdrawn) {
            require(_amount != 0 || _shares != 0, "too small");
            AssetsVault aVault = AssetsVault(assetsVault);
            Minter stoneMinter = Minter(minter);
            (uint256 idleAmount, ) = getVaultAvailableAmount();
            if (_amount != 0) {
                UserReceipt storage receipt = userReceipts[msg.sender];
                if (
                    receipt.withdrawRound != latestRoundID &&
                    receipt.withdrawRound != 0
                ) {
                    // Withdraw previous round share first
                    uint256 withdrawAmount = VaultMath.sharesToAsset(
                        receipt.withdrawShares,
                        roundPricePerShare[receipt.withdrawRound]
                    );
                    stoneMinter.burn(address(this), receipt.withdrawShares);
                    withdrawingSharesInPast =
                        withdrawingSharesInPast -
                        receipt.withdrawShares;
                    receipt.withdrawShares = 0;
                    receipt.withdrawableAmount =
                        receipt.withdrawableAmount +
                        withdrawAmount;
                    receipt.withdrawRound = 0;
                }
                require(
                    receipt.withdrawableAmount >= _amount,
                    "exceed withdrawable"
                );
                receipt.withdrawableAmount = receipt.withdrawableAmount - _amount;
                withdrawableAmountInPast = withdrawableAmountInPast - _amount;
                actualWithdrawn = _amount;
                emit Withdrawn(msg.sender, _amount, latestRoundID);
            }
            if (_shares != 0) {
                uint256 sharePrice;
                if (latestRoundID == 0) {
                    sharePrice = MULTIPLIER;
                } else {
                    uint256 currSharePrice = currentSharePrice();
                    uint256 latestSharePrice = roundPricePerShare[
                        latestRoundID - 1
                    ];
                    sharePrice = latestSharePrice < currSharePrice
                        ? latestSharePrice
                        : currSharePrice;
                }
                uint256 ethAmount = VaultMath.sharesToAsset(_shares, sharePrice);
                stoneMinter.burn(msg.sender, _shares);
                if (ethAmount <= idleAmount) {
                    actualWithdrawn = actualWithdrawn + ethAmount;
                    emit Withdrawn(msg.sender, ethAmount, latestRoundID);
                } else {
                    actualWithdrawn = actualWithdrawn + idleAmount;
                    ethAmount = ethAmount - idleAmount;
                    StrategyController controller = StrategyController(
                        strategyController
                    );
                    uint256 actualAmount = controller.forceWithdraw(ethAmount);
                    actualWithdrawn = actualWithdrawn + actualAmount;
                    emit WithdrawnFromStrategy(
                        msg.sender,
                        ethAmount,
                        actualAmount,
                        latestRoundID
                    );
                }
            }
            require(aVault.getBalance() >= actualWithdrawn, "still need wait");
            uint256 withFee;
            if (withdrawFeeRate != 0) {
                withFee = (actualWithdrawn * withdrawFeeRate) / ONE_HUNDRED_PERCENT;
                aVault.withdraw(feeRecipient, withFee);
                emit FeeCharged(msg.sender, withFee);
            }
            aVault.withdraw(msg.sender, actualWithdrawn - withFee);
        }
        function rollToNextRound() external {
            require(
                block.timestamp > rebaseTime + rebaseTimeInterval,
                "already rebased"
            );
            StrategyController controller = StrategyController(strategyController);
            AssetsVault aVault = AssetsVault(assetsVault);
            uint256 previewSharePrice = currentSharePrice();
            uint256 vaultBalance = aVault.getBalance();
            uint256 amountToWithdraw = VaultMath.sharesToAsset(
                withdrawingSharesInRound,
                previewSharePrice
            );
            uint256 amountVaultNeed = withdrawableAmountInPast + amountToWithdraw;
            uint256 allPendingValue = controller.getAllStrategyPendingValue();
            uint256 vaultIn;
            uint256 vaultOut;
            if (vaultBalance > amountVaultNeed) {
                vaultIn = vaultBalance - amountVaultNeed;
            } else if (vaultBalance + allPendingValue < amountVaultNeed) {
                vaultOut = amountVaultNeed - vaultBalance - allPendingValue;
            }
            controller.rebaseStrategies(vaultIn, vaultOut);
            uint256 newSharePrice = currentSharePrice();
            roundPricePerShare[latestRoundID] = previewSharePrice < newSharePrice
                ? previewSharePrice
                : newSharePrice;
            settlementTime[latestRoundID] = block.timestamp;
            latestRoundID = latestRoundID + 1;
            withdrawingSharesInPast =
                withdrawingSharesInPast +
                withdrawingSharesInRound;
            withdrawableAmountInPast =
                withdrawableAmountInPast +
                VaultMath.sharesToAsset(withdrawingSharesInRound, newSharePrice);
            withdrawingSharesInRound = 0;
            rebaseTime = block.timestamp;
            emit RollToNextRound(latestRoundID, vaultIn, vaultOut, newSharePrice);
        }
        function addStrategy(address _strategy) external onlyProposal {
            StrategyController controller = StrategyController(strategyController);
            controller.addStrategy(_strategy);
            emit StragetyAdded(_strategy);
        }
        function destroyStrategy(address _strategy) external onlyOwner {
            StrategyController controller = StrategyController(strategyController);
            controller.destroyStrategy(_strategy);
            emit StragetyDestroyed(_strategy);
        }
        function clearStrategy(address _strategy) external onlyOwner {
            StrategyController controller = StrategyController(strategyController);
            controller.clearStrategy(_strategy);
            emit StragetyCleared(_strategy);
        }
        function updatePortfolioConfig(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) external onlyProposal {
            StrategyController controller = StrategyController(strategyController);
            controller.setStrategies(_strategies, _ratios);
            emit PortfolioConfigUpdated(_strategies, _ratios);
        }
        function updateProposal(address _proposal) external onlyProposal {
            proposal = _proposal;
        }
        function migrateVault(address _vault) external onlyProposal {
            Minter(minter).setNewVault(_vault);
            AssetsVault(assetsVault).setNewVault(_vault);
            StrategyController(strategyController).setNewVault(_vault);
        }
        function currentSharePrice() public returns (uint256 price) {
            Stone stoneToken = Stone(stone);
            uint256 totalStone = stoneToken.totalSupply();
            if (
                latestRoundID == 0 ||
                totalStone == 0 ||
                totalStone == withdrawingSharesInPast
            ) {
                return MULTIPLIER;
            }
            uint256 etherAmount = AssetsVault(assetsVault).getBalance() +
                StrategyController(strategyController).getAllStrategiesValue() -
                withdrawableAmountInPast;
            uint256 activeShare = totalStone - withdrawingSharesInPast;
            return (etherAmount * MULTIPLIER) / activeShare;
        }
        function getVaultAvailableAmount()
            public
            returns (uint256 idleAmount, uint256 investedAmount)
        {
            AssetsVault vault = AssetsVault(assetsVault);
            if (vault.getBalance() > withdrawableAmountInPast) {
                idleAmount = vault.getBalance() - withdrawableAmountInPast;
            }
            investedAmount = StrategyController(strategyController)
                .getAllStrategyValidValue();
        }
        function setWithdrawFeeRate(uint256 _withdrawFeeRate) external onlyOwner {
            require(_withdrawFeeRate <= MAXMIUM_FEE_RATE, "exceed maximum");
            emit SetWithdrawFeeRate(withdrawFeeRate, _withdrawFeeRate);
            withdrawFeeRate = _withdrawFeeRate;
        }
        function setFeeRecipient(address _feeRecipient) external onlyOwner {
            require(_feeRecipient != address(0), "zero address");
            emit SetFeeRecipient(feeRecipient, _feeRecipient);
            feeRecipient = _feeRecipient;
        }
        function setRebaseInterval(uint256 _interval) external onlyOwner {
            require(_interval <= MINIMUM_REBASE_INTERVAL, "invalid");
            rebaseTimeInterval = _interval;
            emit SetRebaseInterval(rebaseTimeInterval);
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import "@layerzerolabs/solidity-examples/contracts/token/oft/extension/BasedOFT.sol";
    import {Minter} from "./Minter.sol";
    contract Stone is BasedOFT {
        uint256 public constant DAY_INTERVAL = 24 * 60 * 60;
        address public minter;
        uint16 public constant PT_FEED = 1;
        uint16 public constant PT_SET_ENABLE = 2;
        uint16 public constant PT_SET_CAP = 3;
        uint256 public cap;
        bool public enable = true;
        mapping(uint256 => uint256) public quota;
        event FeedToChain(
            uint16 indexed dstChainId,
            address indexed from,
            bytes toAddress,
            uint price
        );
        event SetCapFor(uint16 indexed dstChainId, bytes toAddress, uint cap);
        event SetEnableFor(uint16 indexed dstChainId, bytes toAddress, bool flag);
        constructor(
            address _minter,
            address _layerZeroEndpoint,
            uint256 _cap
        ) BasedOFT("StakeStone Ether", "STONE", _layerZeroEndpoint) {
            minter = _minter;
            cap = _cap;
        }
        modifier onlyMinter() {
            require(msg.sender == minter, "NM");
            _;
        }
        function mint(address _to, uint256 _amount) external onlyMinter {
            _mint(_to, _amount);
        }
        function burn(address _from, uint256 _amount) external onlyMinter {
            _burn(_from, _amount);
        }
        function sendFrom(
            address _from,
            uint16 _dstChainId,
            bytes calldata _toAddress,
            uint _amount,
            address payable _refundAddress,
            address _zroPaymentAddress,
            bytes calldata _adapterParams
        ) public payable override(IOFTCore, OFTCore) {
            require(enable, "invalid");
            uint256 id;
            assembly {
                id := chainid()
            }
            require(id != _dstChainId, "same chain");
            uint256 day = block.timestamp / DAY_INTERVAL;
            require(_amount + quota[day] <= cap, "Exceed cap");
            quota[day] = quota[day] + _amount;
            super.sendFrom(
                _from,
                _dstChainId,
                _toAddress,
                _amount,
                _refundAddress,
                _zroPaymentAddress,
                _adapterParams
            );
        }
        function updatePrice(
            uint16 _dstChainId,
            bytes memory _toAddress
        ) external payable returns (uint256 price) {
            require(enable, "invalid");
            uint256 id;
            assembly {
                id := chainid()
            }
            require(id != _dstChainId, "same chain");
            price = tokenPrice();
            bytes memory lzPayload = abi.encode(
                PT_FEED,
                _toAddress,
                price,
                block.timestamp
            );
            _lzSend(
                _dstChainId,
                lzPayload,
                payable(msg.sender),
                address(0),
                bytes(""),
                msg.value
            );
            emit FeedToChain(_dstChainId, msg.sender, _toAddress, price);
        }
        function setEnableFor(
            uint16 _dstChainId,
            bool _flag,
            bytes memory _toAddress
        ) external payable onlyOwner {
            uint256 id;
            assembly {
                id := chainid()
            }
            if (_dstChainId == id) {
                enable = _flag;
                emit SetEnableFor(
                    _dstChainId,
                    abi.encodePacked(address(this)),
                    enable
                );
                return;
            }
            bytes memory lzPayload = abi.encode(PT_SET_ENABLE, _toAddress, _flag);
            _lzSend(
                _dstChainId,
                lzPayload,
                payable(msg.sender),
                address(0),
                bytes(""),
                msg.value
            );
            emit SetEnableFor(_dstChainId, _toAddress, _flag);
        }
        function setCapFor(
            uint16 _dstChainId,
            uint256 _cap,
            bytes memory _toAddress
        ) external payable onlyOwner {
            uint256 id;
            assembly {
                id := chainid()
            }
            if (_dstChainId == id) {
                cap = _cap;
                emit SetCapFor(_dstChainId, abi.encodePacked(address(this)), cap);
                return;
            }
            bytes memory lzPayload = abi.encode(PT_SET_CAP, _toAddress, _cap);
            _lzSend(
                _dstChainId,
                lzPayload,
                payable(msg.sender),
                address(0),
                bytes(""),
                msg.value
            );
            emit SetCapFor(_dstChainId, _toAddress, _cap);
        }
        function tokenPrice() public returns (uint256 price) {
            price = Minter(minter).getTokenPrice();
        }
        function getQuota() external view returns (uint256) {
            uint256 amount = quota[block.timestamp / DAY_INTERVAL];
            if (cap > amount && enable) {
                return cap - amount;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {Stone} from "./Stone.sol";
    import {StoneVault} from "../StoneVault.sol";
    contract Minter {
        // TODO: governable upgrade
        address public stone;
        address payable public vault;
        modifier onlyVault() {
            require(msg.sender == vault, "not vault");
            _;
        }
        constructor(address _stone, address payable _vault) {
            stone = _stone;
            vault = _vault;
        }
        function mint(address _to, uint256 _amount) external onlyVault {
            Stone(stone).mint(_to, _amount);
        }
        function burn(address _from, uint256 _amount) external onlyVault {
            Stone(stone).burn(_from, _amount);
        }
        function setNewVault(address _vault) external onlyVault {
            vault = payable(_vault);
        }
        function getTokenPrice() public returns (uint256 price) {
            price = StoneVault(vault).currentSharePrice();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
    import {Strategy} from "./Strategy.sol";
    import {AssetsVault} from "../AssetsVault.sol";
    contract StrategyController {
        using EnumerableSet for EnumerableSet.AddressSet;
        uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
        address public stoneVault;
        address payable public immutable assetsVault;
        EnumerableSet.AddressSet private strategies;
        mapping(address => uint256) public ratios;
        struct StrategyDiff {
            address strategy;
            bool isDeposit;
            uint256 amount;
        }
        modifier onlyVault() {
            require(stoneVault == msg.sender, "not vault");
            _;
        }
        constructor(
            address payable _assetsVault,
            address[] memory _strategies,
            uint256[] memory _ratios
        ) {
            require(_assetsVault != address(0), "ZERO ADDRESS");
            uint256 length = _strategies.length;
            for (uint256 i; i < length; i++) {
                require(_strategies[i] != address(0), "ZERO ADDRESS");
            }
            stoneVault = msg.sender;
            assetsVault = _assetsVault;
            _initStrategies(_strategies, _ratios);
        }
        function onlyRebaseStrategies() external {
            _rebase(0, 0);
        }
        function forceWithdraw(
            uint256 _amount
        ) external onlyVault returns (uint256 actualAmount) {
            uint256 balanceBeforeRepay = address(this).balance;
            if (balanceBeforeRepay >= _amount) {
                _repayToVault();
                actualAmount = balanceBeforeRepay;
            } else {
                actualAmount =
                    _forceWithdraw(_amount - balanceBeforeRepay) +
                    balanceBeforeRepay;
            }
        }
        function setStrategies(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) external onlyVault {
            _setStrategies(_strategies, _ratios);
        }
        function addStrategy(address _strategy) external onlyVault {
            require(!strategies.contains(_strategy), "already exist");
            strategies.add(_strategy);
        }
        function rebaseStrategies(
            uint256 _in,
            uint256 _out
        ) external payable onlyVault {
            _rebase(_in, _out);
        }
        function destroyStrategy(address _strategy) external onlyVault {
            _destoryStrategy(_strategy);
        }
        function _rebase(uint256 _in, uint256 _out) internal {
            require(_in == 0 || _out == 0, "only deposit or withdraw");
            if (_in != 0) {
                AssetsVault(assetsVault).withdraw(address(this), _in);
            }
            uint256 total = getAllStrategyValidValue();
            if (total < _out) {
                total = 0;
            } else {
                total = total + _in - _out;
            }
            uint256 length = strategies.length();
            StrategyDiff[] memory diffs = new StrategyDiff[](length);
            uint256 head;
            uint256 tail = length - 1;
            for (uint i; i < length; i++) {
                address strategy = strategies.at(i);
                if (ratios[strategy] == 0) {
                    _clearStrategy(strategy, true);
                    continue;
                }
                uint256 newPosition = (total * ratios[strategy]) /
                    ONE_HUNDRED_PERCENT;
                uint256 position = getStrategyValidValue(strategy);
                if (newPosition < position) {
                    diffs[head] = StrategyDiff(
                        strategy,
                        false,
                        position - newPosition
                    );
                    head++;
                } else if (newPosition > position) {
                    diffs[tail] = StrategyDiff(
                        strategy,
                        true,
                        newPosition - position
                    );
                    if (tail != 0) {
                        tail--;
                    }
                }
            }
            length = diffs.length;
            for (uint256 i; i < length; i++) {
                StrategyDiff memory diff = diffs[i];
                if (diff.amount == 0) {
                    continue;
                }
                if (diff.isDeposit) {
                    if (address(this).balance < diff.amount) {
                        diff.amount = address(this).balance;
                    }
                    _depositToStrategy(diff.strategy, diff.amount);
                } else {
                    _withdrawFromStrategy(diff.strategy, diff.amount);
                }
            }
            _repayToVault();
        }
        function _repayToVault() internal {
            if (address(this).balance != 0) {
                TransferHelper.safeTransferETH(assetsVault, address(this).balance);
            }
        }
        function _depositToStrategy(address _strategy, uint256 _amount) internal {
            Strategy(_strategy).deposit{value: _amount}();
        }
        function _withdrawFromStrategy(
            address _strategy,
            uint256 _amount
        ) internal {
            Strategy(_strategy).withdraw(_amount);
        }
        function _forceWithdraw(
            uint256 _amount
        ) internal returns (uint256 actualAmount) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                address strategy = strategies.at(i);
                uint256 withAmount = (_amount * ratios[strategy]) /
                    ONE_HUNDRED_PERCENT;
                if (withAmount != 0) {
                    actualAmount =
                        Strategy(strategy).instantWithdraw(withAmount) +
                        actualAmount;
                }
            }
            _repayToVault();
        }
        function getStrategyValue(
            address _strategy
        ) public returns (uint256 _value) {
            return Strategy(_strategy).getAllValue();
        }
        function getStrategyValidValue(
            address _strategy
        ) public returns (uint256 _value) {
            return Strategy(_strategy).getInvestedValue();
        }
        function getStrategyPendingValue(
            address _strategy
        ) public returns (uint256 _value) {
            return Strategy(_strategy).getPendingValue();
        }
        function getAllStrategiesValue() public returns (uint256 _value) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                _value = _value + getStrategyValue(strategies.at(i));
            }
        }
        function getAllStrategyValidValue() public returns (uint256 _value) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                _value = _value + getStrategyValidValue(strategies.at(i));
            }
        }
        function getAllStrategyPendingValue() public returns (uint256 _value) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                _value = _value + getStrategyPendingValue(strategies.at(i));
            }
        }
        function getStrategies()
            public
            view
            returns (address[] memory addrs, uint256[] memory portions)
        {
            uint256 length = strategies.length();
            addrs = new address[](length);
            portions = new uint256[](length);
            for (uint256 i; i < length; i++) {
                address addr = strategies.at(i);
                addrs[i] = addr;
                portions[i] = ratios[addr];
            }
        }
        function _initStrategies(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) internal {
            require(_strategies.length == _ratios.length, "invalid length");
            uint256 totalRatio;
            uint256 length = _strategies.length;
            for (uint i; i < length; i++) {
                strategies.add(_strategies[i]);
                ratios[_strategies[i]] = _ratios[i];
                totalRatio = totalRatio + _ratios[i];
            }
            require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
        }
        function _setStrategies(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) internal {
            uint256 length = _strategies.length;
            require(length == _ratios.length, "invalid length");
            uint256 oldLength = strategies.length();
            for (uint i; i < oldLength; i++) {
                ratios[strategies.at(i)] = 0;
            }
            uint256 totalRatio;
            for (uint i; i < length; i++) {
                require(
                    Strategy(_strategies[i]).controller() == address(this),
                    "controller mismatch"
                );
                strategies.add(_strategies[i]);
                ratios[_strategies[i]] = _ratios[i];
                totalRatio = totalRatio + _ratios[i];
            }
            require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
        }
        function clearStrategy(address _strategy) public onlyVault {
            _clearStrategy(_strategy, false);
        }
        function _clearStrategy(address _strategy, bool _isRebase) internal {
            Strategy(_strategy).clear();
            if (!_isRebase) {
                _repayToVault();
            }
        }
        function _destoryStrategy(address _strategy) internal {
            require(_couldDestroyStrategy(_strategy), "still active");
            strategies.remove(_strategy);
            _repayToVault();
        }
        function _couldDestroyStrategy(
            address _strategy
        ) internal returns (bool status) {
            return
                ratios[_strategy] == 0 && Strategy(_strategy).getAllValue() < 1e4;
        }
        function setNewVault(address _vault) external onlyVault {
            stoneVault = _vault;
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {StrategyController} from "../strategies/StrategyController.sol";
    abstract contract Strategy {
        address payable public immutable controller;
        address public governance;
        string public name;
        modifier onlyGovernance() {
            require(governance == msg.sender, "not governace");
            _;
        }
        event TransferGovernance(address oldOwner, address newOwner);
        constructor(address payable _controller, string memory _name) {
            require(_controller != address(0), "ZERO ADDRESS");
            governance = msg.sender;
            controller = _controller;
            name = _name;
        }
        modifier onlyController() {
            require(controller == msg.sender, "not controller");
            _;
        }
        function deposit() public payable virtual onlyController {}
        function withdraw(
            uint256 _amount
        ) public virtual onlyController returns (uint256 actualAmount) {}
        function instantWithdraw(
            uint256 _amount
        ) public virtual onlyController returns (uint256 actualAmount) {}
        function clear() public virtual onlyController returns (uint256 amount) {}
        function execPendingRequest(
            uint256 _amount
        ) public virtual returns (uint256 amount) {}
        function getAllValue() public virtual returns (uint256 value) {}
        function getPendingValue() public virtual returns (uint256 value) {}
        function getInvestedValue() public virtual returns (uint256 value) {}
        function checkPendingStatus()
            public
            virtual
            returns (uint256 pending, uint256 executable)
        {}
        function setGovernance(address governance_) external onlyGovernance {
            emit TransferGovernance(governance, governance_);
            governance = governance_;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    library VaultMath {
        uint256 internal constant DECIMALS = 18;
        function assetToShares(
            uint256 _assetAmount,
            uint256 _assetPerShare
        ) internal pure returns (uint256) {
            require(_assetPerShare > 1, "Vault Lib: invalid assetPerShare");
            return (_assetAmount * (10 ** DECIMALS)) / _assetPerShare;
        }
        function sharesToAsset(
            uint256 _shares,
            uint256 _assetPerShare
        ) internal pure returns (uint256) {
            require(_assetPerShare > 1, "Vault Lib: invalid assetPerShare");
            return (_shares * _assetPerShare) / (10 ** DECIMALS);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
    contract AssetsVault {
        address public stoneVault;
        address public strategyController;
        modifier onlyPermit() {
            require(
                stoneVault == msg.sender || strategyController == msg.sender,
                "not permit"
            );
            _;
        }
        constructor(address _stoneVault, address _strategyController) {
            require(
                _stoneVault != address(0) && _strategyController != address(0),
                "ZERO ADDRESS"
            );
            stoneVault = _stoneVault;
            strategyController = _strategyController;
        }
        function deposit() external payable {
            require(msg.value != 0, "too small");
        }
        function withdraw(address _to, uint256 _amount) external onlyPermit {
            TransferHelper.safeTransferETH(_to, _amount);
        }
        function setNewVault(address _vault) external onlyPermit {
            stoneVault = _vault;
        }
        function getBalance() external view returns (uint256 amount) {
            amount = address(this).balance;
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity >=0.6.0;
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    library TransferHelper {
        /// @notice Transfers tokens from the targeted address to the given destination
        /// @notice Errors with 'STF' if transfer fails
        /// @param token The contract address of the token to be transferred
        /// @param from The originating address from which the tokens will be transferred
        /// @param to The destination address of the transfer
        /// @param value The amount to be transferred
        function safeTransferFrom(
            address token,
            address from,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) =
                token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
        }
        /// @notice Transfers tokens from msg.sender to a recipient
        /// @dev Errors with ST if transfer fails
        /// @param token The contract address of the token which will be transferred
        /// @param to The recipient of the transfer
        /// @param value The value of the transfer
        function safeTransfer(
            address token,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
        }
        /// @notice Approves the stipulated contract to spend the given allowance in the given token
        /// @dev Errors with 'SA' if transfer fails
        /// @param token The contract address of the token to be approved
        /// @param to The target of the approval
        /// @param value The amount of the given token the target will be allowed to spend
        function safeApprove(
            address token,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
        }
        /// @notice Transfers ETH to the recipient address
        /// @dev Fails with `STE`
        /// @param to The destination of the transfer
        /// @param value The value to be transferred
        function safeTransferETH(address to, uint256 value) internal {
            (bool success, ) = to.call{value: value}(new bytes(0));
            require(success, 'STE');
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position of the value in the `values` array, plus 1 because index 0
            // means a value is not in the set.
            mapping(bytes32 => uint256) _indexes;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._indexes[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We read and store the value's index to prevent multiple reads from the same storage slot
            uint256 valueIndex = set._indexes[value];
            if (valueIndex != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 toDeleteIndex = valueIndex - 1;
                uint256 lastIndex = set._values.length - 1;
                if (lastIndex != toDeleteIndex) {
                    bytes32 lastValue = set._values[lastIndex];
                    // Move the last value to the index where the value to delete is
                    set._values[toDeleteIndex] = lastValue;
                    // Update the index for the moved value
                    set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                }
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the index for the deleted slot
                delete set._indexes[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._indexes[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 amount) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.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}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * 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.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            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 override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address to, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(from, to, amount);
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, allowance(owner, spender) + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = allowance(owner, spender);
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(address from, address to, uint256 amount) internal virtual {
            require(from != address(0), "ERC20: transfer from the zero address");
            require(to != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(from, to, amount);
            uint256 fromBalance = _balances[from];
            require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[from] = fromBalance - amount;
                // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                // decrementing then incrementing.
                _balances[to] += amount;
            }
            emit Transfer(from, to, amount);
            _afterTokenTransfer(from, to, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            unchecked {
                // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                _balances[account] += amount;
            }
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
                // Overflow not possible: amount <= accountBalance <= totalSupply.
                _totalSupply -= amount;
            }
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= amount, "ERC20: insufficient allowance");
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 ReentrancyGuard {
        // 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;
        uint256 private _status;
        constructor() {
            _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 {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
        }
        function _nonReentrantAfter() private {
            // 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) {
            return _status == _ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT OR Apache-2.0
    pragma solidity >=0.7.6;
    library ExcessivelySafeCall {
        uint256 constant LOW_28_MASK =
        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
        /// @notice Use when you _really_ really _really_ don't trust the called
        /// contract. This prevents the called contract from causing reversion of
        /// the caller in as many ways as we can.
        /// @dev The main difference between this and a solidity low-level call is
        /// that we limit the number of bytes that the callee can cause to be
        /// copied to caller memory. This prevents stupid things like malicious
        /// contracts returning 10,000,000 bytes causing a local OOG when copying
        /// to memory.
        /// @param _target The address to call
        /// @param _gas The amount of gas to forward to the remote contract
        /// @param _maxCopy The maximum number of bytes of returndata to copy
        /// to memory.
        /// @param _calldata The data to send to the remote contract
        /// @return success and returndata, as `.call()`. Returndata is capped to
        /// `_maxCopy` bytes.
        function excessivelySafeCall(
            address _target,
            uint256 _gas,
            uint16 _maxCopy,
            bytes memory _calldata
        ) internal returns (bool, bytes memory) {
            // set up for assembly call
            uint256 _toCopy;
            bool _success;
            bytes memory _returnData = new bytes(_maxCopy);
            // dispatch message to recipient
            // by assembly calling "handle" function
            // we call via assembly to avoid memcopying a very large returndata
            // returned by a malicious contract
            assembly {
                _success := call(
                _gas, // gas
                _target, // recipient
                0, // ether value
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
                )
            // limit our copy to 256 bytes
                _toCopy := returndatasize()
                if gt(_toCopy, _maxCopy) {
                    _toCopy := _maxCopy
                }
            // Store the length of the copied bytes
                mstore(_returnData, _toCopy)
            // copy the bytes from returndata[0:_toCopy]
                returndatacopy(add(_returnData, 0x20), 0, _toCopy)
            }
            return (_success, _returnData);
        }
        /// @notice Use when you _really_ really _really_ don't trust the called
        /// contract. This prevents the called contract from causing reversion of
        /// the caller in as many ways as we can.
        /// @dev The main difference between this and a solidity low-level call is
        /// that we limit the number of bytes that the callee can cause to be
        /// copied to caller memory. This prevents stupid things like malicious
        /// contracts returning 10,000,000 bytes causing a local OOG when copying
        /// to memory.
        /// @param _target The address to call
        /// @param _gas The amount of gas to forward to the remote contract
        /// @param _maxCopy The maximum number of bytes of returndata to copy
        /// to memory.
        /// @param _calldata The data to send to the remote contract
        /// @return success and returndata, as `.call()`. Returndata is capped to
        /// `_maxCopy` bytes.
        function excessivelySafeStaticCall(
            address _target,
            uint256 _gas,
            uint16 _maxCopy,
            bytes memory _calldata
        ) internal view returns (bool, bytes memory) {
            // set up for assembly call
            uint256 _toCopy;
            bool _success;
            bytes memory _returnData = new bytes(_maxCopy);
            // dispatch message to recipient
            // by assembly calling "handle" function
            // we call via assembly to avoid memcopying a very large returndata
            // returned by a malicious contract
            assembly {
                _success := staticcall(
                _gas, // gas
                _target, // recipient
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
                )
            // limit our copy to 256 bytes
                _toCopy := returndatasize()
                if gt(_toCopy, _maxCopy) {
                    _toCopy := _maxCopy
                }
            // Store the length of the copied bytes
                mstore(_returnData, _toCopy)
            // copy the bytes from returndata[0:_toCopy]
                returndatacopy(add(_returnData, 0x20), 0, _toCopy)
            }
            return (_success, _returnData);
        }
        /**
         * @notice Swaps function selectors in encoded contract calls
         * @dev Allows reuse of encoded calldata for functions with identical
         * argument types but different names. It simply swaps out the first 4 bytes
         * for the new selector. This function modifies memory in place, and should
         * only be used with caution.
         * @param _newSelector The new 4-byte selector
         * @param _buf The encoded contract args
         */
        function swapSelector(bytes4 _newSelector, bytes memory _buf)
        internal
        pure
        {
            require(_buf.length >= 4);
            uint256 _mask = LOW_28_MASK;
            assembly {
            // load the first word of
                let _word := mload(add(_buf, 0x20))
            // mask out the top 4 bytes
            // /x
                _word := and(_word, _mask)
                _word := or(_newSelector, _word)
                mstore(add(_buf, 0x20), _word)
            }
        }
    }
    // SPDX-License-Identifier: Unlicense
    /*
     * @title Solidity Bytes Arrays Utils
     * @author Gonçalo Sá <[email protected]>
     *
     * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
     *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
     */
    pragma solidity >=0.8.0 <0.9.0;
    library BytesLib {
        function concat(
            bytes memory _preBytes,
            bytes memory _postBytes
        )
        internal
        pure
        returns (bytes memory)
        {
            bytes memory tempBytes;
            assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
                tempBytes := mload(0x40)
            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
                let length := mload(_preBytes)
                mstore(tempBytes, length)
            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
                let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
                let end := add(mc, length)
                for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                    let cc := add(_preBytes, 0x20)
                } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                    mstore(mc, mload(cc))
                }
            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
                length := mload(_postBytes)
                mstore(tempBytes, add(length, mload(tempBytes)))
            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
                mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
                end := add(mc, length)
                for {
                    let cc := add(_postBytes, 0x20)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }
            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
                mstore(0x40, and(
                add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                not(31) // Round down to the nearest 32 bytes.
                ))
            }
            return tempBytes;
        }
        function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
            assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
                let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
                let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                switch add(lt(slength, 32), lt(newlength, 32))
                case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                    sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                    // we can just add to the slot contents because the
                    // bytes we want to change are the LSBs
                    fslot,
                    add(
                    mul(
                    div(
                    // load the bytes from memory
                    mload(add(_postBytes, 0x20)),
                    // zero all bytes to the right
                    exp(0x100, sub(32, mlength))
                    ),
                    // and now shift left the number of bytes to
                    // leave space for the length in the slot
                    exp(0x100, sub(32, newlength))
                    ),
                    // increase length by the double of the memory
                    // bytes length
                    mul(mlength, 2)
                    )
                    )
                    )
                }
                case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes.slot)
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                // save new length
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.
                    let submod := sub(32, slength)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
                    sstore(
                    sc,
                    add(
                    and(
                    fslot,
                    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                    ),
                    and(mload(mc), mask)
                    )
                    )
                    for {
                        mc := add(mc, 0x20)
                        sc := add(sc, 1)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
                    mask := exp(0x100, sub(mc, end))
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
                default {
                // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                // save new length
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                    let slengthmod := mod(slength, 32)
                    let mlengthmod := mod(mlength, 32)
                    let submod := sub(32, slengthmod)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
                    sstore(sc, add(sload(sc), and(mload(mc), mask)))
                    for {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
                    mask := exp(0x100, sub(mc, end))
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
            }
        }
        function slice(
            bytes memory _bytes,
            uint256 _start,
            uint256 _length
        )
        internal
        pure
        returns (bytes memory)
        {
            require(_length + 31 >= _length, "slice_overflow");
            require(_bytes.length >= _start + _length, "slice_outOfBounds");
            bytes memory tempBytes;
            assembly {
                switch iszero(_length)
                case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                    tempBytes := mload(0x40)
                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                    let lengthmod := and(_length, 31)
                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                    let end := add(mc, _length)
                    for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                    } lt(mc, end) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        mstore(mc, mload(cc))
                    }
                    mstore(tempBytes, _length)
                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                    mstore(0x40, and(add(mc, 31), not(31)))
                }
                //if we want a zero-length slice let's just return a zero-length array
                default {
                    tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                    mstore(tempBytes, 0)
                    mstore(0x40, add(tempBytes, 0x20))
                }
            }
            return tempBytes;
        }
        function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
            require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
            address tempAddress;
            assembly {
                tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
            }
            return tempAddress;
        }
        function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
            require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
            uint8 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x1), _start))
            }
            return tempUint;
        }
        function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
            require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
            uint16 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x2), _start))
            }
            return tempUint;
        }
        function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
            require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
            uint32 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x4), _start))
            }
            return tempUint;
        }
        function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
            require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
            uint64 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x8), _start))
            }
            return tempUint;
        }
        function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
            require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
            uint96 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0xc), _start))
            }
            return tempUint;
        }
        function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
            require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
            uint128 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x10), _start))
            }
            return tempUint;
        }
        function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
            require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
            uint256 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x20), _start))
            }
            return tempUint;
        }
        function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
            require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
            bytes32 tempBytes32;
            assembly {
                tempBytes32 := mload(add(add(_bytes, 0x20), _start))
            }
            return tempBytes32;
        }
        function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
            bool success = true;
            assembly {
                let length := mload(_preBytes)
            // if lengths don't match the arrays are not equal
                switch eq(length, mload(_postBytes))
                case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                    let cb := 1
                    let mc := add(_preBytes, 0x20)
                    let end := add(mc, length)
                    for {
                        let cc := add(_postBytes, 0x20)
                    // the next line is the loop condition:
                    // while(uint256(mc < end) + cb == 2)
                    } eq(add(lt(mc, end), cb), 2) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                    // if any of these checks fails then arrays are not equal
                        if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                            success := 0
                            cb := 0
                        }
                    }
                }
                default {
                // unsuccess:
                    success := 0
                }
            }
            return success;
        }
        function equalStorage(
            bytes storage _preBytes,
            bytes memory _postBytes
        )
        internal
        view
        returns (bool)
        {
            bool success = true;
            assembly {
            // we know _preBytes_offset is 0
                let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
            // if lengths don't match the arrays are not equal
                switch eq(slength, mlength)
                case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                    if iszero(iszero(slength)) {
                        switch lt(slength, 32)
                        case 1 {
                        // blank the last byte which is the length
                            fslot := mul(div(fslot, 0x100), 0x100)
                            if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                                success := 0
                            }
                        }
                        default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                            let cb := 1
                        // get the keccak hash to get the contents of the array
                            mstore(0x0, _preBytes.slot)
                            let sc := keccak256(0x0, 0x20)
                            let mc := add(_postBytes, 0x20)
                            let end := add(mc, mlength)
                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                            for {} eq(add(lt(mc, end), cb), 2) {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } {
                                if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                    success := 0
                                    cb := 0
                                }
                            }
                        }
                    }
                }
                default {
                // unsuccess:
                    success := 0
                }
            }
            return success;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../OFT.sol";
    contract BasedOFT is OFT {
        constructor(string memory _name, string memory _symbol, address _lzEndpoint) OFT(_name, _symbol, _lzEndpoint) {}
        function circulatingSupply() public view virtual override returns (uint) {
            unchecked {
                return totalSupply() - balanceOf(address(this));
            }
        }
        function _debitFrom(address _from, uint16, bytes memory, uint _amount) internal virtual override returns(uint) {
            address spender = _msgSender();
            if (_from != spender) _spendAllowance(_from, spender, _amount);
            _transfer(_from, address(this), _amount);
            return _amount;
        }
        function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns(uint) {
            _transfer(address(this), _toAddress, _amount);
            return _amount;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../../lzApp/NonblockingLzApp.sol";
    import "./IOFTCore.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    abstract contract OFTCore is NonblockingLzApp, ERC165, IOFTCore {
        using BytesLib for bytes;
        uint public constant NO_EXTRA_GAS = 0;
        // packet type
        uint16 public constant PT_SEND = 0;
        bool public useCustomAdapterParams;
        constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {}
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return interfaceId == type(IOFTCore).interfaceId || super.supportsInterface(interfaceId);
        }
        function estimateSendFee(uint16 _dstChainId, bytes calldata _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) public view virtual override returns (uint nativeFee, uint zroFee) {
            // mock the payload for sendFrom()
            bytes memory payload = abi.encode(PT_SEND, _toAddress, _amount);
            return lzEndpoint.estimateFees(_dstChainId, address(this), payload, _useZro, _adapterParams);
        }
        function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) public payable virtual override {
            _send(_from, _dstChainId, _toAddress, _amount, _refundAddress, _zroPaymentAddress, _adapterParams);
        }
        function setUseCustomAdapterParams(bool _useCustomAdapterParams) public virtual onlyOwner {
            useCustomAdapterParams = _useCustomAdapterParams;
            emit SetUseCustomAdapterParams(_useCustomAdapterParams);
        }
        function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
            uint16 packetType;
            assembly {
                packetType := mload(add(_payload, 32))
            }
            if (packetType == PT_SEND) {
                _sendAck(_srcChainId, _srcAddress, _nonce, _payload);
            } else {
                revert("OFTCore: unknown packet type");
            }
        }
        function _send(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams) internal virtual {
            _checkAdapterParams(_dstChainId, PT_SEND, _adapterParams, NO_EXTRA_GAS);
            uint amount = _debitFrom(_from, _dstChainId, _toAddress, _amount);
            bytes memory lzPayload = abi.encode(PT_SEND, _toAddress, amount);
            _lzSend(_dstChainId, lzPayload, _refundAddress, _zroPaymentAddress, _adapterParams, msg.value);
            emit SendToChain(_dstChainId, _from, _toAddress, amount);
        }
        function _sendAck(uint16 _srcChainId, bytes memory, uint64, bytes memory _payload) internal virtual {
            (, bytes memory toAddressBytes, uint amount) = abi.decode(_payload, (uint16, bytes, uint));
            address to = toAddressBytes.toAddress(0);
            amount = _creditTo(_srcChainId, to, amount);
            emit ReceiveFromChain(_srcChainId, to, amount);
        }
        function _checkAdapterParams(uint16 _dstChainId, uint16 _pkType, bytes memory _adapterParams, uint _extraGas) internal virtual {
            if (useCustomAdapterParams) {
                _checkGasLimit(_dstChainId, _pkType, _adapterParams, _extraGas);
            } else {
                require(_adapterParams.length == 0, "OFTCore: _adapterParams must be empty.");
            }
        }
        function _debitFrom(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _amount) internal virtual returns(uint);
        function _creditTo(uint16 _srcChainId, address _toAddress, uint _amount) internal virtual returns(uint);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    import "./IOFT.sol";
    import "./OFTCore.sol";
    // override decimal() function is needed
    contract OFT is OFTCore, ERC20, IOFT {
        constructor(string memory _name, string memory _symbol, address _lzEndpoint) ERC20(_name, _symbol) OFTCore(_lzEndpoint) {}
        function supportsInterface(bytes4 interfaceId) public view virtual override(OFTCore, IERC165) returns (bool) {
            return interfaceId == type(IOFT).interfaceId || interfaceId == type(IERC20).interfaceId || super.supportsInterface(interfaceId);
        }
        function token() public view virtual override returns (address) {
            return address(this);
        }
        function circulatingSupply() public view virtual override returns (uint) {
            return totalSupply();
        }
        function _debitFrom(address _from, uint16, bytes memory, uint _amount) internal virtual override returns(uint) {
            address spender = _msgSender();
            if (_from != spender) _spendAllowance(_from, spender, _amount);
            _burn(_from, _amount);
            return _amount;
        }
        function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns(uint) {
            _mint(_toAddress, _amount);
            return _amount;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /**
     * @dev Interface of the IOFT core standard
     */
    interface IOFTCore is IERC165 {
        /**
         * @dev estimate send token `_tokenId` to (`_dstChainId`, `_toAddress`)
         * _dstChainId - L0 defined chain id to send tokens too
         * _toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain
         * _amount - amount of the tokens to transfer
         * _useZro - indicates to use zro to pay L0 fees
         * _adapterParam - flexible bytes array to indicate messaging adapter services in L0
         */
        function estimateSendFee(uint16 _dstChainId, bytes calldata _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee);
        /**
         * @dev send `_amount` amount of token to (`_dstChainId`, `_toAddress`) from `_from`
         * `_from` the owner of token
         * `_dstChainId` the destination chain identifier
         * `_toAddress` can be any size depending on the `dstChainId`.
         * `_amount` the quantity of tokens in wei
         * `_refundAddress` the address LayerZero refunds if too much message fee is sent
         * `_zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token)
         * `_adapterParams` is a flexible bytes array to indicate messaging adapter services
         */
        function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
        /**
         * @dev returns the circulating amount of tokens on current chain
         */
        function circulatingSupply() external view returns (uint);
        /**
         * @dev returns the address of the ERC20 token
         */
        function token() external view returns (address);
        /**
         * @dev Emitted when `_amount` tokens are moved from the `_sender` to (`_dstChainId`, `_toAddress`)
         * `_nonce` is the outbound nonce
         */
        event SendToChain(uint16 indexed _dstChainId, address indexed _from, bytes _toAddress, uint _amount);
        /**
         * @dev Emitted when `_amount` tokens are received from `_srcChainId` into the `_toAddress` on the local chain.
         * `_nonce` is the inbound nonce.
         */
        event ReceiveFromChain(uint16 indexed _srcChainId, address indexed _to, uint _amount);
        event SetUseCustomAdapterParams(bool _useCustomAdapterParams);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    import "./IOFTCore.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    /**
     * @dev Interface of the OFT standard
     */
    interface IOFT is IOFTCore, IERC20 {
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./LzApp.sol";
    import "../util/ExcessivelySafeCall.sol";
    /*
     * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel
     * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking
     * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress)
     */
    abstract contract NonblockingLzApp is LzApp {
        using ExcessivelySafeCall for address;
        constructor(address _endpoint) LzApp(_endpoint) {}
        mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages;
        event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);
        event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash);
        // overriding the virtual function in LzReceiver
        function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
            (bool success, bytes memory reason) = address(this).excessivelySafeCall(gasleft(), 150, abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload));
            // try-catch all errors/exceptions
            if (!success) {
                _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason);
            }
        }
        function _storeFailedMessage(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload, bytes memory _reason) internal virtual {
            failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload);
            emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason);
        }
        function nonblockingLzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual {
            // only internal transaction
            require(_msgSender() == address(this), "NonblockingLzApp: caller must be LzApp");
            _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
        }
        //@notice override this function
        function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
        function retryMessage(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public payable virtual {
            // assert there is message to retry
            bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce];
            require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message");
            require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload");
            // clear the stored message
            failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0);
            // execute the message. revert if it fails again
            _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
            emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "../interfaces/ILayerZeroReceiver.sol";
    import "../interfaces/ILayerZeroUserApplicationConfig.sol";
    import "../interfaces/ILayerZeroEndpoint.sol";
    import "../util/BytesLib.sol";
    /*
     * a generic LzReceiver implementation
     */
    abstract contract LzApp is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
        using BytesLib for bytes;
        // ua can not send payload larger than this by default, but it can be changed by the ua owner
        uint constant public DEFAULT_PAYLOAD_SIZE_LIMIT = 10000;
        ILayerZeroEndpoint public immutable lzEndpoint;
        mapping(uint16 => bytes) public trustedRemoteLookup;
        mapping(uint16 => mapping(uint16 => uint)) public minDstGasLookup;
        mapping(uint16 => uint) public payloadSizeLimitLookup;
        address public precrime;
        event SetPrecrime(address precrime);
        event SetTrustedRemote(uint16 _remoteChainId, bytes _path);
        event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress);
        event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint _minDstGas);
        constructor(address _endpoint) {
            lzEndpoint = ILayerZeroEndpoint(_endpoint);
        }
        function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual override {
            // lzReceive must be called by the endpoint for security
            require(_msgSender() == address(lzEndpoint), "LzApp: invalid endpoint caller");
            bytes memory trustedRemote = trustedRemoteLookup[_srcChainId];
            // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote.
            require(_srcAddress.length == trustedRemote.length && trustedRemote.length > 0 && keccak256(_srcAddress) == keccak256(trustedRemote), "LzApp: invalid source sending contract");
            _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
        }
        // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging
        function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
        function _lzSend(uint16 _dstChainId, bytes memory _payload, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams, uint _nativeFee) internal virtual {
            bytes memory trustedRemote = trustedRemoteLookup[_dstChainId];
            require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
            _checkPayloadSize(_dstChainId, _payload.length);
            lzEndpoint.send{value: _nativeFee}(_dstChainId, trustedRemote, _payload, _refundAddress, _zroPaymentAddress, _adapterParams);
        }
        function _checkGasLimit(uint16 _dstChainId, uint16 _type, bytes memory _adapterParams, uint _extraGas) internal view virtual {
            uint providedGasLimit = _getGasLimit(_adapterParams);
            uint minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas;
            require(minGasLimit > 0, "LzApp: minGasLimit not set");
            require(providedGasLimit >= minGasLimit, "LzApp: gas limit is too low");
        }
        function _getGasLimit(bytes memory _adapterParams) internal pure virtual returns (uint gasLimit) {
            require(_adapterParams.length >= 34, "LzApp: invalid adapterParams");
            assembly {
                gasLimit := mload(add(_adapterParams, 34))
            }
        }
        function _checkPayloadSize(uint16 _dstChainId, uint _payloadSize) internal view virtual {
            uint payloadSizeLimit = payloadSizeLimitLookup[_dstChainId];
            if (payloadSizeLimit == 0) { // use default if not set
                payloadSizeLimit = DEFAULT_PAYLOAD_SIZE_LIMIT;
            }
            require(_payloadSize <= payloadSizeLimit, "LzApp: payload size is too large");
        }
        //---------------------------UserApplication config----------------------------------------
        function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) {
            return lzEndpoint.getConfig(_version, _chainId, address(this), _configType);
        }
        // generic config for LayerZero user Application
        function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external override onlyOwner {
            lzEndpoint.setConfig(_version, _chainId, _configType, _config);
        }
        function setSendVersion(uint16 _version) external override onlyOwner {
            lzEndpoint.setSendVersion(_version);
        }
        function setReceiveVersion(uint16 _version) external override onlyOwner {
            lzEndpoint.setReceiveVersion(_version);
        }
        function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner {
            lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress);
        }
        // _path = abi.encodePacked(remoteAddress, localAddress)
        // this function set the trusted path for the cross-chain communication
        function setTrustedRemote(uint16 _remoteChainId, bytes calldata _path) external onlyOwner {
            trustedRemoteLookup[_remoteChainId] = _path;
            emit SetTrustedRemote(_remoteChainId, _path);
        }
        function setTrustedRemoteAddress(uint16 _remoteChainId, bytes calldata _remoteAddress) external onlyOwner {
            trustedRemoteLookup[_remoteChainId] = abi.encodePacked(_remoteAddress, address(this));
            emit SetTrustedRemoteAddress(_remoteChainId, _remoteAddress);
        }
        function getTrustedRemoteAddress(uint16 _remoteChainId) external view returns (bytes memory) {
            bytes memory path = trustedRemoteLookup[_remoteChainId];
            require(path.length != 0, "LzApp: no trusted path record");
            return path.slice(0, path.length - 20); // the last 20 bytes should be address(this)
        }
        function setPrecrime(address _precrime) external onlyOwner {
            precrime = _precrime;
            emit SetPrecrime(_precrime);
        }
        function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint _minGas) external onlyOwner {
            require(_minGas > 0, "LzApp: invalid minGas");
            minDstGasLookup[_dstChainId][_packetType] = _minGas;
            emit SetMinDstGas(_dstChainId, _packetType, _minGas);
        }
        // if the size is 0, it means default size limit
        function setPayloadSizeLimit(uint16 _dstChainId, uint _size) external onlyOwner {
            payloadSizeLimitLookup[_dstChainId] = _size;
        }
        //--------------------------- VIEW FUNCTION ----------------------------------------
        function isTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool) {
            bytes memory trustedSource = trustedRemoteLookup[_srcChainId];
            return keccak256(trustedSource) == keccak256(_srcAddress);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    interface ILayerZeroUserApplicationConfig {
        // @notice set the configuration of the LayerZero messaging library of the specified version
        // @param _version - messaging library version
        // @param _chainId - the chainId for the pending config change
        // @param _configType - type of configuration. every messaging library has its own convention.
        // @param _config - configuration in the bytes. can encode arbitrary content.
        function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external;
        // @notice set the send() LayerZero messaging library version to _version
        // @param _version - new messaging library version
        function setSendVersion(uint16 _version) external;
        // @notice set the lzReceive() LayerZero messaging library version to _version
        // @param _version - new messaging library version
        function setReceiveVersion(uint16 _version) external;
        // @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload
        // @param _srcChainId - the chainId of the source chain
        // @param _srcAddress - the contract address of the source contract at the source chain
        function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    interface ILayerZeroReceiver {
        // @notice LayerZero endpoint will invoke this function to deliver the message on the destination
        // @param _srcChainId - the source endpoint identifier
        // @param _srcAddress - the source sending contract address from the source chain
        // @param _nonce - the ordered message nonce
        // @param _payload - the signed payload is the UA bytes has encoded to be sent
        function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    import "./ILayerZeroUserApplicationConfig.sol";
    interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig {
        // @notice send a LayerZero message to the specified address at a LayerZero endpoint.
        // @param _dstChainId - the destination chain identifier
        // @param _destination - the address on destination chain (in bytes). address length/format may vary by chains
        // @param _payload - a custom bytes payload to send to the destination contract
        // @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address
        // @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction
        // @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination
        function send(uint16 _dstChainId, bytes calldata _destination, bytes calldata _payload, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
        // @notice used by the messaging library to publish verified payload
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source contract (as bytes) at the source chain
        // @param _dstAddress - the address on destination chain
        // @param _nonce - the unbound message ordering nonce
        // @param _gasLimit - the gas limit for external contract execution
        // @param _payload - verified payload to send to the destination contract
        function receivePayload(uint16 _srcChainId, bytes calldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytes calldata _payload) external;
        // @notice get the inboundNonce of a lzApp from a source chain which could be EVM or non-EVM chain
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source chain contract address
        function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (uint64);
        // @notice get the outboundNonce from this source chain which, consequently, is always an EVM
        // @param _srcAddress - the source chain contract address
        function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view returns (uint64);
        // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery
        // @param _dstChainId - the destination chain identifier
        // @param _userApplication - the user app address on this EVM chain
        // @param _payload - the custom message to send over LayerZero
        // @param _payInZRO - if false, user app pays the protocol fee in native token
        // @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain
        function estimateFees(uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParam) external view returns (uint nativeFee, uint zroFee);
        // @notice get this Endpoint's immutable source identifier
        function getChainId() external view returns (uint16);
        // @notice the interface to retry failed message on this Endpoint destination
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source chain contract address
        // @param _payload - the payload to be retried
        function retryPayload(uint16 _srcChainId, bytes calldata _srcAddress, bytes calldata _payload) external;
        // @notice query if any STORED payload (message blocking) at the endpoint.
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source chain contract address
        function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool);
        // @notice query if the _libraryAddress is valid for sending msgs.
        // @param _userApplication - the user app address on this EVM chain
        function getSendLibraryAddress(address _userApplication) external view returns (address);
        // @notice query if the _libraryAddress is valid for receiving msgs.
        // @param _userApplication - the user app address on this EVM chain
        function getReceiveLibraryAddress(address _userApplication) external view returns (address);
        // @notice query if the non-reentrancy guard for send() is on
        // @return true if the guard is on. false otherwise
        function isSendingPayload() external view returns (bool);
        // @notice query if the non-reentrancy guard for receive() is on
        // @return true if the guard is on. false otherwise
        function isReceivingPayload() external view returns (bool);
        // @notice get the configuration of the LayerZero messaging library of the specified version
        // @param _version - messaging library version
        // @param _chainId - the chainId for the pending config change
        // @param _userApplication - the contract address of the user application
        // @param _configType - type of configuration. every messaging library has its own convention.
        function getConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) external view returns (bytes memory);
        // @notice get the send() LayerZero messaging library version
        // @param _userApplication - the contract address of the user application
        function getSendVersion(address _userApplication) external view returns (uint16);
        // @notice get the lzReceive() LayerZero messaging library version
        // @param _userApplication - the contract address of the user application
        function getReceiveVersion(address _userApplication) external view returns (uint16);
    }
    

    File 2 of 2: Stone
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import "@layerzerolabs/solidity-examples/contracts/token/oft/extension/BasedOFT.sol";
    import {Minter} from "./Minter.sol";
    contract Stone is BasedOFT {
        uint256 public constant DAY_INTERVAL = 24 * 60 * 60;
        address public minter;
        uint16 public constant PT_FEED = 1;
        uint16 public constant PT_SET_ENABLE = 2;
        uint16 public constant PT_SET_CAP = 3;
        uint256 public cap;
        bool public enable = true;
        mapping(uint256 => uint256) public quota;
        event FeedToChain(
            uint16 indexed dstChainId,
            address indexed from,
            bytes toAddress,
            uint price
        );
        event SetCapFor(uint16 indexed dstChainId, bytes toAddress, uint cap);
        event SetEnableFor(uint16 indexed dstChainId, bytes toAddress, bool flag);
        constructor(
            address _minter,
            address _layerZeroEndpoint,
            uint256 _cap
        ) BasedOFT("StakeStone Ether", "STONE", _layerZeroEndpoint) {
            minter = _minter;
            cap = _cap;
        }
        modifier onlyMinter() {
            require(msg.sender == minter, "NM");
            _;
        }
        function mint(address _to, uint256 _amount) external onlyMinter {
            _mint(_to, _amount);
        }
        function burn(address _from, uint256 _amount) external onlyMinter {
            _burn(_from, _amount);
        }
        function sendFrom(
            address _from,
            uint16 _dstChainId,
            bytes calldata _toAddress,
            uint _amount,
            address payable _refundAddress,
            address _zroPaymentAddress,
            bytes calldata _adapterParams
        ) public payable override(IOFTCore, OFTCore) {
            require(enable, "invalid");
            uint256 id;
            assembly {
                id := chainid()
            }
            require(id != _dstChainId, "same chain");
            uint256 day = block.timestamp / DAY_INTERVAL;
            require(_amount + quota[day] <= cap, "Exceed cap");
            quota[day] = quota[day] + _amount;
            super.sendFrom(
                _from,
                _dstChainId,
                _toAddress,
                _amount,
                _refundAddress,
                _zroPaymentAddress,
                _adapterParams
            );
        }
        function updatePrice(
            uint16 _dstChainId,
            bytes memory _toAddress
        ) external payable returns (uint256 price) {
            require(enable, "invalid");
            uint256 id;
            assembly {
                id := chainid()
            }
            require(id != _dstChainId, "same chain");
            price = tokenPrice();
            bytes memory lzPayload = abi.encode(
                PT_FEED,
                _toAddress,
                price,
                block.timestamp
            );
            _lzSend(
                _dstChainId,
                lzPayload,
                payable(msg.sender),
                address(0),
                bytes(""),
                msg.value
            );
            emit FeedToChain(_dstChainId, msg.sender, _toAddress, price);
        }
        function setEnableFor(
            uint16 _dstChainId,
            bool _flag,
            bytes memory _toAddress
        ) external payable onlyOwner {
            uint256 id;
            assembly {
                id := chainid()
            }
            if (_dstChainId == id) {
                enable = _flag;
                emit SetEnableFor(
                    _dstChainId,
                    abi.encodePacked(address(this)),
                    enable
                );
                return;
            }
            bytes memory lzPayload = abi.encode(PT_SET_ENABLE, _toAddress, _flag);
            _lzSend(
                _dstChainId,
                lzPayload,
                payable(msg.sender),
                address(0),
                bytes(""),
                msg.value
            );
            emit SetEnableFor(_dstChainId, _toAddress, _flag);
        }
        function setCapFor(
            uint16 _dstChainId,
            uint256 _cap,
            bytes memory _toAddress
        ) external payable onlyOwner {
            uint256 id;
            assembly {
                id := chainid()
            }
            if (_dstChainId == id) {
                cap = _cap;
                emit SetCapFor(_dstChainId, abi.encodePacked(address(this)), cap);
                return;
            }
            bytes memory lzPayload = abi.encode(PT_SET_CAP, _toAddress, _cap);
            _lzSend(
                _dstChainId,
                lzPayload,
                payable(msg.sender),
                address(0),
                bytes(""),
                msg.value
            );
            emit SetCapFor(_dstChainId, _toAddress, _cap);
        }
        function tokenPrice() public returns (uint256 price) {
            price = Minter(minter).getTokenPrice();
        }
        function getQuota() external view returns (uint256) {
            uint256 amount = quota[block.timestamp / DAY_INTERVAL];
            if (cap > amount && enable) {
                return cap - amount;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {Stone} from "./Stone.sol";
    import {StoneVault} from "../StoneVault.sol";
    contract Minter {
        // TODO: governable upgrade
        address public stone;
        address payable public vault;
        modifier onlyVault() {
            require(msg.sender == vault, "not vault");
            _;
        }
        constructor(address _stone, address payable _vault) {
            stone = _stone;
            vault = _vault;
        }
        function mint(address _to, uint256 _amount) external onlyVault {
            Stone(stone).mint(_to, _amount);
        }
        function burn(address _from, uint256 _amount) external onlyVault {
            Stone(stone).burn(_from, _amount);
        }
        function setNewVault(address _vault) external onlyVault {
            vault = payable(_vault);
        }
        function getTokenPrice() public returns (uint256 price) {
            price = StoneVault(vault).currentSharePrice();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
    import {Strategy} from "./Strategy.sol";
    import {AssetsVault} from "../AssetsVault.sol";
    contract StrategyController {
        using EnumerableSet for EnumerableSet.AddressSet;
        uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
        address public stoneVault;
        address payable public immutable assetsVault;
        EnumerableSet.AddressSet private strategies;
        mapping(address => uint256) public ratios;
        struct StrategyDiff {
            address strategy;
            bool isDeposit;
            uint256 amount;
        }
        modifier onlyVault() {
            require(stoneVault == msg.sender, "not vault");
            _;
        }
        constructor(
            address payable _assetsVault,
            address[] memory _strategies,
            uint256[] memory _ratios
        ) {
            require(_assetsVault != address(0), "ZERO ADDRESS");
            uint256 length = _strategies.length;
            for (uint256 i; i < length; i++) {
                require(_strategies[i] != address(0), "ZERO ADDRESS");
            }
            stoneVault = msg.sender;
            assetsVault = _assetsVault;
            _initStrategies(_strategies, _ratios);
        }
        function onlyRebaseStrategies() external {
            _rebase(0, 0);
        }
        function forceWithdraw(
            uint256 _amount
        ) external onlyVault returns (uint256 actualAmount) {
            uint256 balanceBeforeRepay = address(this).balance;
            if (balanceBeforeRepay >= _amount) {
                _repayToVault();
                actualAmount = balanceBeforeRepay;
            } else {
                actualAmount =
                    _forceWithdraw(_amount - balanceBeforeRepay) +
                    balanceBeforeRepay;
            }
        }
        function setStrategies(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) external onlyVault {
            _setStrategies(_strategies, _ratios);
        }
        function addStrategy(address _strategy) external onlyVault {
            require(!strategies.contains(_strategy), "already exist");
            strategies.add(_strategy);
        }
        function rebaseStrategies(
            uint256 _in,
            uint256 _out
        ) external payable onlyVault {
            _rebase(_in, _out);
        }
        function destroyStrategy(address _strategy) external onlyVault {
            _destoryStrategy(_strategy);
        }
        function _rebase(uint256 _in, uint256 _out) internal {
            require(_in == 0 || _out == 0, "only deposit or withdraw");
            if (_in != 0) {
                AssetsVault(assetsVault).withdraw(address(this), _in);
            }
            uint256 total = getAllStrategyValidValue();
            if (total < _out) {
                total = 0;
            } else {
                total = total + _in - _out;
            }
            uint256 length = strategies.length();
            StrategyDiff[] memory diffs = new StrategyDiff[](length);
            uint256 head;
            uint256 tail = length - 1;
            for (uint i; i < length; i++) {
                address strategy = strategies.at(i);
                if (ratios[strategy] == 0) {
                    _clearStrategy(strategy, true);
                    continue;
                }
                uint256 newPosition = (total * ratios[strategy]) /
                    ONE_HUNDRED_PERCENT;
                uint256 position = getStrategyValidValue(strategy);
                if (newPosition < position) {
                    diffs[head] = StrategyDiff(
                        strategy,
                        false,
                        position - newPosition
                    );
                    head++;
                } else if (newPosition > position) {
                    diffs[tail] = StrategyDiff(
                        strategy,
                        true,
                        newPosition - position
                    );
                    if (tail != 0) {
                        tail--;
                    }
                }
            }
            length = diffs.length;
            for (uint256 i; i < length; i++) {
                StrategyDiff memory diff = diffs[i];
                if (diff.amount == 0) {
                    continue;
                }
                if (diff.isDeposit) {
                    if (address(this).balance < diff.amount) {
                        diff.amount = address(this).balance;
                    }
                    _depositToStrategy(diff.strategy, diff.amount);
                } else {
                    _withdrawFromStrategy(diff.strategy, diff.amount);
                }
            }
            _repayToVault();
        }
        function _repayToVault() internal {
            if (address(this).balance != 0) {
                TransferHelper.safeTransferETH(assetsVault, address(this).balance);
            }
        }
        function _depositToStrategy(address _strategy, uint256 _amount) internal {
            Strategy(_strategy).deposit{value: _amount}();
        }
        function _withdrawFromStrategy(
            address _strategy,
            uint256 _amount
        ) internal {
            Strategy(_strategy).withdraw(_amount);
        }
        function _forceWithdraw(
            uint256 _amount
        ) internal returns (uint256 actualAmount) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                address strategy = strategies.at(i);
                uint256 withAmount = (_amount * ratios[strategy]) /
                    ONE_HUNDRED_PERCENT;
                if (withAmount != 0) {
                    actualAmount =
                        Strategy(strategy).instantWithdraw(withAmount) +
                        actualAmount;
                }
            }
            _repayToVault();
        }
        function getStrategyValue(
            address _strategy
        ) public returns (uint256 _value) {
            return Strategy(_strategy).getAllValue();
        }
        function getStrategyValidValue(
            address _strategy
        ) public returns (uint256 _value) {
            return Strategy(_strategy).getInvestedValue();
        }
        function getStrategyPendingValue(
            address _strategy
        ) public returns (uint256 _value) {
            return Strategy(_strategy).getPendingValue();
        }
        function getAllStrategiesValue() public returns (uint256 _value) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                _value = _value + getStrategyValue(strategies.at(i));
            }
        }
        function getAllStrategyValidValue() public returns (uint256 _value) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                _value = _value + getStrategyValidValue(strategies.at(i));
            }
        }
        function getAllStrategyPendingValue() public returns (uint256 _value) {
            uint256 length = strategies.length();
            for (uint i; i < length; i++) {
                _value = _value + getStrategyPendingValue(strategies.at(i));
            }
        }
        function getStrategies()
            public
            view
            returns (address[] memory addrs, uint256[] memory portions)
        {
            uint256 length = strategies.length();
            addrs = new address[](length);
            portions = new uint256[](length);
            for (uint256 i; i < length; i++) {
                address addr = strategies.at(i);
                addrs[i] = addr;
                portions[i] = ratios[addr];
            }
        }
        function _initStrategies(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) internal {
            require(_strategies.length == _ratios.length, "invalid length");
            uint256 totalRatio;
            uint256 length = _strategies.length;
            for (uint i; i < length; i++) {
                strategies.add(_strategies[i]);
                ratios[_strategies[i]] = _ratios[i];
                totalRatio = totalRatio + _ratios[i];
            }
            require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
        }
        function _setStrategies(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) internal {
            uint256 length = _strategies.length;
            require(length == _ratios.length, "invalid length");
            uint256 oldLength = strategies.length();
            for (uint i; i < oldLength; i++) {
                ratios[strategies.at(i)] = 0;
            }
            uint256 totalRatio;
            for (uint i; i < length; i++) {
                require(
                    Strategy(_strategies[i]).controller() == address(this),
                    "controller mismatch"
                );
                strategies.add(_strategies[i]);
                ratios[_strategies[i]] = _ratios[i];
                totalRatio = totalRatio + _ratios[i];
            }
            require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
        }
        function clearStrategy(address _strategy) public onlyVault {
            _clearStrategy(_strategy, false);
        }
        function _clearStrategy(address _strategy, bool _isRebase) internal {
            Strategy(_strategy).clear();
            if (!_isRebase) {
                _repayToVault();
            }
        }
        function _destoryStrategy(address _strategy) internal {
            require(_couldDestroyStrategy(_strategy), "still active");
            strategies.remove(_strategy);
            _repayToVault();
        }
        function _couldDestroyStrategy(
            address _strategy
        ) internal returns (bool status) {
            return
                ratios[_strategy] == 0 && Strategy(_strategy).getAllValue() < 1e4;
        }
        function setNewVault(address _vault) external onlyVault {
            stoneVault = _vault;
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {StrategyController} from "../strategies/StrategyController.sol";
    abstract contract Strategy {
        address payable public immutable controller;
        address public governance;
        string public name;
        modifier onlyGovernance() {
            require(governance == msg.sender, "not governace");
            _;
        }
        event TransferGovernance(address oldOwner, address newOwner);
        constructor(address payable _controller, string memory _name) {
            require(_controller != address(0), "ZERO ADDRESS");
            governance = msg.sender;
            controller = _controller;
            name = _name;
        }
        modifier onlyController() {
            require(controller == msg.sender, "not controller");
            _;
        }
        function deposit() public payable virtual onlyController {}
        function withdraw(
            uint256 _amount
        ) public virtual onlyController returns (uint256 actualAmount) {}
        function instantWithdraw(
            uint256 _amount
        ) public virtual onlyController returns (uint256 actualAmount) {}
        function clear() public virtual onlyController returns (uint256 amount) {}
        function execPendingRequest(
            uint256 _amount
        ) public virtual returns (uint256 amount) {}
        function getAllValue() public virtual returns (uint256 value) {}
        function getPendingValue() public virtual returns (uint256 value) {}
        function getInvestedValue() public virtual returns (uint256 value) {}
        function checkPendingStatus()
            public
            virtual
            returns (uint256 pending, uint256 executable)
        {}
        function setGovernance(address governance_) external onlyGovernance {
            emit TransferGovernance(governance, governance_);
            governance = governance_;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    library VaultMath {
        uint256 internal constant DECIMALS = 18;
        function assetToShares(
            uint256 _assetAmount,
            uint256 _assetPerShare
        ) internal pure returns (uint256) {
            require(_assetPerShare > 1, "Vault Lib: invalid assetPerShare");
            return (_assetAmount * (10 ** DECIMALS)) / _assetPerShare;
        }
        function sharesToAsset(
            uint256 _shares,
            uint256 _assetPerShare
        ) internal pure returns (uint256) {
            require(_assetPerShare > 1, "Vault Lib: invalid assetPerShare");
            return (_shares * _assetPerShare) / (10 ** DECIMALS);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
    import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
    import {Minter} from "./token/Minter.sol";
    import {Stone} from "./token/Stone.sol";
    import {AssetsVault} from "./AssetsVault.sol";
    import {StrategyController} from "./strategies/StrategyController.sol";
    import {VaultMath} from "./libraries/VaultMath.sol";
    contract StoneVault is ReentrancyGuard, Ownable {
        uint256 internal constant MULTIPLIER = 1e18;
        uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
        uint256 internal constant MAXMIUM_FEE_RATE = ONE_HUNDRED_PERCENT / 100; // 1%
        uint256 internal constant MINIMUM_REBASE_INTERVAL = 7 * 24 * 60 * 60;
        uint256 public constant VERSION = 1;
        uint256 public rebaseTimeInterval = 24 * 60 * 60;
        address public immutable minter;
        address public immutable stone;
        address payable public immutable strategyController;
        address payable public immutable assetsVault;
        address public proposal;
        address public feeRecipient;
        uint256 public latestRoundID;
        uint256 public withdrawableAmountInPast;
        uint256 public withdrawingSharesInPast;
        uint256 public withdrawingSharesInRound;
        uint256 public withdrawFeeRate;
        uint256 public rebaseTime;
        mapping(uint256 => uint256) public roundPricePerShare;
        mapping(uint256 => uint256) public settlementTime;
        mapping(address => UserReceipt) public userReceipts;
        struct UserReceipt {
            uint256 withdrawRound;
            uint256 withdrawShares;
            uint256 withdrawableAmount;
        }
        event Deposit(
            address indexed account,
            uint256 amount,
            uint256 mint,
            uint256 round
        );
        event InitiateWithdraw(
            address indexed account,
            uint256 shares,
            uint256 round
        );
        event CancelWithdraw(
            address indexed account,
            uint256 amount,
            uint256 round
        );
        event Withdrawn(address indexed account, uint256 amount, uint256 round);
        event WithdrawnFromStrategy(
            address indexed account,
            uint256 amount,
            uint256 actualAmount,
            uint256 round
        );
        event RollToNextRound(
            uint256 round,
            uint256 vaultIn,
            uint256 vaultOut,
            uint256 sharePrice
        );
        event StragetyAdded(address strategy);
        event StragetyDestroyed(address strategy);
        event StragetyCleared(address strategy);
        event PortfolioConfigUpdated(address[] strategies, uint256[] ratios);
        event FeeCharged(address indexed account, uint256 amount);
        event SetWithdrawFeeRate(uint256 oldRate, uint256 newRate);
        event SetFeeRecipient(address oldAddr, address newAddr);
        event SetRebaseInterval(uint256 interval);
        modifier onlyProposal() {
            require(proposal == msg.sender, "not proposal");
            _;
        }
        constructor(
            address _minter,
            address _proposal,
            address payable _assetsVault,
            address[] memory _strategies,
            uint256[] memory _ratios
        ) {
            require(
                _minter != address(0) &&
                    _proposal != address(0) &&
                    _assetsVault != address(0),
                "ZERO ADDRESS"
            );
            uint256 length = _strategies.length;
            for (uint256 i; i < length; i++) {
                require(_strategies[i] != address(0), "ZERO ADDRESS");
            }
            minter = _minter;
            proposal = _proposal;
            assetsVault = _assetsVault;
            feeRecipient = msg.sender;
            StrategyController controller = new StrategyController(
                _assetsVault,
                _strategies,
                _ratios
            );
            strategyController = payable(address(controller));
            stone = Minter(_minter).stone();
            roundPricePerShare[0] = MULTIPLIER;
            latestRoundID = 0;
        }
        function deposit()
            external
            payable
            nonReentrant
            returns (uint256 mintAmount)
        {
            mintAmount = _depositFor(msg.value, msg.sender);
        }
        function depositFor(
            address _user
        ) external payable nonReentrant returns (uint256 mintAmount) {
            mintAmount = _depositFor(msg.value, _user);
        }
        function _depositFor(
            uint256 _amount,
            address _user
        ) internal returns (uint256 mintAmount) {
            require(_amount != 0, "too small");
            uint256 sharePrice;
            uint256 currSharePrice = currentSharePrice();
            if (latestRoundID == 0) {
                sharePrice = MULTIPLIER;
            } else {
                uint256 latestSharePrice = roundPricePerShare[latestRoundID - 1];
                sharePrice = latestSharePrice > currSharePrice
                    ? latestSharePrice
                    : currSharePrice;
            }
            mintAmount = (_amount * MULTIPLIER) / sharePrice;
            AssetsVault(assetsVault).deposit{value: address(this).balance}();
            Minter(minter).mint(_user, mintAmount);
            emit Deposit(_user, _amount, mintAmount, latestRoundID);
        }
        function requestWithdraw(uint256 _shares) external nonReentrant {
            require(_shares != 0, "too small");
            require(latestRoundID != 0, "should withdraw instantly");
            Stone stoneToken = Stone(stone);
            Minter stoneMinter = Minter(minter);
            require(stoneToken.balanceOf(msg.sender) >= _shares, "exceed balance");
            TransferHelper.safeTransferFrom(
                stone,
                msg.sender,
                address(this),
                _shares
            );
            withdrawingSharesInRound = withdrawingSharesInRound + _shares;
            UserReceipt storage receipt = userReceipts[msg.sender];
            if (receipt.withdrawRound == latestRoundID) {
                receipt.withdrawShares = receipt.withdrawShares + _shares;
            } else if (receipt.withdrawRound == 0) {
                receipt.withdrawShares = _shares;
                receipt.withdrawRound = latestRoundID;
            } else {
                // Withdraw previous round share first
                uint256 withdrawAmount = VaultMath.sharesToAsset(
                    receipt.withdrawShares,
                    roundPricePerShare[receipt.withdrawRound]
                );
                stoneMinter.burn(address(this), receipt.withdrawShares);
                withdrawingSharesInPast =
                    withdrawingSharesInPast -
                    receipt.withdrawShares;
                receipt.withdrawShares = _shares;
                receipt.withdrawableAmount =
                    receipt.withdrawableAmount +
                    withdrawAmount;
                receipt.withdrawRound = latestRoundID;
            }
            emit InitiateWithdraw(msg.sender, _shares, latestRoundID);
        }
        function cancelWithdraw(uint256 _shares) external nonReentrant {
            require(_shares != 0, "too small");
            UserReceipt storage receipt = userReceipts[msg.sender];
            require(receipt.withdrawRound == latestRoundID, "no pending withdraw");
            require(receipt.withdrawShares >= _shares, "exceed pending withdraw");
            receipt.withdrawShares = receipt.withdrawShares - _shares;
            TransferHelper.safeTransfer(stone, msg.sender, _shares);
            if (receipt.withdrawShares == 0) {
                receipt.withdrawRound = 0;
            }
            withdrawingSharesInRound = withdrawingSharesInRound - _shares;
            emit CancelWithdraw(msg.sender, _shares, latestRoundID);
        }
        function instantWithdraw(
            uint256 _amount,
            uint256 _shares
        ) external nonReentrant returns (uint256 actualWithdrawn) {
            require(_amount != 0 || _shares != 0, "too small");
            AssetsVault aVault = AssetsVault(assetsVault);
            Minter stoneMinter = Minter(minter);
            (uint256 idleAmount, ) = getVaultAvailableAmount();
            if (_amount != 0) {
                UserReceipt storage receipt = userReceipts[msg.sender];
                if (
                    receipt.withdrawRound != latestRoundID &&
                    receipt.withdrawRound != 0
                ) {
                    // Withdraw previous round share first
                    uint256 withdrawAmount = VaultMath.sharesToAsset(
                        receipt.withdrawShares,
                        roundPricePerShare[receipt.withdrawRound]
                    );
                    stoneMinter.burn(address(this), receipt.withdrawShares);
                    withdrawingSharesInPast =
                        withdrawingSharesInPast -
                        receipt.withdrawShares;
                    receipt.withdrawShares = 0;
                    receipt.withdrawableAmount =
                        receipt.withdrawableAmount +
                        withdrawAmount;
                    receipt.withdrawRound = 0;
                }
                require(
                    receipt.withdrawableAmount >= _amount,
                    "exceed withdrawable"
                );
                receipt.withdrawableAmount = receipt.withdrawableAmount - _amount;
                withdrawableAmountInPast = withdrawableAmountInPast - _amount;
                actualWithdrawn = _amount;
                emit Withdrawn(msg.sender, _amount, latestRoundID);
            }
            if (_shares != 0) {
                uint256 sharePrice;
                if (latestRoundID == 0) {
                    sharePrice = MULTIPLIER;
                } else {
                    uint256 currSharePrice = currentSharePrice();
                    uint256 latestSharePrice = roundPricePerShare[
                        latestRoundID - 1
                    ];
                    sharePrice = latestSharePrice < currSharePrice
                        ? latestSharePrice
                        : currSharePrice;
                }
                uint256 ethAmount = VaultMath.sharesToAsset(_shares, sharePrice);
                stoneMinter.burn(msg.sender, _shares);
                if (ethAmount <= idleAmount) {
                    actualWithdrawn = actualWithdrawn + ethAmount;
                    emit Withdrawn(msg.sender, ethAmount, latestRoundID);
                } else {
                    actualWithdrawn = actualWithdrawn + idleAmount;
                    ethAmount = ethAmount - idleAmount;
                    StrategyController controller = StrategyController(
                        strategyController
                    );
                    uint256 actualAmount = controller.forceWithdraw(ethAmount);
                    actualWithdrawn = actualWithdrawn + actualAmount;
                    emit WithdrawnFromStrategy(
                        msg.sender,
                        ethAmount,
                        actualAmount,
                        latestRoundID
                    );
                }
            }
            require(aVault.getBalance() >= actualWithdrawn, "still need wait");
            uint256 withFee;
            if (withdrawFeeRate != 0) {
                withFee = (actualWithdrawn * withdrawFeeRate) / ONE_HUNDRED_PERCENT;
                aVault.withdraw(feeRecipient, withFee);
                emit FeeCharged(msg.sender, withFee);
            }
            aVault.withdraw(msg.sender, actualWithdrawn - withFee);
        }
        function rollToNextRound() external {
            require(
                block.timestamp > rebaseTime + rebaseTimeInterval,
                "already rebased"
            );
            StrategyController controller = StrategyController(strategyController);
            AssetsVault aVault = AssetsVault(assetsVault);
            uint256 previewSharePrice = currentSharePrice();
            uint256 vaultBalance = aVault.getBalance();
            uint256 amountToWithdraw = VaultMath.sharesToAsset(
                withdrawingSharesInRound,
                previewSharePrice
            );
            uint256 amountVaultNeed = withdrawableAmountInPast + amountToWithdraw;
            uint256 allPendingValue = controller.getAllStrategyPendingValue();
            uint256 vaultIn;
            uint256 vaultOut;
            if (vaultBalance > amountVaultNeed) {
                vaultIn = vaultBalance - amountVaultNeed;
            } else if (vaultBalance + allPendingValue < amountVaultNeed) {
                vaultOut = amountVaultNeed - vaultBalance - allPendingValue;
            }
            controller.rebaseStrategies(vaultIn, vaultOut);
            uint256 newSharePrice = currentSharePrice();
            roundPricePerShare[latestRoundID] = previewSharePrice < newSharePrice
                ? previewSharePrice
                : newSharePrice;
            settlementTime[latestRoundID] = block.timestamp;
            latestRoundID = latestRoundID + 1;
            withdrawingSharesInPast =
                withdrawingSharesInPast +
                withdrawingSharesInRound;
            withdrawableAmountInPast =
                withdrawableAmountInPast +
                VaultMath.sharesToAsset(withdrawingSharesInRound, newSharePrice);
            withdrawingSharesInRound = 0;
            rebaseTime = block.timestamp;
            emit RollToNextRound(latestRoundID, vaultIn, vaultOut, newSharePrice);
        }
        function addStrategy(address _strategy) external onlyProposal {
            StrategyController controller = StrategyController(strategyController);
            controller.addStrategy(_strategy);
            emit StragetyAdded(_strategy);
        }
        function destroyStrategy(address _strategy) external onlyOwner {
            StrategyController controller = StrategyController(strategyController);
            controller.destroyStrategy(_strategy);
            emit StragetyDestroyed(_strategy);
        }
        function clearStrategy(address _strategy) external onlyOwner {
            StrategyController controller = StrategyController(strategyController);
            controller.clearStrategy(_strategy);
            emit StragetyCleared(_strategy);
        }
        function updatePortfolioConfig(
            address[] memory _strategies,
            uint256[] memory _ratios
        ) external onlyProposal {
            StrategyController controller = StrategyController(strategyController);
            controller.setStrategies(_strategies, _ratios);
            emit PortfolioConfigUpdated(_strategies, _ratios);
        }
        function updateProposal(address _proposal) external onlyProposal {
            proposal = _proposal;
        }
        function migrateVault(address _vault) external onlyProposal {
            Minter(minter).setNewVault(_vault);
            AssetsVault(assetsVault).setNewVault(_vault);
            StrategyController(strategyController).setNewVault(_vault);
        }
        function currentSharePrice() public returns (uint256 price) {
            Stone stoneToken = Stone(stone);
            uint256 totalStone = stoneToken.totalSupply();
            if (
                latestRoundID == 0 ||
                totalStone == 0 ||
                totalStone == withdrawingSharesInPast
            ) {
                return MULTIPLIER;
            }
            uint256 etherAmount = AssetsVault(assetsVault).getBalance() +
                StrategyController(strategyController).getAllStrategiesValue() -
                withdrawableAmountInPast;
            uint256 activeShare = totalStone - withdrawingSharesInPast;
            return (etherAmount * MULTIPLIER) / activeShare;
        }
        function getVaultAvailableAmount()
            public
            returns (uint256 idleAmount, uint256 investedAmount)
        {
            AssetsVault vault = AssetsVault(assetsVault);
            if (vault.getBalance() > withdrawableAmountInPast) {
                idleAmount = vault.getBalance() - withdrawableAmountInPast;
            }
            investedAmount = StrategyController(strategyController)
                .getAllStrategyValidValue();
        }
        function setWithdrawFeeRate(uint256 _withdrawFeeRate) external onlyOwner {
            require(_withdrawFeeRate <= MAXMIUM_FEE_RATE, "exceed maximum");
            emit SetWithdrawFeeRate(withdrawFeeRate, _withdrawFeeRate);
            withdrawFeeRate = _withdrawFeeRate;
        }
        function setFeeRecipient(address _feeRecipient) external onlyOwner {
            require(_feeRecipient != address(0), "zero address");
            emit SetFeeRecipient(feeRecipient, _feeRecipient);
            feeRecipient = _feeRecipient;
        }
        function setRebaseInterval(uint256 _interval) external onlyOwner {
            require(_interval <= MINIMUM_REBASE_INTERVAL, "invalid");
            rebaseTimeInterval = _interval;
            emit SetRebaseInterval(rebaseTimeInterval);
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.21;
    import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
    contract AssetsVault {
        address public stoneVault;
        address public strategyController;
        modifier onlyPermit() {
            require(
                stoneVault == msg.sender || strategyController == msg.sender,
                "not permit"
            );
            _;
        }
        constructor(address _stoneVault, address _strategyController) {
            require(
                _stoneVault != address(0) && _strategyController != address(0),
                "ZERO ADDRESS"
            );
            stoneVault = _stoneVault;
            strategyController = _strategyController;
        }
        function deposit() external payable {
            require(msg.value != 0, "too small");
        }
        function withdraw(address _to, uint256 _amount) external onlyPermit {
            TransferHelper.safeTransferETH(_to, _amount);
        }
        function setNewVault(address _vault) external onlyPermit {
            stoneVault = _vault;
        }
        function getBalance() external view returns (uint256 amount) {
            amount = address(this).balance;
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity >=0.6.0;
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    library TransferHelper {
        /// @notice Transfers tokens from the targeted address to the given destination
        /// @notice Errors with 'STF' if transfer fails
        /// @param token The contract address of the token to be transferred
        /// @param from The originating address from which the tokens will be transferred
        /// @param to The destination address of the transfer
        /// @param value The amount to be transferred
        function safeTransferFrom(
            address token,
            address from,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) =
                token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
        }
        /// @notice Transfers tokens from msg.sender to a recipient
        /// @dev Errors with ST if transfer fails
        /// @param token The contract address of the token which will be transferred
        /// @param to The recipient of the transfer
        /// @param value The value of the transfer
        function safeTransfer(
            address token,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
        }
        /// @notice Approves the stipulated contract to spend the given allowance in the given token
        /// @dev Errors with 'SA' if transfer fails
        /// @param token The contract address of the token to be approved
        /// @param to The target of the approval
        /// @param value The amount of the given token the target will be allowed to spend
        function safeApprove(
            address token,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
        }
        /// @notice Transfers ETH to the recipient address
        /// @dev Fails with `STE`
        /// @param to The destination of the transfer
        /// @param value The value to be transferred
        function safeTransferETH(address to, uint256 value) internal {
            (bool success, ) = to.call{value: value}(new bytes(0));
            require(success, 'STE');
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position of the value in the `values` array, plus 1 because index 0
            // means a value is not in the set.
            mapping(bytes32 => uint256) _indexes;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._indexes[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We read and store the value's index to prevent multiple reads from the same storage slot
            uint256 valueIndex = set._indexes[value];
            if (valueIndex != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 toDeleteIndex = valueIndex - 1;
                uint256 lastIndex = set._values.length - 1;
                if (lastIndex != toDeleteIndex) {
                    bytes32 lastValue = set._values[lastIndex];
                    // Move the last value to the index where the value to delete is
                    set._values[toDeleteIndex] = lastValue;
                    // Update the index for the moved value
                    set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                }
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the index for the deleted slot
                delete set._indexes[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._indexes[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 amount) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.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}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * 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.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            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 override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address to, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(from, to, amount);
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, allowance(owner, spender) + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = allowance(owner, spender);
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(address from, address to, uint256 amount) internal virtual {
            require(from != address(0), "ERC20: transfer from the zero address");
            require(to != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(from, to, amount);
            uint256 fromBalance = _balances[from];
            require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[from] = fromBalance - amount;
                // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                // decrementing then incrementing.
                _balances[to] += amount;
            }
            emit Transfer(from, to, amount);
            _afterTokenTransfer(from, to, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            unchecked {
                // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                _balances[account] += amount;
            }
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
                // Overflow not possible: amount <= accountBalance <= totalSupply.
                _totalSupply -= amount;
            }
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= amount, "ERC20: insufficient allowance");
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 ReentrancyGuard {
        // 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;
        uint256 private _status;
        constructor() {
            _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 {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
        }
        function _nonReentrantAfter() private {
            // 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) {
            return _status == _ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT OR Apache-2.0
    pragma solidity >=0.7.6;
    library ExcessivelySafeCall {
        uint256 constant LOW_28_MASK =
        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
        /// @notice Use when you _really_ really _really_ don't trust the called
        /// contract. This prevents the called contract from causing reversion of
        /// the caller in as many ways as we can.
        /// @dev The main difference between this and a solidity low-level call is
        /// that we limit the number of bytes that the callee can cause to be
        /// copied to caller memory. This prevents stupid things like malicious
        /// contracts returning 10,000,000 bytes causing a local OOG when copying
        /// to memory.
        /// @param _target The address to call
        /// @param _gas The amount of gas to forward to the remote contract
        /// @param _maxCopy The maximum number of bytes of returndata to copy
        /// to memory.
        /// @param _calldata The data to send to the remote contract
        /// @return success and returndata, as `.call()`. Returndata is capped to
        /// `_maxCopy` bytes.
        function excessivelySafeCall(
            address _target,
            uint256 _gas,
            uint16 _maxCopy,
            bytes memory _calldata
        ) internal returns (bool, bytes memory) {
            // set up for assembly call
            uint256 _toCopy;
            bool _success;
            bytes memory _returnData = new bytes(_maxCopy);
            // dispatch message to recipient
            // by assembly calling "handle" function
            // we call via assembly to avoid memcopying a very large returndata
            // returned by a malicious contract
            assembly {
                _success := call(
                _gas, // gas
                _target, // recipient
                0, // ether value
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
                )
            // limit our copy to 256 bytes
                _toCopy := returndatasize()
                if gt(_toCopy, _maxCopy) {
                    _toCopy := _maxCopy
                }
            // Store the length of the copied bytes
                mstore(_returnData, _toCopy)
            // copy the bytes from returndata[0:_toCopy]
                returndatacopy(add(_returnData, 0x20), 0, _toCopy)
            }
            return (_success, _returnData);
        }
        /// @notice Use when you _really_ really _really_ don't trust the called
        /// contract. This prevents the called contract from causing reversion of
        /// the caller in as many ways as we can.
        /// @dev The main difference between this and a solidity low-level call is
        /// that we limit the number of bytes that the callee can cause to be
        /// copied to caller memory. This prevents stupid things like malicious
        /// contracts returning 10,000,000 bytes causing a local OOG when copying
        /// to memory.
        /// @param _target The address to call
        /// @param _gas The amount of gas to forward to the remote contract
        /// @param _maxCopy The maximum number of bytes of returndata to copy
        /// to memory.
        /// @param _calldata The data to send to the remote contract
        /// @return success and returndata, as `.call()`. Returndata is capped to
        /// `_maxCopy` bytes.
        function excessivelySafeStaticCall(
            address _target,
            uint256 _gas,
            uint16 _maxCopy,
            bytes memory _calldata
        ) internal view returns (bool, bytes memory) {
            // set up for assembly call
            uint256 _toCopy;
            bool _success;
            bytes memory _returnData = new bytes(_maxCopy);
            // dispatch message to recipient
            // by assembly calling "handle" function
            // we call via assembly to avoid memcopying a very large returndata
            // returned by a malicious contract
            assembly {
                _success := staticcall(
                _gas, // gas
                _target, // recipient
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
                )
            // limit our copy to 256 bytes
                _toCopy := returndatasize()
                if gt(_toCopy, _maxCopy) {
                    _toCopy := _maxCopy
                }
            // Store the length of the copied bytes
                mstore(_returnData, _toCopy)
            // copy the bytes from returndata[0:_toCopy]
                returndatacopy(add(_returnData, 0x20), 0, _toCopy)
            }
            return (_success, _returnData);
        }
        /**
         * @notice Swaps function selectors in encoded contract calls
         * @dev Allows reuse of encoded calldata for functions with identical
         * argument types but different names. It simply swaps out the first 4 bytes
         * for the new selector. This function modifies memory in place, and should
         * only be used with caution.
         * @param _newSelector The new 4-byte selector
         * @param _buf The encoded contract args
         */
        function swapSelector(bytes4 _newSelector, bytes memory _buf)
        internal
        pure
        {
            require(_buf.length >= 4);
            uint256 _mask = LOW_28_MASK;
            assembly {
            // load the first word of
                let _word := mload(add(_buf, 0x20))
            // mask out the top 4 bytes
            // /x
                _word := and(_word, _mask)
                _word := or(_newSelector, _word)
                mstore(add(_buf, 0x20), _word)
            }
        }
    }
    // SPDX-License-Identifier: Unlicense
    /*
     * @title Solidity Bytes Arrays Utils
     * @author Gonçalo Sá <[email protected]>
     *
     * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
     *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
     */
    pragma solidity >=0.8.0 <0.9.0;
    library BytesLib {
        function concat(
            bytes memory _preBytes,
            bytes memory _postBytes
        )
        internal
        pure
        returns (bytes memory)
        {
            bytes memory tempBytes;
            assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
                tempBytes := mload(0x40)
            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
                let length := mload(_preBytes)
                mstore(tempBytes, length)
            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
                let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
                let end := add(mc, length)
                for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                    let cc := add(_preBytes, 0x20)
                } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                    mstore(mc, mload(cc))
                }
            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
                length := mload(_postBytes)
                mstore(tempBytes, add(length, mload(tempBytes)))
            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
                mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
                end := add(mc, length)
                for {
                    let cc := add(_postBytes, 0x20)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }
            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
                mstore(0x40, and(
                add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                not(31) // Round down to the nearest 32 bytes.
                ))
            }
            return tempBytes;
        }
        function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
            assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
                let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
                let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                switch add(lt(slength, 32), lt(newlength, 32))
                case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                    sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                    // we can just add to the slot contents because the
                    // bytes we want to change are the LSBs
                    fslot,
                    add(
                    mul(
                    div(
                    // load the bytes from memory
                    mload(add(_postBytes, 0x20)),
                    // zero all bytes to the right
                    exp(0x100, sub(32, mlength))
                    ),
                    // and now shift left the number of bytes to
                    // leave space for the length in the slot
                    exp(0x100, sub(32, newlength))
                    ),
                    // increase length by the double of the memory
                    // bytes length
                    mul(mlength, 2)
                    )
                    )
                    )
                }
                case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes.slot)
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                // save new length
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.
                    let submod := sub(32, slength)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
                    sstore(
                    sc,
                    add(
                    and(
                    fslot,
                    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                    ),
                    and(mload(mc), mask)
                    )
                    )
                    for {
                        mc := add(mc, 0x20)
                        sc := add(sc, 1)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
                    mask := exp(0x100, sub(mc, end))
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
                default {
                // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                // save new length
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                    let slengthmod := mod(slength, 32)
                    let mlengthmod := mod(mlength, 32)
                    let submod := sub(32, slengthmod)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
                    sstore(sc, add(sload(sc), and(mload(mc), mask)))
                    for {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
                    mask := exp(0x100, sub(mc, end))
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
            }
        }
        function slice(
            bytes memory _bytes,
            uint256 _start,
            uint256 _length
        )
        internal
        pure
        returns (bytes memory)
        {
            require(_length + 31 >= _length, "slice_overflow");
            require(_bytes.length >= _start + _length, "slice_outOfBounds");
            bytes memory tempBytes;
            assembly {
                switch iszero(_length)
                case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                    tempBytes := mload(0x40)
                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                    let lengthmod := and(_length, 31)
                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                    let end := add(mc, _length)
                    for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                    } lt(mc, end) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        mstore(mc, mload(cc))
                    }
                    mstore(tempBytes, _length)
                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                    mstore(0x40, and(add(mc, 31), not(31)))
                }
                //if we want a zero-length slice let's just return a zero-length array
                default {
                    tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                    mstore(tempBytes, 0)
                    mstore(0x40, add(tempBytes, 0x20))
                }
            }
            return tempBytes;
        }
        function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
            require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
            address tempAddress;
            assembly {
                tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
            }
            return tempAddress;
        }
        function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
            require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
            uint8 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x1), _start))
            }
            return tempUint;
        }
        function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
            require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
            uint16 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x2), _start))
            }
            return tempUint;
        }
        function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
            require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
            uint32 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x4), _start))
            }
            return tempUint;
        }
        function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
            require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
            uint64 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x8), _start))
            }
            return tempUint;
        }
        function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
            require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
            uint96 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0xc), _start))
            }
            return tempUint;
        }
        function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
            require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
            uint128 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x10), _start))
            }
            return tempUint;
        }
        function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
            require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
            uint256 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x20), _start))
            }
            return tempUint;
        }
        function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
            require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
            bytes32 tempBytes32;
            assembly {
                tempBytes32 := mload(add(add(_bytes, 0x20), _start))
            }
            return tempBytes32;
        }
        function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
            bool success = true;
            assembly {
                let length := mload(_preBytes)
            // if lengths don't match the arrays are not equal
                switch eq(length, mload(_postBytes))
                case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                    let cb := 1
                    let mc := add(_preBytes, 0x20)
                    let end := add(mc, length)
                    for {
                        let cc := add(_postBytes, 0x20)
                    // the next line is the loop condition:
                    // while(uint256(mc < end) + cb == 2)
                    } eq(add(lt(mc, end), cb), 2) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                    // if any of these checks fails then arrays are not equal
                        if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                            success := 0
                            cb := 0
                        }
                    }
                }
                default {
                // unsuccess:
                    success := 0
                }
            }
            return success;
        }
        function equalStorage(
            bytes storage _preBytes,
            bytes memory _postBytes
        )
        internal
        view
        returns (bool)
        {
            bool success = true;
            assembly {
            // we know _preBytes_offset is 0
                let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
            // if lengths don't match the arrays are not equal
                switch eq(slength, mlength)
                case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                    if iszero(iszero(slength)) {
                        switch lt(slength, 32)
                        case 1 {
                        // blank the last byte which is the length
                            fslot := mul(div(fslot, 0x100), 0x100)
                            if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                                success := 0
                            }
                        }
                        default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                            let cb := 1
                        // get the keccak hash to get the contents of the array
                            mstore(0x0, _preBytes.slot)
                            let sc := keccak256(0x0, 0x20)
                            let mc := add(_postBytes, 0x20)
                            let end := add(mc, mlength)
                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                            for {} eq(add(lt(mc, end), cb), 2) {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } {
                                if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                    success := 0
                                    cb := 0
                                }
                            }
                        }
                    }
                }
                default {
                // unsuccess:
                    success := 0
                }
            }
            return success;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../OFT.sol";
    contract BasedOFT is OFT {
        constructor(string memory _name, string memory _symbol, address _lzEndpoint) OFT(_name, _symbol, _lzEndpoint) {}
        function circulatingSupply() public view virtual override returns (uint) {
            unchecked {
                return totalSupply() - balanceOf(address(this));
            }
        }
        function _debitFrom(address _from, uint16, bytes memory, uint _amount) internal virtual override returns(uint) {
            address spender = _msgSender();
            if (_from != spender) _spendAllowance(_from, spender, _amount);
            _transfer(_from, address(this), _amount);
            return _amount;
        }
        function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns(uint) {
            _transfer(address(this), _toAddress, _amount);
            return _amount;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../../lzApp/NonblockingLzApp.sol";
    import "./IOFTCore.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    abstract contract OFTCore is NonblockingLzApp, ERC165, IOFTCore {
        using BytesLib for bytes;
        uint public constant NO_EXTRA_GAS = 0;
        // packet type
        uint16 public constant PT_SEND = 0;
        bool public useCustomAdapterParams;
        constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {}
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return interfaceId == type(IOFTCore).interfaceId || super.supportsInterface(interfaceId);
        }
        function estimateSendFee(uint16 _dstChainId, bytes calldata _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) public view virtual override returns (uint nativeFee, uint zroFee) {
            // mock the payload for sendFrom()
            bytes memory payload = abi.encode(PT_SEND, _toAddress, _amount);
            return lzEndpoint.estimateFees(_dstChainId, address(this), payload, _useZro, _adapterParams);
        }
        function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) public payable virtual override {
            _send(_from, _dstChainId, _toAddress, _amount, _refundAddress, _zroPaymentAddress, _adapterParams);
        }
        function setUseCustomAdapterParams(bool _useCustomAdapterParams) public virtual onlyOwner {
            useCustomAdapterParams = _useCustomAdapterParams;
            emit SetUseCustomAdapterParams(_useCustomAdapterParams);
        }
        function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
            uint16 packetType;
            assembly {
                packetType := mload(add(_payload, 32))
            }
            if (packetType == PT_SEND) {
                _sendAck(_srcChainId, _srcAddress, _nonce, _payload);
            } else {
                revert("OFTCore: unknown packet type");
            }
        }
        function _send(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams) internal virtual {
            _checkAdapterParams(_dstChainId, PT_SEND, _adapterParams, NO_EXTRA_GAS);
            uint amount = _debitFrom(_from, _dstChainId, _toAddress, _amount);
            bytes memory lzPayload = abi.encode(PT_SEND, _toAddress, amount);
            _lzSend(_dstChainId, lzPayload, _refundAddress, _zroPaymentAddress, _adapterParams, msg.value);
            emit SendToChain(_dstChainId, _from, _toAddress, amount);
        }
        function _sendAck(uint16 _srcChainId, bytes memory, uint64, bytes memory _payload) internal virtual {
            (, bytes memory toAddressBytes, uint amount) = abi.decode(_payload, (uint16, bytes, uint));
            address to = toAddressBytes.toAddress(0);
            amount = _creditTo(_srcChainId, to, amount);
            emit ReceiveFromChain(_srcChainId, to, amount);
        }
        function _checkAdapterParams(uint16 _dstChainId, uint16 _pkType, bytes memory _adapterParams, uint _extraGas) internal virtual {
            if (useCustomAdapterParams) {
                _checkGasLimit(_dstChainId, _pkType, _adapterParams, _extraGas);
            } else {
                require(_adapterParams.length == 0, "OFTCore: _adapterParams must be empty.");
            }
        }
        function _debitFrom(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _amount) internal virtual returns(uint);
        function _creditTo(uint16 _srcChainId, address _toAddress, uint _amount) internal virtual returns(uint);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    import "./IOFT.sol";
    import "./OFTCore.sol";
    // override decimal() function is needed
    contract OFT is OFTCore, ERC20, IOFT {
        constructor(string memory _name, string memory _symbol, address _lzEndpoint) ERC20(_name, _symbol) OFTCore(_lzEndpoint) {}
        function supportsInterface(bytes4 interfaceId) public view virtual override(OFTCore, IERC165) returns (bool) {
            return interfaceId == type(IOFT).interfaceId || interfaceId == type(IERC20).interfaceId || super.supportsInterface(interfaceId);
        }
        function token() public view virtual override returns (address) {
            return address(this);
        }
        function circulatingSupply() public view virtual override returns (uint) {
            return totalSupply();
        }
        function _debitFrom(address _from, uint16, bytes memory, uint _amount) internal virtual override returns(uint) {
            address spender = _msgSender();
            if (_from != spender) _spendAllowance(_from, spender, _amount);
            _burn(_from, _amount);
            return _amount;
        }
        function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns(uint) {
            _mint(_toAddress, _amount);
            return _amount;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /**
     * @dev Interface of the IOFT core standard
     */
    interface IOFTCore is IERC165 {
        /**
         * @dev estimate send token `_tokenId` to (`_dstChainId`, `_toAddress`)
         * _dstChainId - L0 defined chain id to send tokens too
         * _toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain
         * _amount - amount of the tokens to transfer
         * _useZro - indicates to use zro to pay L0 fees
         * _adapterParam - flexible bytes array to indicate messaging adapter services in L0
         */
        function estimateSendFee(uint16 _dstChainId, bytes calldata _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee);
        /**
         * @dev send `_amount` amount of token to (`_dstChainId`, `_toAddress`) from `_from`
         * `_from` the owner of token
         * `_dstChainId` the destination chain identifier
         * `_toAddress` can be any size depending on the `dstChainId`.
         * `_amount` the quantity of tokens in wei
         * `_refundAddress` the address LayerZero refunds if too much message fee is sent
         * `_zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token)
         * `_adapterParams` is a flexible bytes array to indicate messaging adapter services
         */
        function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
        /**
         * @dev returns the circulating amount of tokens on current chain
         */
        function circulatingSupply() external view returns (uint);
        /**
         * @dev returns the address of the ERC20 token
         */
        function token() external view returns (address);
        /**
         * @dev Emitted when `_amount` tokens are moved from the `_sender` to (`_dstChainId`, `_toAddress`)
         * `_nonce` is the outbound nonce
         */
        event SendToChain(uint16 indexed _dstChainId, address indexed _from, bytes _toAddress, uint _amount);
        /**
         * @dev Emitted when `_amount` tokens are received from `_srcChainId` into the `_toAddress` on the local chain.
         * `_nonce` is the inbound nonce.
         */
        event ReceiveFromChain(uint16 indexed _srcChainId, address indexed _to, uint _amount);
        event SetUseCustomAdapterParams(bool _useCustomAdapterParams);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    import "./IOFTCore.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    /**
     * @dev Interface of the OFT standard
     */
    interface IOFT is IOFTCore, IERC20 {
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./LzApp.sol";
    import "../util/ExcessivelySafeCall.sol";
    /*
     * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel
     * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking
     * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress)
     */
    abstract contract NonblockingLzApp is LzApp {
        using ExcessivelySafeCall for address;
        constructor(address _endpoint) LzApp(_endpoint) {}
        mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages;
        event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);
        event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash);
        // overriding the virtual function in LzReceiver
        function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
            (bool success, bytes memory reason) = address(this).excessivelySafeCall(gasleft(), 150, abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload));
            // try-catch all errors/exceptions
            if (!success) {
                _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason);
            }
        }
        function _storeFailedMessage(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload, bytes memory _reason) internal virtual {
            failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload);
            emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason);
        }
        function nonblockingLzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual {
            // only internal transaction
            require(_msgSender() == address(this), "NonblockingLzApp: caller must be LzApp");
            _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
        }
        //@notice override this function
        function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
        function retryMessage(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public payable virtual {
            // assert there is message to retry
            bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce];
            require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message");
            require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload");
            // clear the stored message
            failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0);
            // execute the message. revert if it fails again
            _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
            emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "../interfaces/ILayerZeroReceiver.sol";
    import "../interfaces/ILayerZeroUserApplicationConfig.sol";
    import "../interfaces/ILayerZeroEndpoint.sol";
    import "../util/BytesLib.sol";
    /*
     * a generic LzReceiver implementation
     */
    abstract contract LzApp is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
        using BytesLib for bytes;
        // ua can not send payload larger than this by default, but it can be changed by the ua owner
        uint constant public DEFAULT_PAYLOAD_SIZE_LIMIT = 10000;
        ILayerZeroEndpoint public immutable lzEndpoint;
        mapping(uint16 => bytes) public trustedRemoteLookup;
        mapping(uint16 => mapping(uint16 => uint)) public minDstGasLookup;
        mapping(uint16 => uint) public payloadSizeLimitLookup;
        address public precrime;
        event SetPrecrime(address precrime);
        event SetTrustedRemote(uint16 _remoteChainId, bytes _path);
        event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress);
        event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint _minDstGas);
        constructor(address _endpoint) {
            lzEndpoint = ILayerZeroEndpoint(_endpoint);
        }
        function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual override {
            // lzReceive must be called by the endpoint for security
            require(_msgSender() == address(lzEndpoint), "LzApp: invalid endpoint caller");
            bytes memory trustedRemote = trustedRemoteLookup[_srcChainId];
            // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote.
            require(_srcAddress.length == trustedRemote.length && trustedRemote.length > 0 && keccak256(_srcAddress) == keccak256(trustedRemote), "LzApp: invalid source sending contract");
            _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
        }
        // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging
        function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
        function _lzSend(uint16 _dstChainId, bytes memory _payload, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams, uint _nativeFee) internal virtual {
            bytes memory trustedRemote = trustedRemoteLookup[_dstChainId];
            require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
            _checkPayloadSize(_dstChainId, _payload.length);
            lzEndpoint.send{value: _nativeFee}(_dstChainId, trustedRemote, _payload, _refundAddress, _zroPaymentAddress, _adapterParams);
        }
        function _checkGasLimit(uint16 _dstChainId, uint16 _type, bytes memory _adapterParams, uint _extraGas) internal view virtual {
            uint providedGasLimit = _getGasLimit(_adapterParams);
            uint minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas;
            require(minGasLimit > 0, "LzApp: minGasLimit not set");
            require(providedGasLimit >= minGasLimit, "LzApp: gas limit is too low");
        }
        function _getGasLimit(bytes memory _adapterParams) internal pure virtual returns (uint gasLimit) {
            require(_adapterParams.length >= 34, "LzApp: invalid adapterParams");
            assembly {
                gasLimit := mload(add(_adapterParams, 34))
            }
        }
        function _checkPayloadSize(uint16 _dstChainId, uint _payloadSize) internal view virtual {
            uint payloadSizeLimit = payloadSizeLimitLookup[_dstChainId];
            if (payloadSizeLimit == 0) { // use default if not set
                payloadSizeLimit = DEFAULT_PAYLOAD_SIZE_LIMIT;
            }
            require(_payloadSize <= payloadSizeLimit, "LzApp: payload size is too large");
        }
        //---------------------------UserApplication config----------------------------------------
        function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) {
            return lzEndpoint.getConfig(_version, _chainId, address(this), _configType);
        }
        // generic config for LayerZero user Application
        function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external override onlyOwner {
            lzEndpoint.setConfig(_version, _chainId, _configType, _config);
        }
        function setSendVersion(uint16 _version) external override onlyOwner {
            lzEndpoint.setSendVersion(_version);
        }
        function setReceiveVersion(uint16 _version) external override onlyOwner {
            lzEndpoint.setReceiveVersion(_version);
        }
        function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner {
            lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress);
        }
        // _path = abi.encodePacked(remoteAddress, localAddress)
        // this function set the trusted path for the cross-chain communication
        function setTrustedRemote(uint16 _remoteChainId, bytes calldata _path) external onlyOwner {
            trustedRemoteLookup[_remoteChainId] = _path;
            emit SetTrustedRemote(_remoteChainId, _path);
        }
        function setTrustedRemoteAddress(uint16 _remoteChainId, bytes calldata _remoteAddress) external onlyOwner {
            trustedRemoteLookup[_remoteChainId] = abi.encodePacked(_remoteAddress, address(this));
            emit SetTrustedRemoteAddress(_remoteChainId, _remoteAddress);
        }
        function getTrustedRemoteAddress(uint16 _remoteChainId) external view returns (bytes memory) {
            bytes memory path = trustedRemoteLookup[_remoteChainId];
            require(path.length != 0, "LzApp: no trusted path record");
            return path.slice(0, path.length - 20); // the last 20 bytes should be address(this)
        }
        function setPrecrime(address _precrime) external onlyOwner {
            precrime = _precrime;
            emit SetPrecrime(_precrime);
        }
        function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint _minGas) external onlyOwner {
            require(_minGas > 0, "LzApp: invalid minGas");
            minDstGasLookup[_dstChainId][_packetType] = _minGas;
            emit SetMinDstGas(_dstChainId, _packetType, _minGas);
        }
        // if the size is 0, it means default size limit
        function setPayloadSizeLimit(uint16 _dstChainId, uint _size) external onlyOwner {
            payloadSizeLimitLookup[_dstChainId] = _size;
        }
        //--------------------------- VIEW FUNCTION ----------------------------------------
        function isTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool) {
            bytes memory trustedSource = trustedRemoteLookup[_srcChainId];
            return keccak256(trustedSource) == keccak256(_srcAddress);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    interface ILayerZeroUserApplicationConfig {
        // @notice set the configuration of the LayerZero messaging library of the specified version
        // @param _version - messaging library version
        // @param _chainId - the chainId for the pending config change
        // @param _configType - type of configuration. every messaging library has its own convention.
        // @param _config - configuration in the bytes. can encode arbitrary content.
        function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external;
        // @notice set the send() LayerZero messaging library version to _version
        // @param _version - new messaging library version
        function setSendVersion(uint16 _version) external;
        // @notice set the lzReceive() LayerZero messaging library version to _version
        // @param _version - new messaging library version
        function setReceiveVersion(uint16 _version) external;
        // @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload
        // @param _srcChainId - the chainId of the source chain
        // @param _srcAddress - the contract address of the source contract at the source chain
        function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    interface ILayerZeroReceiver {
        // @notice LayerZero endpoint will invoke this function to deliver the message on the destination
        // @param _srcChainId - the source endpoint identifier
        // @param _srcAddress - the source sending contract address from the source chain
        // @param _nonce - the ordered message nonce
        // @param _payload - the signed payload is the UA bytes has encoded to be sent
        function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.0;
    import "./ILayerZeroUserApplicationConfig.sol";
    interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig {
        // @notice send a LayerZero message to the specified address at a LayerZero endpoint.
        // @param _dstChainId - the destination chain identifier
        // @param _destination - the address on destination chain (in bytes). address length/format may vary by chains
        // @param _payload - a custom bytes payload to send to the destination contract
        // @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address
        // @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction
        // @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination
        function send(uint16 _dstChainId, bytes calldata _destination, bytes calldata _payload, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
        // @notice used by the messaging library to publish verified payload
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source contract (as bytes) at the source chain
        // @param _dstAddress - the address on destination chain
        // @param _nonce - the unbound message ordering nonce
        // @param _gasLimit - the gas limit for external contract execution
        // @param _payload - verified payload to send to the destination contract
        function receivePayload(uint16 _srcChainId, bytes calldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytes calldata _payload) external;
        // @notice get the inboundNonce of a lzApp from a source chain which could be EVM or non-EVM chain
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source chain contract address
        function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (uint64);
        // @notice get the outboundNonce from this source chain which, consequently, is always an EVM
        // @param _srcAddress - the source chain contract address
        function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view returns (uint64);
        // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery
        // @param _dstChainId - the destination chain identifier
        // @param _userApplication - the user app address on this EVM chain
        // @param _payload - the custom message to send over LayerZero
        // @param _payInZRO - if false, user app pays the protocol fee in native token
        // @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain
        function estimateFees(uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParam) external view returns (uint nativeFee, uint zroFee);
        // @notice get this Endpoint's immutable source identifier
        function getChainId() external view returns (uint16);
        // @notice the interface to retry failed message on this Endpoint destination
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source chain contract address
        // @param _payload - the payload to be retried
        function retryPayload(uint16 _srcChainId, bytes calldata _srcAddress, bytes calldata _payload) external;
        // @notice query if any STORED payload (message blocking) at the endpoint.
        // @param _srcChainId - the source chain identifier
        // @param _srcAddress - the source chain contract address
        function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool);
        // @notice query if the _libraryAddress is valid for sending msgs.
        // @param _userApplication - the user app address on this EVM chain
        function getSendLibraryAddress(address _userApplication) external view returns (address);
        // @notice query if the _libraryAddress is valid for receiving msgs.
        // @param _userApplication - the user app address on this EVM chain
        function getReceiveLibraryAddress(address _userApplication) external view returns (address);
        // @notice query if the non-reentrancy guard for send() is on
        // @return true if the guard is on. false otherwise
        function isSendingPayload() external view returns (bool);
        // @notice query if the non-reentrancy guard for receive() is on
        // @return true if the guard is on. false otherwise
        function isReceivingPayload() external view returns (bool);
        // @notice get the configuration of the LayerZero messaging library of the specified version
        // @param _version - messaging library version
        // @param _chainId - the chainId for the pending config change
        // @param _userApplication - the contract address of the user application
        // @param _configType - type of configuration. every messaging library has its own convention.
        function getConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) external view returns (bytes memory);
        // @notice get the send() LayerZero messaging library version
        // @param _userApplication - the contract address of the user application
        function getSendVersion(address _userApplication) external view returns (uint16);
        // @notice get the lzReceive() LayerZero messaging library version
        // @param _userApplication - the contract address of the user application
        function getReceiveVersion(address _userApplication) external view returns (uint16);
    }