// File: contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmController.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "./PendlePowerFarmControllerHelper.sol";

contract PendlePowerFarmController is PendlePowerFarmControllerHelper {

    constructor(
        address _vePendle,
        address _pendleToken,
        address _voterContract,
        address _voterRewardsClaimerAddress,
        address _wiseOracleHub
    )
        PendlePowerFarmControllerBase(
            _vePendle,
            _pendleToken,
            _voterContract,
            _voterRewardsClaimerAddress,
            _wiseOracleHub
        )
    {}

    function withdrawLp(
        address _pendleMarket,
        address _to,
        uint256 _amount
    )
        external
        onlyChildContract(_pendleMarket)
    {
        _safeTransfer(
            _pendleMarket,
            _to,
            _amount
        );

        emit WithdrawLp(
            _pendleMarket,
            _to,
            _amount
        );
    }

    function exchangeRewardsForCompoundingWithIncentive(
        address _pendleMarket,
        address _rewardToken,
        uint256 _rewardAmount
    )
        external
        syncSupply(_pendleMarket)
        returns (uint256)
    {
        CompoundStruct memory childInfo = pendleChildCompoundInfo[
            _pendleMarket
        ];

        uint256 index = _findIndex(
            childInfo.rewardTokens,
            _rewardToken
        );

        if (childInfo.reservedForCompound[index] < _rewardAmount) {
            revert NotEnoughCompound();
        }

        uint256 sendingAmount = _getAmountToSend(
            _pendleMarket,
            _getTokensInETH(
                _rewardToken,
                _rewardAmount
            )
        );

        childInfo.reservedForCompound[index] -= _rewardAmount;
        pendleChildCompoundInfo[_pendleMarket] = childInfo;

        _safeTransferFrom(
            _pendleMarket,
            msg.sender,
            address(this),
            sendingAmount
        );

        IPendlePowerFarmToken(pendleChildAddress[_pendleMarket]).addCompoundRewards(
            sendingAmount
        );

        _safeTransfer(
            childInfo.rewardTokens[index],
            msg.sender,
            _rewardAmount
        );

        emit ExchangeRewardsForCompounding(
            _pendleMarket,
            _rewardToken,
            _rewardAmount,
            sendingAmount
        );

        return sendingAmount;
    }

    function exchangeLpFeesForPendleWithIncentive(
        address _pendleMarket,
        uint256 _pendleChildShares
    )
        external
        syncSupply(_pendleMarket)
        returns (
            uint256,
            uint256
        )
    {
        if (_pendleChildShares == 0) {
            revert ZeroShares();
        }

        address pendleChild = pendleChildAddress[
            _pendleMarket
        ];

        uint256 tokenAmountSend = _getAmountToSend(
            PENDLE_TOKEN_ADDRESS,
            _getTokensInETH(
                pendleChild,
                _pendleChildShares
            )
        );

        reservedPendleForLocking += tokenAmountSend;

        _safeTransferFrom(
            PENDLE_TOKEN_ADDRESS,
            msg.sender,
            address(this),
            tokenAmountSend
        );

        uint256 withdrawnAmount = IPendlePowerFarmToken(pendleChild).withdrawExactShares(
            _pendleChildShares
        );

        _safeTransfer(
            _pendleMarket,
            msg.sender,
            withdrawnAmount
        );

        emit ExchangeLpFeesForPendle(
            _pendleMarket,
            _pendleChildShares,
            tokenAmountSend,
            withdrawnAmount
        );

        return(
            tokenAmountSend,
            withdrawnAmount
        );
    }

    function skim(
        address _pendleMarket
    )
        external
        returns (uint256)
    {
        address childMarket = pendleChildAddress[
            _pendleMarket
        ];

        if (childMarket == ZERO_ADDRESS) {
            revert WrongAddress();
        }

        uint256 balance = IPendleMarket(_pendleMarket).balanceOf(
            address(this)
        );

        uint256 totalAssets = IPendlePowerFarmToken(
            childMarket
        ).totalLpAssets();

        if (balance < totalAssets + 1) {
            revert NothingToSkim();
        }

        uint256 difference = balance
            - totalAssets
            + 1;

        _safeTransfer(
            _pendleMarket,
            master,
            difference
        );

        return difference;
    }

    function addPendleMarket(
        address _pendleMarket,
        address _pendleChildAddress
    )
        external
        onlyMaster
    {
        if (pendleChildAddress[_pendleMarket] > ZERO_ADDRESS) {
            revert AlreadySet();
        }

        pendleChildAddress[_pendleMarket] = _pendleChildAddress;

        _setRewardTokens(
            _pendleMarket,
            _getRewardTokens(
                _pendleMarket
            )
        );

        CompoundStruct storage childInfo = pendleChildCompoundInfo[
            _pendleMarket
        ];

        uint256 rewardTokensLength = childInfo
            .rewardTokens
            .length;

        childInfo.lastIndex = new uint128[](
            rewardTokensLength
        );

        childInfo.reservedForCompound = new uint256[](
            rewardTokensLength
        );

        uint256 i;

        while (i < rewardTokensLength) {

            address token = childInfo.rewardTokens[i];

            childInfo.lastIndex[i] = _getUserRewardIndex(
                _pendleMarket,
                token,
                address(this)
            );

            childInfo.reservedForCompound[i] = 0;

            _checkFeed(
                token
            );

            unchecked {
                ++i;
            }
        }

        _checkFeed(
            _pendleMarket
        );

        activePendleMarkets.push(
            _pendleMarket
        );

        emit AddPendleMarket(
            _pendleMarket,
            _pendleChildAddress
        );
    }

    function updateRewardTokens(
        address _pendleMarket
    )
        external
        onlyChildContract(_pendleMarket)
        returns (bool)
    {
        address[] memory rewardTokens = _getRewardTokens(
            _pendleMarket
        );

        if (_compareHashes(_pendleMarket, rewardTokens) == true) {
            return false;
        }

        _setRewardTokens(
            _pendleMarket,
            rewardTokens
        );

        emit UpdateRewardTokens(
            _pendleMarket,
            rewardTokens
        );

        return true;
    }

    function changeExchangeIncentive(
        uint256 _newExchangeIncentive
    )
        external
        onlyMaster
    {
        exchangeIncentive = _newExchangeIncentive;

        emit ChangeExchangeIncentive(
            _newExchangeIncentive
        );
    }

    function changeMintFee(
        address _pendleMarket,
        uint256 _newFee
    )
        external
        onlyMaster
    {
        address child = pendleChildAddress[
            _pendleMarket
        ];

        if (child == ZERO_ADDRESS) {
            revert WrongAddress();
        }

        IPendlePowerFarmToken(
            child
        ).changeMintFee(
            _newFee
        );

        emit ChangeMintFee(
            _pendleMarket,
            _newFee
        );
    }

    /**
     * @dev Can also be used to extend existing lock.
     */
    function lockPendle(
        uint256 _amount,
        uint128 _weeks,
        bool _fromInside,
        bool _sameExpiry
    )
        external
        onlyMaster
        returns (uint256 newVeBalance)
    {
        syncAllSupply();

        uint256 currentExpiry = _getExpiry();

        uint128 expiry = _sameExpiry
            ? uint128(currentExpiry)
            : _calcExpiry(
                _weeks
            );

        if (uint256(expiry) < currentExpiry) {
            revert LockTimeTooShort();
        }

        if (_amount > 0) {

            _safeApprove(
                PENDLE_TOKEN_ADDRESS,
                VE_PENDLE_CONTRACT_ADDRESS,
                _amount
            );

            if (_fromInside == false) {
                _safeTransferFrom(
                    PENDLE_TOKEN_ADDRESS,
                    msg.sender,
                    address(this),
                    _amount
                );
            }
        }

        newVeBalance = PENDLE_LOCK.increaseLockPosition(
            uint128(_amount),
            expiry
        );

        syncAllSupply();

        if (_fromInside == false) {
            return newVeBalance;
        }

        if (_amount > 0) {
            reservedPendleForLocking -= _amount;
        }

        emit LockPendle(
            _amount,
            expiry,
            newVeBalance,
            _fromInside,
            _sameExpiry,
            block.timestamp
        );
    }

    function claimArb(
        uint256 _accrued,
        bytes32[] calldata _proof
    )
        external
        onlyArbitrum
    {
        ARB_REWARDS.claim(
            master,
            _accrued,
            _proof
        );

        emit ClaimArb(
            _accrued,
            _proof,
            block.timestamp
        );
    }

    function withdrawLock()
        external
        onlyMaster
        returns (uint256 amount)
    {
        if (IS_ETH_MAIN == false) {

            amount = reservedPendleForLocking;
            reservedPendleForLocking = 0;

            _safeTransfer(
                PENDLE_TOKEN_ADDRESS,
                master,
                amount
            );

            emit WithdrawLock(
                amount,
                block.timestamp
            );

            return amount;
        }

        if (_getExpiry() > block.timestamp) {
            revert NotExpired();
        }

        syncAllSupply();

        amount = PENDLE_LOCK.withdraw();

        _safeTransfer(
            PENDLE_TOKEN_ADDRESS,
            master,
            amount
        );

        syncAllSupply();

        emit WithdrawLock(
            amount,
            block.timestamp
        );
    }

    function increaseReservedForCompound(
        address _pendleMarket,
        uint256[] calldata _amounts
    )
        external
        onlyChildContract(_pendleMarket)
    {
        CompoundStruct memory childInfo = pendleChildCompoundInfo[
            _pendleMarket
        ];

        uint256 i;
        uint256 length = childInfo.rewardTokens.length;

        while (i < length) {
            childInfo.reservedForCompound[i] += _amounts[i];
            unchecked {
                ++i;
            }
        }

        pendleChildCompoundInfo[_pendleMarket] = childInfo;

        emit IncreaseReservedForCompound(
            _pendleMarket,
            _amounts
        );
    }

    function overWriteIndex(
        address _pendleMarket,
        uint256 _tokenIndex
    )
        public
        onlyChildContract(_pendleMarket)
    {
        CompoundStruct storage childInfo = pendleChildCompoundInfo[
            _pendleMarket
        ];

        childInfo.lastIndex[_tokenIndex] = _getUserRewardIndex(
            _pendleMarket,
            childInfo.rewardTokens[_tokenIndex],
            address(this)
        );
    }

    function overWriteIndexAll(
        address _pendleMarket
    )
        external
        onlyChildContract(_pendleMarket)
    {
        uint256 i;
        uint256 length = pendleChildCompoundInfo[
            _pendleMarket
        ].rewardTokens.length;

        while (i < length) {
            overWriteIndex(
                _pendleMarket,
                i
            );
            unchecked {
                ++i;
            }
        }
    }

    function overWriteAmounts(
        address _pendleMarket
    )
        external
        onlyChildContract(_pendleMarket)
    {
        CompoundStruct storage childInfo = pendleChildCompoundInfo[
            _pendleMarket
        ];

        childInfo.reservedForCompound = new uint256[](
            childInfo.rewardTokens.length
        );
    }

    function claimVoteRewards(
        uint256 _amount,
        bytes32[] calldata _merkleProof
    )
        external
    {
        PENDLE_VOTE_REWARDS.claimRetail(
            address(this),
            _amount,
            _merkleProof
        );

        emit ClaimVoteRewards(
            _amount,
            _merkleProof,
            block.timestamp
        );
    }

    function forwardETH(
        address _to,
        uint256 _amount
    )
        external
        onlyMaster
    {
        payable(_to).transfer(
            _amount
        );
    }

    function vote(
        address[] calldata _pools,
        uint64[] calldata _weights
    )
        external
        onlyMaster
    {
        if (_weights.length != _pools.length) {
            revert InvalidLength();
        }

        uint256 i;
        uint256 len = _weights.length;

        uint256 weightSum;

        while (i < len) {
            unchecked {
                weightSum += _weights[i];
                ++i;
            }
        }

        if (weightSum > PRECISION_FACTOR_E18) {
            revert InvalidWeightSum();
        }

        PENDLE_VOTER.vote(
            _pools,
            _weights
        );
    }
}

