ETH Price: $2,686.21 (-2.49%)

Contract Diff Checker

Contract Name:
CurveRegistryCache

Contract Source Code:

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.17;

import "contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "contracts/access/Ownable.sol";

import "libraries/CurvePoolUtils.sol";
import "libraries/ArrayExtensions.sol";

import "interfaces/ICurveRegistryCache.sol";
import "interfaces/vendor/ICurveMetaRegistry.sol";
import "interfaces/vendor/ICurvePoolV0.sol";
import "interfaces/vendor/ICurvePoolV1.sol";

contract CurveRegistryCache is ICurveRegistryCache, Ownable {
    using ArrayExtensions for address[];

    address internal constant _ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    address internal constant _WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    ICurveMetaRegistry public constant CURVE_REGISTRY =
        ICurveMetaRegistry(0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC);

    IBooster public constant BOOSTER = IBooster(0xF403C135812408BFbE8713b5A23a04b3D48AAE31);

    modifier onlyInitialized(address pool) {
        require(_isRegistered[pool], "CurveRegistryCache: pool not initialized");
        _;
    }

    mapping(address => bool) internal _isRegistered;
    mapping(address => address) internal _lpToken;
    mapping(address => mapping(address => bool)) internal _hasCoinDirectly;
    mapping(address => mapping(address => bool)) internal _hasCoinAnywhere;
    mapping(address => address) internal _basePool;
    mapping(address => mapping(address => int128)) internal _coinIndex;
    mapping(address => uint256) internal _nCoins;
    mapping(address => address[]) internal _coins;
    mapping(address => uint256[]) internal _decimals;
    mapping(address => address) internal _poolFromLpToken;
    mapping(address => CurvePoolUtils.AssetType) internal _assetType;
    mapping(address => uint256) internal _interfaceVersion;

    /// Information needed for staking Curve LP tokens on Convex
    mapping(address => uint256) internal _convexPid;
    mapping(address => address) internal _convexRewardPool; // curve pool => CRV rewards pool (convex)

    function initPool(address pool_) external override {
        _initPool(pool_, false, 0);
    }

    function initPool(address pool_, uint256 pid_) external override {
        _initPool(pool_, true, pid_);
    }

    function initPool(address pool_, Types.PoolInfo memory poolInfo_) external override onlyOwner {
        _initUnregisteredPool(pool_, false, 0, poolInfo_);
    }

    function initPool(
        address pool_,
        uint256 pid_,
        Types.PoolInfo memory poolInfo_
    ) external override onlyOwner {
        _initUnregisteredPool(pool_, true, pid_, poolInfo_);
    }

    function _initUnregisteredPool(
        address pool_,
        bool setPid_,
        uint256 pid_,
        Types.PoolInfo memory poolInfo_
    ) internal {
        if (_isRegistered[pool_]) return;
        require(!_isCurvePool(pool_), "CurveRegistryCache: curve pool supported by metaregistry");
        _initPool(pool_, setPid_, pid_, poolInfo_);
    }

    function _initPool(address pool_, bool setPid_, uint256 pid_) internal {
        if (_isRegistered[pool_]) return;
        require(_isCurvePool(pool_), "CurveRegistryCache: invalid curve pool");
        Types.PoolInfo memory poolInfo_ = Types.PoolInfo({
            lpToken: CURVE_REGISTRY.get_lp_token(pool_),
            basePool: CURVE_REGISTRY.get_base_pool(pool_),
            assetType: CURVE_REGISTRY.get_pool_asset_type(pool_)
        });
        _initPool(pool_, setPid_, pid_, poolInfo_);
    }

    function _initPool(
        address pool_,
        bool setPid_,
        uint256 pid_,
        Types.PoolInfo memory poolInfo_
    ) internal {
        address curveLpToken_ = poolInfo_.lpToken;
        address basePool_ = poolInfo_.basePool;

        _isRegistered[pool_] = true;
        _lpToken[pool_] = curveLpToken_;
        if (setPid_) {
            _setConvexPid(pool_, curveLpToken_, pid_);
        } else {
            pid_ = _setConvexPid(pool_, curveLpToken_);
        }
        _poolFromLpToken[curveLpToken_] = pool_;
        _basePool[pool_] = basePool_;
        if (basePool_ != address(0)) {
            _initPool(basePool_, false, 0);
            address[] memory basePoolCoins_ = _coins[basePool_];
            for (uint256 i; i < basePoolCoins_.length; i++) {
                address coin_ = basePoolCoins_[i];
                _hasCoinAnywhere[pool_][coin_] = true;
            }
        }
        _assetType[pool_] = CurvePoolUtils.AssetType(poolInfo_.assetType);
        uint256 interfaceVersion_ = _getInterfaceVersion(pool_);
        address[] memory coins_ = _getCoins(pool_, interfaceVersion_);

        for (uint256 i; i < coins_.length; i++) {
            address coin_ = coins_[i];
            require(coin_ != address(0), "CurveRegistryCache: invalid coin");
            _hasCoinDirectly[pool_][coin_] = true;
            _hasCoinAnywhere[pool_][coin_] = true;
            _coinIndex[pool_][coin_] = int128(uint128(i));

            if (coin_ == _ETH) {
                coin_ = _WETH;
                coins_[i] = coin_;
                _hasCoinDirectly[pool_][coin_] = true;
                _hasCoinAnywhere[pool_][coin_] = true;
                _coinIndex[pool_][coin_] = int128(uint128(i));
            }
            _decimals[pool_].push(IERC20Metadata(coin_).decimals());
        }
        _nCoins[pool_] = coins_.length;
        _coins[pool_] = coins_;
        _interfaceVersion[pool_] = interfaceVersion_;

        emit PoolInitialized(pool_, pid_);
    }

    function _setConvexPid(address pool_, address lpToken_) internal returns (uint256 pid_) {
        uint256 length = BOOSTER.poolLength();
        address rewardPool;
        for (uint256 i; i < length; i++) {
            (address curveToken, , , address rewardPool_, , bool _isShutdown) = BOOSTER.poolInfo(i);
            if (lpToken_ != curveToken || _isShutdown) continue;
            rewardPool = rewardPool_;
            pid_ = i;
            _convexPid[pool_] = i;
            break;
        }
        /// Only Curve pools that have a valid Convex PID can be added to the cache
        require(rewardPool != address(0), "no convex pid found");
        _convexRewardPool[pool_] = rewardPool;
    }

    function _setConvexPid(address pool_, address lpToken_, uint256 pid_) internal {
        (address curveToken, , , address rewardPool_, , bool _isShutdown) = BOOSTER.poolInfo(pid_);
        require(lpToken_ == curveToken, "invalid lp token for curve pool");
        require(!_isShutdown, "convex pool is shut down");
        require(rewardPool_ != address(0), "no convex pid found");
        _convexRewardPool[pool_] = rewardPool_;
        _convexPid[pool_] = pid_;
    }

    function isRegistered(address pool_) external view override returns (bool) {
        return _isRegistered[pool_];
    }

    function lpToken(
        address pool_
    ) external view override onlyInitialized(pool_) returns (address) {
        return _lpToken[pool_];
    }

    function assetType(
        address pool_
    ) external view override onlyInitialized(pool_) returns (CurvePoolUtils.AssetType) {
        return _assetType[pool_];
    }

    function hasCoinDirectly(
        address pool_,
        address coin_
    ) external view override onlyInitialized(pool_) returns (bool) {
        return _hasCoinDirectly[pool_][coin_];
    }

    function hasCoinAnywhere(
        address pool_,
        address coin_
    ) external view override onlyInitialized(pool_) returns (bool) {
        return _hasCoinAnywhere[pool_][coin_];
    }

    function basePool(
        address pool_
    ) external view override onlyInitialized(pool_) returns (address) {
        return _basePool[pool_];
    }

    function coinIndex(
        address pool_,
        address coin_
    ) external view override onlyInitialized(pool_) returns (int128) {
        return _coinIndex[pool_][coin_];
    }

    function nCoins(address pool_) external view override onlyInitialized(pool_) returns (uint256) {
        return _nCoins[pool_];
    }

    function getAllUnderlyingCoins(address pool) public view returns (address[] memory) {
        address base = _basePool[pool];
        if (base == address(0)) return _coins[pool];

        address[] memory result = new address[](_nCoins[pool] - 1);
        for ((uint256 i, uint256 j) = (0, 0); i < _nCoins[pool]; i++) {
            address coin = _coins[pool][i];
            if (_poolFromLpToken[coin] != base) {
                result[j++] = coin;
            }
        }
        return result.concat(getAllUnderlyingCoins(base));
    }

    function coinIndices(
        address pool_,
        address from_,
        address to_
    ) external view override onlyInitialized(pool_) returns (int128, int128, bool) {
        return (
            _coinIndex[pool_][from_],
            _coinIndex[pool_][to_],
            _hasCoinDirectly[pool_][from_] && _hasCoinDirectly[pool_][to_]
        );
    }

    function decimals(
        address pool_
    ) external view override onlyInitialized(pool_) returns (uint256[] memory) {
        return _decimals[pool_];
    }

    /// @notice Returns the Curve interface version for a given pool
    /// @dev Version 0 uses `int128` for `coins` and `balances`, and `int128` for `get_dy`
    /// Version 1 uses `uint256` for `coins` and `balances`, and `int128` for `get_dy`
    /// Version 2 uses `uint256` for `coins` and `balances`, and `uint256` for `get_dy`
    /// They correspond with which interface the pool implements: ICurvePoolV0, ICurvePoolV1, ICurvePoolV2
    function interfaceVersion(
        address pool_
    ) external view override onlyInitialized(pool_) returns (uint256) {
        return _interfaceVersion[pool_];
    }

    function poolFromLpToken(address lpToken_) external view override returns (address) {
        return _poolFromLpToken[lpToken_];
    }

    function coins(
        address pool_
    ) external view override onlyInitialized(pool_) returns (address[] memory) {
        return _coins[pool_];
    }

    function getPid(address pool_) external view override onlyInitialized(pool_) returns (uint256) {
        require(_convexRewardPool[pool_] != address(0), "pid not found");
        return _convexPid[pool_];
    }

    function getRewardPool(
        address pool_
    ) external view override onlyInitialized(pool_) returns (address) {
        return _convexRewardPool[pool_];
    }

    function isShutdownPid(uint256 pid_) external view override returns (bool) {
        (, , , , , bool _isShutdown) = BOOSTER.poolInfo(pid_);
        return _isShutdown;
    }

    function _isCurvePool(address pool_) internal view returns (bool) {
        try CURVE_REGISTRY.is_registered(pool_) returns (bool registered_) {
            return registered_;
        } catch {
            return false;
        }
    }

    function _getCoins(
        address pool_,
        uint256 interfaceVersion_
    ) internal view returns (address[] memory) {
        uint256 count_;
        address[] memory coins_ = new address[](8);
        for (uint256 i; i < 8; i++) {
            if (interfaceVersion_ == 0) {
                try ICurvePoolV0(pool_).coins(int128(int256(i))) returns (address coin) {
                    coins_[i] = coin;
                    count_++;
                } catch {
                    break;
                }
            } else {
                try ICurvePoolV1(pool_).coins(i) returns (address coin) {
                    coins_[i] = coin;
                    count_++;
                } catch {
                    break;
                }
            }
        }
        return coins_.trim(count_);
    }

    function _getInterfaceVersion(address pool_) internal view returns (uint256) {
        if (_assetType[pool_] == CurvePoolUtils.AssetType.CRYPTO) return 2;
        try ICurvePoolV1(pool_).balances(uint256(0)) returns (uint256) {
            return 1;
        } catch {
            return 0;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "contracts/token/ERC20/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) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "contracts/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
// 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: GPL-3.0-or-later
pragma solidity 0.8.17;

import "interfaces/vendor/ICurvePoolV2.sol";
import "interfaces/vendor/ICurvePoolV1.sol";
import "libraries/ScaledMath.sol";

library CurvePoolUtils {
    using ScaledMath for uint256;

    error NotWithinThreshold(address pool, uint256 assetA, uint256 assetB);

    /// @dev by default, allow for 30 bps deviation regardless of pool fees
    uint256 internal constant _DEFAULT_IMBALANCE_BUFFER = 30e14;

    /// @dev Curve scales the `fee` by 1e10
    uint8 internal constant _CURVE_POOL_FEE_DECIMALS = 10;

    /// @dev allow imbalance to be buffer + 3x the fee, e.g. if fee is 3.6 bps and buffer is 30 bps, allow 40.8 bps
    uint256 internal constant _FEE_IMBALANCE_MULTIPLIER = 3;

    enum AssetType {
        USD,
        ETH,
        BTC,
        OTHER,
        CRYPTO
    }

    struct PoolMeta {
        address pool;
        uint256 numberOfCoins;
        AssetType assetType;
        uint256[] decimals;
        uint256[] prices;
        uint256[] imbalanceBuffers;
    }

    function ensurePoolBalanced(PoolMeta memory poolMeta) internal view {
        uint256 poolFee = ICurvePoolV1(poolMeta.pool).fee().convertScale(
            _CURVE_POOL_FEE_DECIMALS,
            18
        );

        for (uint256 i = 0; i < poolMeta.numberOfCoins - 1; i++) {
            uint256 fromDecimals = poolMeta.decimals[i];
            uint256 fromBalance = 10 ** fromDecimals;
            uint256 fromPrice = poolMeta.prices[i];

            for (uint256 j = i + 1; j < poolMeta.numberOfCoins; j++) {
                uint256 toDecimals = poolMeta.decimals[j];
                uint256 toPrice = poolMeta.prices[j];
                uint256 toExpectedUnscaled = (fromBalance * fromPrice) / toPrice;
                uint256 toExpected = toExpectedUnscaled.convertScale(
                    uint8(fromDecimals),
                    uint8(toDecimals)
                );

                uint256 toActual;

                if (poolMeta.assetType == AssetType.CRYPTO) {
                    // Handling crypto pools
                    toActual = ICurvePoolV2(poolMeta.pool).get_dy(i, j, fromBalance);
                } else {
                    // Handling other pools
                    toActual = ICurvePoolV1(poolMeta.pool).get_dy(
                        int128(uint128(i)),
                        int128(uint128(j)),
                        fromBalance
                    );
                }
                uint256 _maxImbalanceBuffer = poolMeta.imbalanceBuffers[i].max(
                    poolMeta.imbalanceBuffers[j]
                );

                if (!_isWithinThreshold(toExpected, toActual, poolFee, _maxImbalanceBuffer))
                    revert NotWithinThreshold(poolMeta.pool, i, j);
            }
        }
    }

    function _isWithinThreshold(
        uint256 a,
        uint256 b,
        uint256 poolFee,
        uint256 imbalanceBuffer
    ) internal pure returns (bool) {
        if (imbalanceBuffer == 0) imbalanceBuffer = _DEFAULT_IMBALANCE_BUFFER;
        uint256 imbalanceTreshold = imbalanceBuffer + poolFee * _FEE_IMBALANCE_MULTIPLIER;
        if (a > b) return (a - b).divDown(a) <= imbalanceTreshold;
        return (b - a).divDown(b) <= imbalanceTreshold;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface ICurvePoolV2 {
    function token() external view returns (address);

    function coins(uint256 i) external view returns (address);

    function factory() external view returns (address);

    function exchange(
        uint256 i,
        uint256 j,
        uint256 dx,
        uint256 min_dy,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    function exchange_underlying(
        uint256 i,
        uint256 j,
        uint256 dx,
        uint256 min_dy,
        address receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[2] memory amounts,
        uint256 min_mint_amount,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[2] memory amounts,
        uint256 min_mint_amount
    ) external returns (uint256);

    function add_liquidity(
        uint256[3] memory amounts,
        uint256 min_mint_amount,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[3] memory amounts,
        uint256 min_mint_amount
    ) external returns (uint256);

    function remove_liquidity(
        uint256 _amount,
        uint256[2] memory min_amounts,
        bool use_eth,
        address receiver
    ) external;

    function remove_liquidity(uint256 _amount, uint256[2] memory min_amounts) external;

    function remove_liquidity(
        uint256 _amount,
        uint256[3] memory min_amounts,
        bool use_eth,
        address receiver
    ) external;

    function remove_liquidity(uint256 _amount, uint256[3] memory min_amounts) external;

    function remove_liquidity_one_coin(
        uint256 token_amount,
        uint256 i,
        uint256 min_amount,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);

    function calc_token_amount(uint256[] memory amounts) external view returns (uint256);

    function calc_withdraw_one_coin(
        uint256 token_amount,
        uint256 i
    ) external view returns (uint256);

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

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface ICurvePoolV1 {
    function get_virtual_price() external view returns (uint256);

    function add_liquidity(uint256[8] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[7] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[6] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[5] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[3] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) external;

    function remove_liquidity_imbalance(
        uint256[4] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[3] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[2] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function lp_token() external view returns (address);

    function A_PRECISION() external view returns (uint256);

    function A_precise() external view returns (uint256);

    function remove_liquidity(uint256 _amount, uint256[3] calldata min_amounts) external;

    function exchange(
        int128 from,
        int128 to,
        uint256 _from_amount,
        uint256 _min_to_amount
    ) external;

    function coins(uint256 i) external view returns (address);

    function balances(uint256 i) external view returns (uint256);

    function get_dy(int128 i, int128 j, uint256 _dx) external view returns (uint256);

    function calc_token_amount(
        uint256[4] calldata amounts,
        bool deposit
    ) external view returns (uint256);

    function calc_token_amount(
        uint256[3] calldata amounts,
        bool deposit
    ) external view returns (uint256);

    function calc_token_amount(
        uint256[2] calldata amounts,
        bool deposit
    ) external view returns (uint256);

    function calc_withdraw_one_coin(
        uint256 _token_amount,
        int128 i
    ) external view returns (uint256);

    function remove_liquidity_one_coin(
        uint256 _token_amount,
        int128 i,
        uint256 min_amount
    ) external;

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

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.17;

library ScaledMath {
    uint256 internal constant DECIMALS = 18;
    uint256 internal constant ONE = 10 ** DECIMALS;

    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * b) / ONE;
    }

    function mulDown(uint256 a, uint256 b, uint256 decimals) internal pure returns (uint256) {
        return (a * b) / (10 ** decimals);
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * ONE) / b;
    }

    function divDown(uint256 a, uint256 b, uint256 decimals) internal pure returns (uint256) {
        return (a * 10 ** decimals) / b;
    }

    function divUp(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        return ((a * ONE) - 1) / b + 1;
    }

    function mulDown(int256 a, int256 b) internal pure returns (int256) {
        return (a * b) / int256(ONE);
    }

    function mulDownUint128(uint128 a, uint128 b) internal pure returns (uint128) {
        return (a * b) / uint128(ONE);
    }

    function mulDown(int256 a, int256 b, uint256 decimals) internal pure returns (int256) {
        return (a * b) / int256(10 ** decimals);
    }

    function divDown(int256 a, int256 b) internal pure returns (int256) {
        return (a * int256(ONE)) / b;
    }

    function divDownUint128(uint128 a, uint128 b) internal pure returns (uint128) {
        return (a * uint128(ONE)) / b;
    }

    function divDown(int256 a, int256 b, uint256 decimals) internal pure returns (int256) {
        return (a * int256(10 ** decimals)) / b;
    }

    function convertScale(
        uint256 a,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (uint256) {
        if (fromDecimals == toDecimals) return a;
        if (fromDecimals > toDecimals) return downscale(a, fromDecimals, toDecimals);
        return upscale(a, fromDecimals, toDecimals);
    }

    function convertScale(
        int256 a,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (int256) {
        if (fromDecimals == toDecimals) return a;
        if (fromDecimals > toDecimals) return downscale(a, fromDecimals, toDecimals);
        return upscale(a, fromDecimals, toDecimals);
    }

    function upscale(
        uint256 a,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (uint256) {
        return a * (10 ** (toDecimals - fromDecimals));
    }

    function downscale(
        uint256 a,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (uint256) {
        return a / (10 ** (fromDecimals - toDecimals));
    }

    function upscale(
        int256 a,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (int256) {
        return a * int256(10 ** (toDecimals - fromDecimals));
    }

    function downscale(
        int256 a,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (int256) {
        return a / int256(10 ** (fromDecimals - toDecimals));
    }

    function intPow(uint256 a, uint256 n) internal pure returns (uint256) {
        uint256 result = ONE;
        for (uint256 i; i < n; ) {
            result = mulDown(result, a);
            unchecked {
                ++i;
            }
        }
        return result;
    }

    function absSub(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return a >= b ? a - b : b - a;
        }
    }

    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a <= b ? a : b;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.17;

library ArrayExtensions {
    function copy(uint256[] memory array) internal pure returns (uint256[] memory) {
        uint256[] memory copy_ = new uint256[](array.length);
        for (uint256 i = 0; i < array.length; i++) {
            copy_[i] = array[i];
        }
        return copy_;
    }

    function concat(
        address[] memory a,
        address[] memory b
    ) internal pure returns (address[] memory result) {
        result = new address[](a.length + b.length);
        for (uint256 i; i < a.length; i++) result[i] = a[i];
        for (uint256 i; i < b.length; i++) result[i + a.length] = b[i];
    }

    function includes(address[] memory array, address element) internal pure returns (bool) {
        return _includes(array, element, array.length);
    }

    function _includes(
        address[] memory array,
        address element,
        uint256 until
    ) internal pure returns (bool) {
        for (uint256 i; i < until; i++) {
            if (array[i] == element) return true;
        }
        return false;
    }

    function removeDuplicates(address[] memory array) internal pure returns (address[] memory) {
        address[] memory unique = new address[](array.length);
        uint256 j;
        for (uint256 i; i < array.length; i++) {
            if (!_includes(unique, array[i], j)) {
                unique[j++] = array[i];
            }
        }
        return trim(unique, j);
    }

    function trim(
        address[] memory array,
        uint256 length
    ) internal pure returns (address[] memory trimmed) {
        trimmed = new address[](length);
        for (uint256 i; i < length; i++) trimmed[i] = array[i];
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.17;

import "interfaces/vendor/IBooster.sol";
import "libraries/CurvePoolUtils.sol";
import "libraries/Types.sol";

interface ICurveRegistryCache {
    event PoolInitialized(address indexed pool, uint256 indexed pid);

    function BOOSTER() external view returns (IBooster);

    function initPool(address pool_) external;

    function initPool(address pool_, uint256 pid_) external;

    function initPool(address pool_, Types.PoolInfo memory poolInfo_) external;

    function initPool(address pool_, uint256 pid_, Types.PoolInfo memory poolInfo_) external;

    function lpToken(address pool_) external view returns (address);

    function assetType(address pool_) external view returns (CurvePoolUtils.AssetType);

    function isRegistered(address pool_) external view returns (bool);

    function hasCoinDirectly(address pool_, address coin_) external view returns (bool);

    function hasCoinAnywhere(address pool_, address coin_) external view returns (bool);

    function basePool(address pool_) external view returns (address);

    function coinIndex(address pool_, address coin_) external view returns (int128);

    function nCoins(address pool_) external view returns (uint256);

    function coinIndices(
        address pool_,
        address from_,
        address to_
    ) external view returns (int128, int128, bool);

    function decimals(address pool_) external view returns (uint256[] memory);

    function interfaceVersion(address pool_) external view returns (uint256);

    function poolFromLpToken(address lpToken_) external view returns (address);

    function coins(address pool_) external view returns (address[] memory);

    function getPid(address _pool) external view returns (uint256);

    function getRewardPool(address _pool) external view returns (address);

    function isShutdownPid(uint256 pid_) external view returns (bool);

    /// @notice this returns the underlying coins of a pool, including the underlying of the base pool
    /// if the given pool is a meta pool
    /// This does not return the LP token of the base pool as an underlying
    /// e.g. if the pool is 3CrvFrax, this will return FRAX, DAI, USDC, USDT
    function getAllUnderlyingCoins(address pool) external view returns (address[] memory);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface IBooster {
    function poolInfo(
        uint256 pid
    )
        external
        view
        returns (
            address lpToken,
            address token,
            address gauge,
            address crvRewards,
            address stash,
            bool shutdown
        );

    function poolLength() external view returns (uint256);

    function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool);

    function withdraw(uint256 _pid, uint256 _amount) external returns (bool);

    function withdrawAll(uint256 _pid) external returns (bool);

    function depositAll(uint256 _pid, bool _stake) external returns (bool);

    function earmarkRewards(uint256 _pid) external returns (bool);

    function isShutdown() external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.17;

library Types {
    struct Coin {
        address coinAddress;
        uint8 decimals;
    }

    struct CliffInfo {
        uint256 currentCliff;
        bool withinThreshold;
    }

    struct PoolInfo {
        address lpToken;
        address basePool;
        uint256 assetType;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface ICurveMetaRegistry {
    event CommitNewAdmin(uint256 indexed deadline, address indexed admin);
    event NewAdmin(address indexed admin);

    function add_registry_handler(address _registry_handler) external;

    function update_registry_handler(uint256 _index, address _registry_handler) external;

    function get_registry_handlers_from_pool(
        address _pool
    ) external view returns (address[10] memory);

    function get_base_registry(address registry_handler) external view returns (address);

    function find_pool_for_coins(address _from, address _to) external view returns (address);

    function find_pool_for_coins(
        address _from,
        address _to,
        uint256 i
    ) external view returns (address);

    function find_pools_for_coins(
        address _from,
        address _to
    ) external view returns (address[] memory);

    function get_admin_balances(address _pool) external view returns (uint256[8] memory);

    function get_admin_balances(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[8] memory);

    function get_balances(address _pool) external view returns (uint256[8] memory);

    function get_balances(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[8] memory);

    function get_base_pool(address _pool) external view returns (address);

    function get_base_pool(address _pool, uint256 _handler_id) external view returns (address);

    function get_coin_indices(
        address _pool,
        address _from,
        address _to
    ) external view returns (int128, int128, bool);

    function get_coin_indices(
        address _pool,
        address _from,
        address _to,
        uint256 _handler_id
    ) external view returns (int128, int128, bool);

    function get_coins(address _pool) external view returns (address[8] memory);

    function get_coins(
        address _pool,
        uint256 _handler_id
    ) external view returns (address[8] memory);

    function get_decimals(address _pool) external view returns (uint256[8] memory);

    function get_decimals(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[8] memory);

    function get_fees(address _pool) external view returns (uint256[10] memory);

    function get_fees(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[10] memory);

    function get_gauge(address _pool) external view returns (address);

    function get_gauge(address _pool, uint256 gauge_idx) external view returns (address);

    function get_gauge(
        address _pool,
        uint256 gauge_idx,
        uint256 _handler_id
    ) external view returns (address);

    function get_gauge_type(address _pool) external view returns (int128);

    function get_gauge_type(address _pool, uint256 gauge_idx) external view returns (int128);

    function get_gauge_type(
        address _pool,
        uint256 gauge_idx,
        uint256 _handler_id
    ) external view returns (int128);

    function get_lp_token(address _pool) external view returns (address);

    function get_lp_token(address _pool, uint256 _handler_id) external view returns (address);

    function get_n_coins(address _pool) external view returns (uint256);

    function get_n_coins(address _pool, uint256 _handler_id) external view returns (uint256);

    function get_n_underlying_coins(address _pool) external view returns (uint256);

    function get_n_underlying_coins(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256);

    function get_pool_asset_type(address _pool) external view returns (uint256);

    function get_pool_asset_type(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256);

    function get_pool_from_lp_token(address _token) external view returns (address);

    function get_pool_from_lp_token(
        address _token,
        uint256 _handler_id
    ) external view returns (address);

    function get_pool_params(address _pool) external view returns (uint256[20] memory);

    function get_pool_params(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[20] memory);

    function get_pool_name(address _pool) external view returns (string memory);

    function get_pool_name(
        address _pool,
        uint256 _handler_id
    ) external view returns (string memory);

    function get_underlying_balances(address _pool) external view returns (uint256[8] memory);

    function get_underlying_balances(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[8] memory);

    function get_underlying_coins(address _pool) external view returns (address[8] memory);

    function get_underlying_coins(
        address _pool,
        uint256 _handler_id
    ) external view returns (address[8] memory);

    function get_underlying_decimals(address _pool) external view returns (uint256[8] memory);

    function get_underlying_decimals(
        address _pool,
        uint256 _handler_id
    ) external view returns (uint256[8] memory);

    function get_virtual_price_from_lp_token(address _token) external view returns (uint256);

    function get_virtual_price_from_lp_token(
        address _token,
        uint256 _handler_id
    ) external view returns (uint256);

    function is_meta(address _pool) external view returns (bool);

    function is_meta(address _pool, uint256 _handler_id) external view returns (bool);

    function is_registered(address _pool) external view returns (bool);

    function is_registered(address _pool, uint256 _handler_id) external view returns (bool);

    function pool_count() external view returns (uint256);

    function pool_list(uint256 _index) external view returns (address);

    function address_provider() external view returns (address);

    function owner() external view returns (address);

    function get_registry(uint256 arg0) external view returns (address);

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

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface ICurvePoolV0 {
    function get_virtual_price() external view returns (uint256);

    function add_liquidity(uint256[8] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[7] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[6] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[5] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[3] calldata amounts, uint256 min_mint_amount) external;

    function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) external;

    function remove_liquidity_imbalance(
        uint256[4] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[3] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[2] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function lp_token() external view returns (address);

    function A_PRECISION() external view returns (uint256);

    function A_precise() external view returns (uint256);

    function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts) external;

    function remove_liquidity(uint256 _amount, uint256[3] calldata min_amounts) external;

    function remove_liquidity(uint256 _amount, uint256[4] calldata min_amounts) external;

    function remove_liquidity(uint256 _amount, uint256[5] calldata min_amounts) external;

    function remove_liquidity(uint256 _amount, uint256[6] calldata min_amounts) external;

    function remove_liquidity(uint256 _amount, uint256[7] calldata min_amounts) external;

    function remove_liquidity(uint256 _amount, uint256[8] calldata min_amounts) external;

    function exchange(
        int128 from,
        int128 to,
        uint256 _from_amount,
        uint256 _min_to_amount
    ) external;

    function coins(int128 i) external view returns (address);

    function balances(int128 i) external view returns (uint256);

    function get_dy(int128 i, int128 j, uint256 _dx) external view returns (uint256);

    function calc_token_amount(
        uint256[4] calldata amounts,
        bool deposit
    ) external view returns (uint256);

    function calc_token_amount(
        uint256[3] calldata amounts,
        bool deposit
    ) external view returns (uint256);

    function calc_token_amount(
        uint256[2] calldata amounts,
        bool deposit
    ) external view returns (uint256);

    function calc_withdraw_one_coin(
        uint256 _token_amount,
        int128 i
    ) external view returns (uint256);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):