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);
}