// File: contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmControllerHelper.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "./PendlePowerFarmControllerBase.sol";

abstract contract PendlePowerFarmControllerHelper is PendlePowerFarmControllerBase {

    function _findIndex(
        address[] memory _array,
        address _value
    )
        internal
        pure
        returns (uint256)
    {
        uint256 i;
        uint256 len = _array.length;

        while (i < len) {
            if (_array[i] == _value) {
                return i;
            }
            unchecked {
                ++i;
            }
        }

        revert NotFound();
    }

    function _calcExpiry(
        uint128 _weeks
    )
        internal
        view
        returns (uint128)
    {
        uint128 startTime = uint128((block.timestamp / WEEK)
            * WEEK);

        return startTime + (_weeks * WEEK);
    }

    function _getExpiry()
        internal
        view
        returns (uint256)
    {
        return _getPositionData(address(this)).expiry;
    }

    function _getLockAmount()
        internal
        view
        returns (uint256)
    {
        return _getPositionData(address(this)).amount;
    }

    function _getPositionData(
        address _user
    )
        private
        view
        returns (LockedPosition memory)
    {
        return PENDLE_LOCK.positionData(
            _user
        );
    }

    function _getAmountToSend(
        address _tokenAddress,
        uint256 _rewardValue
    )
        internal
        view
        returns (uint256)
    {
        uint256 sendingValue = _rewardValue
            * (PRECISION_FACTOR_E6 - exchangeIncentive)
            / PRECISION_FACTOR_E6;

        if (sendingValue < PRECISION_FACTOR_E10) {
            revert ValueTooSmall();
        }

        return _getTokensFromETH(
            _tokenAddress,
            sendingValue
        );
    }

    function pendleChildCompoundInfoReservedForCompound(
        address _pendleMarket
    )
        external
        view
        returns (uint256[] memory)
    {
        return pendleChildCompoundInfo[_pendleMarket].reservedForCompound;
    }

    function pendleChildCompoundInfoLastIndex(
        address _pendleMarket
    )
        external
        view
        returns (uint128[] memory)
    {
        return pendleChildCompoundInfo[_pendleMarket].lastIndex;
    }

    function pendleChildCompoundInfoRewardTokens(
        address _pendleMarket
    )
        external
        view
        returns (address[] memory)
    {
        return pendleChildCompoundInfo[_pendleMarket].rewardTokens;
    }

    function activePendleMarketsLength()
        external
        view
        returns (uint256)
    {
        return activePendleMarkets.length;
    }

    function _checkFeed(
        address token
    )
        internal
        view
    {
        if (ORACLE_HUB.priceFeed(token) == ZERO_ADDRESS) {
            revert WrongAddress();
        }
    }

    function _getRewardTokens(
        address _pendleMarket
    )
        internal
        view
        returns (address[] memory)
    {
        return IPendleMarket(
            _pendleMarket
        ).getRewardTokens();
    }

    function _getUserReward(
        address _pendleMarket,
        address _rewardToken,
        address _user
    )
        internal
        view
        returns (UserReward memory)
    {
        return IPendleMarket(
            _pendleMarket
        ).userReward(
            _rewardToken,
            _user
        );
    }

    function _getUserRewardIndex(
        address _pendleMarket,
        address _rewardToken,
        address _user
    )
        internal
        view
        returns (uint128)
    {
        return _getUserReward(
            _pendleMarket,
            _rewardToken,
            _user
        ).index;
    }

    function _getTokensInETH(
        address _tokenAddress,
        uint256 _tokenAmount
    )
        internal
        view
        returns (uint256)
    {
        return ORACLE_HUB.getTokensInETH(
            _tokenAddress,
            _tokenAmount
        );
    }

    function _getTokensFromETH(
        address _tokenAddress,
        uint256 _ethValue
    )
        internal
        view
        returns (uint256)
    {
        return ORACLE_HUB.getTokensFromETH(
            _tokenAddress,
            _ethValue
        );
    }

    function _compareHashes(
        address _pendleMarket,
        address[] memory rewardTokensToCompare
    )
        internal
        view
        returns (bool)
    {
        return keccak256(
            abi.encode(
                rewardTokensToCompare
            )
        ) == keccak256(
            abi.encode(
                pendleChildCompoundInfo[_pendleMarket].rewardTokens
            )
        );
    }

    function _setRewardTokens(
        address _pendleMarket,
        address[] memory _rewardTokens
    )
        internal
    {
        pendleChildCompoundInfo[_pendleMarket].rewardTokens = _rewardTokens;
    }

    function getExpiry()
        external
        view
        returns (uint256)
    {
        return _getExpiry();
    }

    function getLockAmount()
        external
        view
        returns (uint256)
    {
        return _getLockAmount();
    }
}

// File: contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmControllerBase.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "../../OwnableMaster.sol";

import "../../TransferHub/TransferHelper.sol";
import "../../TransferHub/ApprovalHelper.sol";

import "../../InterfaceHub/IPendle.sol";
import "../../InterfaceHub/IArbRewardsClaimer.sol";
import "../../InterfaceHub/IWiseOracleHub.sol";
import "../../InterfaceHub/IPendlePowerFarmToken.sol";

error NotAllowed();
error AlreadySet();
error NotExpired();
error LockTimeTooShort();
error ZeroShares();
error ValueTooSmall();
error InvalidLength();
error InvalidWeightSum();
error WrongAddress();
error NothingToSkim();
error NotFound();
error NotEnoughCompound();
error NotArbitrum();

contract PendlePowerFarmControllerBase is
    OwnableMaster,
    TransferHelper,
    ApprovalHelper
{
    struct CompoundStruct {
        uint256[] reservedForCompound;
        uint128[] lastIndex;
        address[] rewardTokens;
    }

    address internal immutable PENDLE_TOKEN_ADDRESS;

    address internal immutable VOTER_CONTRACT_ADDRESS;
    address internal immutable VOTER_REWARDS_CLAIMER_ADDRESS;

    address internal immutable WISE_ORACLE_HUB_ADDRESS;
    address internal immutable VE_PENDLE_CONTRACT_ADDRESS;

    uint256 internal PRECISION_FACTOR_E6 = 1E6;
    uint256 internal PRECISION_FACTOR_E10 = 1E10;
    uint256 internal PRECISION_FACTOR_E18 = 1E18;

    uint256 internal MAINNET_CHAIN_ID = 1;
    uint256 internal ARBITRUM_CHAIN_ID = 42161;

    uint256 public reservedPendleForLocking;

    mapping(address => address) public pendleChildAddress;
    mapping(address => CompoundStruct) pendleChildCompoundInfo;

    address[] public activePendleMarkets;

    bool immutable IS_ETH_MAIN;

    uint128 internal constant WEEK = 7 days;
    uint256 public exchangeIncentive;

    IPendleLock immutable public PENDLE_LOCK;
    IPendleVoter immutable public PENDLE_VOTER;
    IPendleVoteRewards immutable public PENDLE_VOTE_REWARDS;

    IArbRewardsClaimer public ARB_REWARDS;

    address internal constant ARB_REWARDS_ADDRESS = 0x23a102e78D1FF1645a3666691495174764a5FCAF;
    address internal constant ARB_TOKEN_ADDRESS = 0x912CE59144191C1204E64559FE8253a0e49E6548;

    IWiseOracleHub immutable internal ORACLE_HUB;

    constructor(
        address _vePendle,
        address _pendleToken,
        address _voterContract,
        address _voterRewardsClaimerAddress,
        address _wiseOracleHub
    )
        OwnableMaster(
            msg.sender
        )
    {
        IS_ETH_MAIN = block.chainid == MAINNET_CHAIN_ID
            ? true
            : false;

        PENDLE_TOKEN_ADDRESS = _pendleToken;

        VOTER_CONTRACT_ADDRESS = _voterContract;
        VOTER_REWARDS_CLAIMER_ADDRESS = _voterRewardsClaimerAddress;

        WISE_ORACLE_HUB_ADDRESS = _wiseOracleHub;
        VE_PENDLE_CONTRACT_ADDRESS = _vePendle;

        PENDLE_LOCK = IPendleLock(
            _vePendle
        );

        PENDLE_VOTER = IPendleVoter(
            _voterContract
        );

        PENDLE_VOTE_REWARDS = IPendleVoteRewards(
            _voterRewardsClaimerAddress
        );

        ORACLE_HUB = IWiseOracleHub(
            _wiseOracleHub
        );

        ARB_REWARDS = IArbRewardsClaimer(
            ARB_REWARDS_ADDRESS
        );

        exchangeIncentive = 5000;
    }

    receive()
        external
        payable
    {
        emit ETHReceived(
            msg.value,
            msg.sender
        );
    }

    event ETHReceived(
        uint256 _amount,
        address _sender
    );

    event ChangeExchangeIncentive(
        uint256 _newExchangeIncentive
    );

    event WithdrawLp(
        address indexed _pendleMarket,
        address indexed _to,
        uint256 _amount
    );

    event ExchangeRewardsForCompounding(
        address indexed _pendleMarket,
        address indexed _rewardToken,
        uint256 _rewardAmount,
        uint256 _sendingAmount
    );

    event ExchangeLpFeesForPendle(
        address indexed _pendleMarket,
        uint256 _pendleChildShares,
        uint256 _tokenAmountSend,
        uint256 _withdrawnAmount
    );

    event AddPendleMarket(
        address indexed _pendleMarket,
        address indexed _pendleChildAddress
    );

    event UpdateRewardTokens(
        address indexed _pendleMarket,
        address[] _rewardTokens
    );

    event ChangeMintFee(
        address indexed _pendleMarket,
        uint256 _newFee
    );

    event LockPendle(
        uint256 _amount,
        uint128 _expiry,
        uint256 _newVeBalance,
        bool _fromInside,
        bool _sameExpiry,
        uint256 _timestamp
    );

    event ClaimArb(
        uint256 _accrued,
        bytes32[] _proof,
        uint256 _timestamp
    );

    event IncreaseReservedForCompound(
        address indexed _pendleMarket,
        uint256[] _amounts
    );

    event ClaimVoteRewards(
        uint256 _amount,
        bytes32[] _merkleProof,
        uint256 _timestamp
    );

    event WithdrawLock(
        uint256 _amount,
        uint256 _timestamp
    );

    modifier syncSupply(
        address _pendleMarket
    )
    {
        _syncSupply(
            _pendleMarket
        );
        _;
    }

    modifier onlyChildContract(
        address _pendleMarket
    )
    {
        _onlyChildContract(
            _pendleMarket
        );
        _;
    }

    modifier onlyArbitrum()
    {
        _onlyArbitrum();
        _;
    }

    function _onlyArbitrum()
        private
        view
    {
        if (block.chainid != ARBITRUM_CHAIN_ID) {
            revert NotArbitrum();
        }
    }

    function _onlyChildContract(
        address _pendleMarket
    )
        private
        view
    {
        if (msg.sender != pendleChildAddress[_pendleMarket]) {
            revert NotAllowed();
        }
    }

    function _syncSupply(
        address _pendleMarket
    )
        internal
    {
        address child = pendleChildAddress[
            _pendleMarket
        ];

        if (child == ZERO_ADDRESS) {
            revert WrongAddress();
        }

        IPendlePowerFarmToken(child).manualSync();
    }

    function syncAllSupply()
        public
    {
        uint256 i;
        uint256 length = activePendleMarkets.length;

        while (i < length) {
            _syncSupply(
                activePendleMarkets[i]
            );
            unchecked {
                ++i;
            }
        }
    }
}

// File: contracts/InterfaceHub/IPendlePowerFarmToken.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

interface IPendlePowerFarmToken {

    function changeMintFee(
        uint256 _newFee
    )
        external;

    function manualSync()
        external;

    function addCompoundRewards(
        uint256 _amount
    )
        external;

    function withdrawExactShares(
        uint256 _shares
    )
        external
        returns (uint256);

    function totalLpAssets()
        external
        view
        returns (uint256);

    function totalSupply()
        external
        view
        returns (uint256);

    function previewUnderlyingLpAssets()
        external
        view
        returns (uint256);

    function previewMintShares(
        uint256 _underlyingAssetAmount,
        uint256 _underlyingLpAssetsCurrent
    )
        external
        view
        returns (uint256);

    function previewAmountWithdrawShares(
        uint256 _shares,
        uint256 _underlyingLpAssetsCurrent
    )
        external
        view
        returns (uint256);

    function previewBurnShares(
        uint256 _underlyingAssetAmount,
        uint256 _underlyingLpAssetsCurrent
    )
        external
        view
        returns (uint256);

    function balanceOf(
        address _account
    )
        external
        view
        returns (uint256);

    function withdrawExactAmount(
        uint256 _underlyingLpAssetAmount
    )
        external
        returns (uint256);

    function depositExactAmount(
        uint256 _underlyingLpAssetAmount
    )
        external
        returns (
            uint256,
            uint256
        );

    function underlyingLpAssetsCurrent()
        external
        view
        returns (uint256);

    function totalLpAssetsToDistribute()
        external
        view
        returns (uint256);
}

// File: contracts/InterfaceHub/IWiseOracleHub.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

interface IWiseOracleHub {

    function getTokensPriceFromUSD(
        address _tokenAddress,
        uint256 _usdValue
    )
        external
        view
        returns (uint256);

    function getTokensPriceInUSD(
        address _tokenAddress,
        uint256 _tokenAmount
    )
        external
        view
        returns (uint256);

    function latestResolver(
        address _tokenAddress
    )
        external
        view
        returns (uint256);

    function getTokensFromUSD(
        address _tokenAddress,
        uint256 _usdValue
    )
        external
        view
        returns (uint256);

    function getTokensFromETH(
        address _tokenAddress,
        uint256 _ethValue
    )
        external
        view
        returns (uint256);

    function getTokensInUSD(
        address _tokenAddress,
        uint256 _amount
    )
        external
        view
        returns (uint256);

    function getTokensInETH(
        address _tokenAddress,
        uint256 _tokenAmount
    )
        external
        view
        returns (uint256);

    function chainLinkIsDead(
        address _tokenAddress
    )
        external
        view
        returns (bool);

    function decimalsUSD()
        external
        pure
        returns (uint8);

    function addOracle(
        address _tokenAddress,
        address _priceFeedAddress,
        address[] calldata _underlyingFeedTokens
    )
        external;

    function recalibrate(
        address _tokenAddress
    )
        external;

    function WETH_ADDRESS()
        external
        view
        returns (address);

    function priceFeed(
        address _tokenAddress
    )
        external
        view
        returns (address);
}

// File: contracts/InterfaceHub/IArbRewardsClaimer.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

interface IArbRewardsClaimer{
    function claim(
        address _receiver,
        uint256 _accrued,
        bytes32[] calldata _proof
    )
        external;
}

// File: contracts/InterfaceHub/IPendle.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

struct MarketStorage {
    int128 totalPt;
    int128 totalSy;
    uint96 lastLnImpliedRate;
    uint16 observationIndex;
    uint16 observationCardinality;
    uint16 observationCardinalityNext;
}

struct MarketState {
    int256 totalPt;
    int256 totalSy;
    int256 totalLp;
    address treasury;
    int256 scalarRoot;
    uint256 expiry;
    uint256 lnFeeRateRoot;
    uint256 reserveFeePercent;
    uint256 lastLnImpliedRate;
}

struct LockedPosition {
    uint128 amount;
    uint128 expiry;
}

struct UserReward {
    uint128 index;
    uint128 accrued;
}

struct ApproxParams {
    uint256 guessMin;
    uint256 guessMax;
    uint256 guessOffchain;
    uint256 maxIteration;
    uint256 eps;
}

interface IPendleSy {

    function previewDeposit(
        address _tokenIn,
        uint256 _amountTokenToDeposit
    )
        external
        view
        returns (uint256 sharesAmount);

    function deposit(
        address _receiver,
        address _tokenIn,
        uint256 _amountTokenToDeposit,
        uint256 _minSharesOut
    )
        external
        returns (uint256 sharesAmount);

    function exchangeRate()
        external
        view
        returns (uint256);

    function redeem(
        address _receiver,
        uint256 _amountSharesToRedeem,
        address _tokenOut,
        uint256 _minTokenOut,
        bool _burnFromInternalBalance
    )
        external
        returns (uint256 amountTokenOut);
}

interface IPendleYt {

    function mintPY(
        address _receiverPT,
        address _receiverYT
    )
        external
        returns (uint256 pyAmount);

    function redeemPY(
        address _receiver
    )
        external
        returns (uint256);

    function redeemDueInterestAndRewards(
        address _user,
        bool _redeemInterest,
        bool _redeemRewards
    )
        external
        returns (
            uint256 interestOut,
            uint256[] memory rewardsOut
        );

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

    function userReward(
        address _token,
        address _user
    )
        external
        view
        returns (UserReward memory);

    function userInterest(
        address user
    )
        external
        view
        returns (
            uint128 lastPYIndex,
            uint128 accruedInterest
        );

    function pyIndexStored()
        external
        view
        returns (uint256);
}

interface IPendleMarket {

    function readTokens()
        external
        view
        returns (
            address SY,
            address PT,
            address YT
        );

    function activeBalance(
        address _user
    )
        external
        view
        returns (uint256);

    function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    )
        external;

    function balanceOf(
        address _user
    )
        external
        view
        returns (uint256);

    function isExpired()
        external
        view
        returns (bool);

    function decimals()
        external
        view
        returns (uint8);

    function increaseObservationsCardinalityNext(
        uint16 _newObservationCardinalityNext
    )
        external;

    function swapExactPtForSy(
        address receiver,
        uint256 exactPtIn,
        bytes calldata data
    )
        external
        returns (
            uint256 netSyOut,
            uint256 netSyFee
        );

    function _storage()
        external
        view
        returns (MarketStorage memory);

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

    function readState(
        address _router
    )
        external
        view
        returns (MarketState memory marketState);

    function mint(
        address _receiver,
        uint256 _netSyDesired,
        uint256 _netPtDesired
    )
        external
        returns (uint256[3] memory);

    function burn(
        address _receiverAddressSy,
        address _receiverAddressPt,
        uint256 _lpToBurn
    )
        external
        returns (
            uint256 syOut,
            uint256 ptOut
        );

    function redeemRewards(
        address _user
    )
        external
        returns (uint256[] memory);

    function totalSupply()
        external
        view
        returns (uint256);

    function userReward(
        address _token,
        address _user
    )
        external
        view
        returns (UserReward memory);
}

interface IPendleChildToken {

    function totalLpAssets()
        external
        view
        returns (uint256);

    function totalSupply()
        external
        view
        returns (uint256);

    function previewUnderlyingLpAssets()
        external
        view
        returns (uint256);

    function previewMintShares(
        uint256 _underlyingAssetAmount,
        uint256 _underlyingLpAssetsCurrent
    )
        external
        view
        returns (uint256);

    function previewAmountWithdrawShares(
        uint256 _shares,
        uint256 _underlyingLpAssetsCurrent
    )
        external
        view
        returns (uint256);

    function previewBurnShares(
        uint256 _underlyingAssetAmount,
        uint256 _underlyingLpAssetsCurrent
    )
        external
        view
        returns (uint256);
}

interface IPendleLock {

    function increaseLockPosition(
        uint128 _additionalAmountToLock,
        uint128 _newExpiry
    )
        external
        returns (uint128 newVeBalance);

    function withdraw()
        external
        returns (uint128);

    function positionData(
        address _user
    )
        external
        view
        returns (LockedPosition memory);

    function getBroadcastPositionFee(
        uint256[] calldata _chainIds
    )
        external
        view
        returns (uint256);
}

interface IPendleVoteRewards {
    function claimRetail(
        address _user,
        uint256 _amount,
        bytes32[] calldata _merkleProof
    )
        external
        returns (uint256);
}

interface IPendleVoter {
    function vote(
        address[] memory _pools,
        uint64[] memory _weights
    )
        external;
}

interface IPendleChild {
    function depositExactAmount(
        uint256 _amount
    )
        external
        returns (
            uint256,
            uint256
        );

    function withdrawExactShares(
        uint256 _shares
    )
        external
        returns (uint256);
}

interface IPendleRouter {

    function swapSyForExactYt(
        address _receiver,
        address _market,
        uint256 _exactYtOut,
        uint256 _maxSyIn
    )
        external
        returns (
            uint256 netSyIn,
            uint256 netSyFee
        );

    function swapExactSyForYt(
        address _receiver,
        address _market,
        uint256 _exactSyIn,
        uint256 _minYtOut
    )
        external
        returns (
            uint256 netYtOut,
            uint256 netSyFee
        );

    function swapSyForExactPt(
        address _receiver,
        address _market,
        uint256 _exactPtOut,
        uint256 _maxSyIn
    )
        external
        returns (
            uint256 netSyIn,
            uint256 netSyFee
        );

    function swapExactSyForPt(
        address _receiver,
        address _market,
        uint256 _exactSyIn,
        uint256 _minPtOut
    )
        external
        returns (
            uint256 netPtOut,
            uint256 netSyFee
        );

    function removeLiquiditySingleSy(
        address _receiver,
        address _market,
        uint256 _netLpToRemove,
        uint256 _minSyOut
    )
        external
        returns (
            uint256 netSyOut,
            uint256 netSyFee
        );

    function addLiquiditySingleSy(
        address _receiver,
        address _market,
        uint256 _netSyIn,
        uint256 _minLpOut,
        ApproxParams calldata _guessPtReceivedFromSy
    )
        external
        returns (
            uint256 netLpOut,
            uint256 netSyFee
        );
}

interface IPendleRouterStatic {

    function addLiquiditySingleSyStatic(
        address _market,
        uint256 _netSyIn
    )
        external
        view
        returns (
            uint256 netLpOut,
            uint256 netPtFromSwap,
            uint256 netSyFee,
            uint256 priceImpact,
            uint256 exchangeRateAfter,
            uint256 netSyToSwap
        );

    function swapExactPtForSyStatic(
        address _market,
        uint256 _exactPtIn
    )
        external
        view
        returns (
            uint256 netSyOut,
            uint256 netSyFee,
            uint256 priceImpact,
            uint256 exchangeRateAfter
        );
}

// File: contracts/TransferHub/ApprovalHelper.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "./CallOptionalReturn.sol";

contract ApprovalHelper is CallOptionalReturn {

    /**
     * @dev
     * Allows to execute safe approve for a token
     */
    function _safeApprove(
        address _token,
        address _spender,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                IERC20.approve.selector,
                _spender,
                _value
            )
        );
    }
}
// File: contracts/TransferHub/TransferHelper.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "./CallOptionalReturn.sol";

contract TransferHelper is CallOptionalReturn {

    /**
     * @dev
     * Allows to execute safe transfer for a token
     */
    function _safeTransfer(
        address _token,
        address _to,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                IERC20.transfer.selector,
                _to,
                _value
            )
        );
    }

    /**
     * @dev
     * Allows to execute safe transferFrom for a token
     */
    function _safeTransferFrom(
        address _token,
        address _from,
        address _to,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                IERC20.transferFrom.selector,
                _from,
                _to,
                _value
            )
        );
    }
}

// File: contracts/OwnableMaster.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

error NoValue();
error NotMaster();
error NotProposed();

contract OwnableMaster {

    address public master;
    address private proposedMaster;

    address internal constant ZERO_ADDRESS = address(0x0);

    modifier onlyProposed() {
        _onlyProposed();
        _;
    }

    function _onlyMaster()
        private
        view
    {
        if (msg.sender == master) {
            return;
        }

        revert NotMaster();
    }

    modifier onlyMaster() {
        _onlyMaster();
        _;
    }

    function _onlyProposed()
        private
        view
    {
        if (msg.sender == proposedMaster) {
            return;
        }

        revert NotProposed();
    }

    event MasterProposed(
        address indexed proposer,
        address indexed proposedMaster
    );

    event ClaimedOwnership(
        address indexed newMaster,
        address indexed previousMaster
    );

    event RenouncedOwnership(
        address indexed previousMaster
    );

    constructor(
        address _master
    ) {
        if (_master == ZERO_ADDRESS) {
            revert NoValue();
        }
        master = _master;
    }

    /**
     * @dev Allows to propose next master.
     * Must be claimed by proposer.
     */
    function proposeOwner(
        address _proposedOwner
    )
        external
        onlyMaster
    {
        if (_proposedOwner == ZERO_ADDRESS) {
            revert NoValue();
        }

        proposedMaster = _proposedOwner;

        emit MasterProposed(
            msg.sender,
            _proposedOwner
        );
    }

    /**
     * @dev Allows to claim master role.
     * Must be called by proposer.
     */
    function claimOwnership()
        external
        onlyProposed
    {
        master = proposedMaster;

        emit ClaimedOwnership(
            master,
            msg.sender
        );
    }

    /**
     * @dev Removes master role.
     * No ability to be in control.
     */
    function renounceOwnership()
        external
        onlyMaster
    {
        master = ZERO_ADDRESS;
        proposedMaster = ZERO_ADDRESS;

        emit RenouncedOwnership(
            msg.sender
        );
    }
}

// File: contracts/TransferHub/CallOptionalReturn.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "../InterfaceHub/IERC20.sol";

contract CallOptionalReturn {

    /**
     * @dev Helper function to do low-level call
     */
    function _callOptionalReturn(
        address token,
        bytes memory data
    )
        internal
        returns (bool call)
    {
        (
            bool success,
            bytes memory returndata
        ) = token.call(
            data
        );

        bool results = returndata.length == 0 || abi.decode(
            returndata,
            (bool)
        );

        if (success == false) {
            revert();
        }

        call = success
            && results
            && token.code.length > 0;
    }
}
// File: contracts/InterfaceHub/IERC20.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

interface IERC20 {

    function totalSupply()
        external
        view
        returns (uint256);

    function balanceOf(
        address _account
    )
        external
        view
        returns (uint256);

    function transferFrom(
        address _sender,
        address _recipient,
        uint256 _amount
    )
        external
        returns (bool);

    function transfer(
        address _recipient,
        uint256 _amount
    )
        external
        returns (bool);

    function allowance(
        address owner,
        address spender
    )
        external
        view
        returns (uint256);

    function approve(
        address _spender,
        uint256 _amount
    )
        external
        returns (bool);

    function decimals()
        external
        view
        returns (uint8);

    event Transfer(
        address indexed from,
        address indexed to,
        uint256 value
    );

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    event  Deposit(
        address indexed dst,
        uint wad
    );

    event  Withdrawal(
        address indexed src,
        uint wad
    );
}