diff --git a/.gitignore b/.gitignore index 866cf27..a541f5f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ __pycache__/ venv/ bvenv/ .env +.hypothesis/ diff --git a/contracts/interfaces/StableFactoryNG.json b/contracts/interfaces/StableFactoryNG.json new file mode 100644 index 0000000..f4d25f0 --- /dev/null +++ b/contracts/interfaces/StableFactoryNG.json @@ -0,0 +1,996 @@ +[ + { + "name": "BasePoolAdded", + "inputs": [ + { + "name": "base_pool", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "PlainPoolDeployed", + "inputs": [ + { + "name": "coins", + "type": "address[8]", + "indexed": false + }, + { + "name": "A", + "type": "uint256", + "indexed": false + }, + { + "name": "fee", + "type": "uint256", + "indexed": false + }, + { + "name": "deployer", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "MetaPoolDeployed", + "inputs": [ + { + "name": "coin", + "type": "address", + "indexed": false + }, + { + "name": "base_pool", + "type": "address", + "indexed": false + }, + { + "name": "A", + "type": "uint256", + "indexed": false + }, + { + "name": "fee", + "type": "uint256", + "indexed": false + }, + { + "name": "deployer", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "LiquidityGaugeDeployed", + "inputs": [ + { + "name": "pool", + "type": "address", + "indexed": false + }, + { + "name": "gauge", + "type": "address", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "constructor", + "inputs": [ + { + "name": "_fee_receiver", + "type": "address" + }, + { + "name": "_owner", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "find_pool_for_coins", + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "find_pool_for_coins", + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "i", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_base_pool", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_n_coins", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_meta_n_coins", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_coins", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_underlying_coins", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_decimals", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_underlying_decimals", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_metapool_rates", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_balances", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_underlying_balances", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_A", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_fees", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_admin_balances", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[8]" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_coin_indices", + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "int128" + }, + { + "name": "", + "type": "int128" + }, + { + "name": "", + "type": "bool" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_gauge", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_implementation_address", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "is_meta", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_pool_asset_types", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint8[8]" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deploy_plain_pool", + "inputs": [ + { + "name": "_name", + "type": "string" + }, + { + "name": "_symbol", + "type": "string" + }, + { + "name": "_coins", + "type": "address[8]" + }, + { + "name": "_A", + "type": "uint256" + }, + { + "name": "_fee", + "type": "uint256" + }, + { + "name": "_offpeg_fee_multiplier", + "type": "uint256" + }, + { + "name": "_ma_exp_time", + "type": "uint256" + }, + { + "name": "_implementation_idx", + "type": "uint256" + }, + { + "name": "_asset_types", + "type": "uint8[8]" + }, + { + "name": "_method_ids", + "type": "bytes4[8]" + }, + { + "name": "_oracles", + "type": "address[8]" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deploy_metapool", + "inputs": [ + { + "name": "_base_pool", + "type": "address" + }, + { + "name": "_name", + "type": "string" + }, + { + "name": "_symbol", + "type": "string" + }, + { + "name": "_coin", + "type": "address" + }, + { + "name": "_A", + "type": "uint256" + }, + { + "name": "_fee", + "type": "uint256" + }, + { + "name": "_offpeg_fee_multiplier", + "type": "uint256" + }, + { + "name": "_ma_exp_time", + "type": "uint256" + }, + { + "name": "_implementation_idx", + "type": "uint256" + }, + { + "name": "_asset_type", + "type": "uint8" + }, + { + "name": "_method_id", + "type": "bytes4" + }, + { + "name": "_oracle", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "deploy_gauge", + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "add_base_pool", + "inputs": [ + { + "name": "_base_pool", + "type": "address" + }, + { + "name": "_base_lp_token", + "type": "address" + }, + { + "name": "_asset_types", + "type": "uint8[8]" + }, + { + "name": "_n_coins", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_pool_implementations", + "inputs": [ + { + "name": "_implementation_index", + "type": "uint256" + }, + { + "name": "_implementation", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_metapool_implementations", + "inputs": [ + { + "name": "_implementation_index", + "type": "uint256" + }, + { + "name": "_implementation", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_math_implementation", + "inputs": [ + { + "name": "_math_implementation", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_gauge_implementation", + "inputs": [ + { + "name": "_gauge_implementation", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_views_implementation", + "inputs": [ + { + "name": "_views_implementation", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "commit_transfer_ownership", + "inputs": [ + { + "name": "_addr", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "accept_transfer_ownership", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_fee_receiver", + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_fee_receiver", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "add_asset_type", + "inputs": [ + { + "name": "_id", + "type": "uint8" + }, + { + "name": "_name", + "type": "string" + } + ], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "admin", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_admin", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "asset_types", + "inputs": [ + { + "name": "arg0", + "type": "uint8" + } + ], + "outputs": [ + { + "name": "", + "type": "string" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "pool_list", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "pool_count", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "base_pool_list", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "base_pool_count", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "base_pool_data", + "inputs": [ + { + "name": "arg0", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "components": [ + { + "name": "lp_token", + "type": "address" + }, + { + "name": "coins", + "type": "address[8]" + }, + { + "name": "decimals", + "type": "uint256" + }, + { + "name": "n_coins", + "type": "uint256" + }, + { + "name": "asset_types", + "type": "uint8[8]" + } + ] + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "base_pool_assets", + "inputs": [ + { + "name": "arg0", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "pool_implementations", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "metapool_implementations", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "math_implementation", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "gauge_implementation", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "views_implementation", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "fee_receiver", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + } + ] diff --git a/contracts/ng/CurveStableSwapFactoryNGHandler.vy b/contracts/ng/CurveStableSwapFactoryNGHandler.vy new file mode 100644 index 0000000..38030b6 --- /dev/null +++ b/contracts/ng/CurveStableSwapFactoryNGHandler.vy @@ -0,0 +1,557 @@ +# pragma version 0.3.10 +# pragma evm-version paris +""" +@title CurveStableswapFactoryNGHandler +@author Curve.Fi +@license Copyright (c) Curve.Fi, 2023 - all rights reserved +@notice StableswapNGFactory handler for the Metaregistry +""" + +# ---- interfaces ---- # +interface BaseRegistry: + def find_pool_for_coins(_from: address, _to: address, i: uint256 = 0) -> address: view + def get_admin_balances(_pool: address) -> DynArray[uint256, MAX_METAREGISTRY_COINS]: view + def get_A(_pool: address) -> uint256: view + def get_balances(_pool: address) -> DynArray[uint256, MAX_METAREGISTRY_COINS]: view + def get_base_pool(_pool: address) -> address: view + def get_coins(_pool: address) -> DynArray[address, MAX_METAREGISTRY_COINS]: view + def get_coin_indices(_pool: address, _from: address, _to: address) -> (int128, int128): view + def get_decimals(_pool: address) -> DynArray[uint256, MAX_METAREGISTRY_COINS]: view + def get_fees(_pool: address) -> uint256[2]: view + def get_gauge(_pool: address) -> address: view + def get_lp_token(_pool: address) -> address: view + def get_meta_n_coins(_pool: address) -> (uint256, uint256): view + def get_n_coins(_pool: address) -> uint256: view + def get_pool_asset_type(_pool: address) -> uint256: view + def get_underlying_balances(_pool: address) -> DynArray[uint256, MAX_METAREGISTRY_COINS]: view + def get_underlying_coins(_pool: address) -> DynArray[address, MAX_METAREGISTRY_COINS]: view + def get_underlying_decimals(_pool: address) -> DynArray[uint256, MAX_METAREGISTRY_COINS]: view + def is_meta(_pool: address) -> bool: view + def pool_count() -> uint256: view + def pool_list(pool_id: uint256) -> address: view + +interface BasePoolRegistry: + def get_base_pool_for_lp_token(_lp_token: address) -> address: view + def get_n_coins(_pool: address) -> uint256: view + def get_coins(_pool: address) -> DynArray[address, MAX_METAREGISTRY_COINS]: view + def get_lp_token(_pool: address) -> address: view + def is_legacy(_pool: address) -> bool: view + def base_pool_list(i: uint256) -> address: view + +interface CurveLegacyPool: + def balances(i: int128) -> uint256: view + +interface CurvePool: + def admin_balances(i: uint256) -> uint256: view + def balances(i: uint256) -> uint256: view + def get_virtual_price() -> uint256: view + +interface ERC20: + def balanceOf(_addr: address) -> uint256: view + def decimals() -> uint256: view + def name() -> String[64]: view + def totalSupply() -> uint256: view + +interface GaugeController: + def gauge_types(gauge: address) -> int128: view + def gauges(i: uint256) -> address: view + +interface Gauge: + def is_killed() -> bool: view + +interface MetaRegistry: + def registry_length() -> uint256: view + + +# ---- constants ---- # +GAUGE_CONTROLLER: constant(address) = 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB +MAX_METAREGISTRY_COINS: constant(uint256) = 8 + + +# ---- storage variables ---- # +base_registry: public(BaseRegistry) +base_pool_registry: public(BasePoolRegistry) + + +# ---- constructor ---- # +@external +def __init__(_registry_address: address, _base_pool_registry: address): + self.base_registry = BaseRegistry(_registry_address) + self.base_pool_registry = BasePoolRegistry(_base_pool_registry) + + +# ---- internal methods ---- # +@internal +@view +def _is_meta(_pool: address) -> bool: + return self.base_registry.is_meta(_pool) + + +@internal +@view +def _get_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + _coins: DynArray[address, MAX_METAREGISTRY_COINS] = self.base_registry.get_coins(_pool) + _padded_coins: address[MAX_METAREGISTRY_COINS] = empty(address[MAX_METAREGISTRY_COINS]) + for i in range(MAX_METAREGISTRY_COINS): + if i == len(_coins): + break + _padded_coins[i] = _coins[i] + return _padded_coins + + +@internal +@view +def _get_underlying_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + _coins: DynArray[address, MAX_METAREGISTRY_COINS] = self.base_registry.get_underlying_coins(_pool) + _padded_coins: address[MAX_METAREGISTRY_COINS] = empty(address[MAX_METAREGISTRY_COINS]) + for i in range(MAX_METAREGISTRY_COINS): + if i == len(_coins): + break + _padded_coins[i] = _coins[i] + return _padded_coins + + +@internal +@view +def _get_n_coins(_pool: address) -> uint256: + if self._is_meta(_pool): + return 2 + return self.base_registry.get_n_coins(_pool) + + +@internal +@view +def _get_base_pool(_pool: address) -> address: + _coins: address[MAX_METAREGISTRY_COINS] = self._get_coins(_pool) + _base_pool: address = empty(address) + for coin in _coins: + _base_pool = self.base_pool_registry.get_base_pool_for_lp_token(coin) + if _base_pool != empty(address): + return _base_pool + return empty(address) + + +@view +@internal +def _get_meta_underlying_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + base_coin_idx: uint256 = self._get_n_coins(_pool) - 1 + base_pool: address = self._get_base_pool(_pool) + base_total_supply: uint256 = ERC20(self.base_pool_registry.get_lp_token(base_pool)).totalSupply() + + ul_balance: uint256 = 0 + underlying_pct: uint256 = 0 + if base_total_supply > 0: + underlying_pct = CurvePool(_pool).balances(base_coin_idx) * 10**36 / base_total_supply + + underlying_balances: uint256[MAX_METAREGISTRY_COINS] = empty(uint256[MAX_METAREGISTRY_COINS]) + ul_coins: address[MAX_METAREGISTRY_COINS] = self._get_underlying_coins(_pool) + + for i in range(MAX_METAREGISTRY_COINS): + + if ul_coins[i] == empty(address): + break + + if i < base_coin_idx: + ul_balance = CurvePool(_pool).balances(i) + + else: + + if self.base_pool_registry.is_legacy(base_pool): + ul_balance = CurveLegacyPool(base_pool).balances(convert(i - base_coin_idx, int128)) + else: + ul_balance = CurvePool(base_pool).balances(i - base_coin_idx) + ul_balance = ul_balance * underlying_pct / 10**36 + underlying_balances[i] = ul_balance + + return underlying_balances + + +@internal +@view +def _pad_uint_dynarray( + _array: DynArray[uint256, MAX_METAREGISTRY_COINS] +) -> uint256[MAX_METAREGISTRY_COINS]: + _padded_array: uint256[MAX_METAREGISTRY_COINS] = empty(uint256[MAX_METAREGISTRY_COINS]) + array_len: uint256 = len(_array) + for i in range(MAX_METAREGISTRY_COINS): + if i == array_len: + break + _padded_array[i] = _array[i] + return _padded_array + + +@internal +@view +def _pad_addr_dynarray(_array: DynArray[address, MAX_METAREGISTRY_COINS]) -> address[MAX_METAREGISTRY_COINS]: + _padded_array: address[MAX_METAREGISTRY_COINS] = empty(address[MAX_METAREGISTRY_COINS]) + array_len: uint256 = len(_array) + for i in range(MAX_METAREGISTRY_COINS): + if i == array_len: + break + _padded_array[i] = _array[i] + return _padded_array + + +@internal +@view +def _get_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + return self._pad_uint_dynarray(self.base_registry.get_balances(_pool)) + + +@internal +@view +def _get_decimals(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + return self._pad_uint_dynarray(self.base_registry.get_decimals(_pool)) + + +@internal +@view +def _get_gauge_type(_gauge: address) -> int128: + + success: bool = False + response: Bytes[32] = b"" + success, response = raw_call( + GAUGE_CONTROLLER, + concat( + method_id("gauge_type(address)"), + convert(_gauge, bytes32), + ), + max_outsize=32, + revert_on_failure=False, + is_static_call=True + ) + + if success and not Gauge(_gauge).is_killed(): + return convert(response, int128) + + return 0 + + +# ---- view methods (API) of the contract ---- # +@external +@view +def find_pool_for_coins(_from: address, _to: address, i: uint256 = 0) -> address: + return self.base_registry.find_pool_for_coins(_from, _to, i) + + +@external +@view +def get_admin_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Get the balances of the admin of the pool + @dev does not use base registry admin_balances because that has errors + in the getter for n_coins (some pools show zero, so admin balances is zero) + @param _pool address of the pool + @return balances of the admin of the pool + """ + n_coins: uint256 = self._get_n_coins(_pool) + admin_balances: uint256[MAX_METAREGISTRY_COINS] = empty(uint256[MAX_METAREGISTRY_COINS]) + for i in range(MAX_METAREGISTRY_COINS): + if i == n_coins: + break + admin_balances[i] = CurvePool(_pool).admin_balances(i) + return admin_balances + + +@external +@view +def get_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Get the balances of the pool + @param _pool address of the pool + @return balances of the pool + """ + return self._get_balances(_pool) + + +@external +@view +def get_base_pool(_pool: address) -> address: + """ + @notice Get the base pool of the pool + @param _pool address of the pool + @return base pool of the pool + """ + return self._get_base_pool(_pool) + + +@view +@external +def get_coin_indices(_pool: address, _from: address, _to: address) -> (int128, int128, bool): + """ + @notice Get the indices of the coins in the pool + @param _pool address of the pool + @param _from address of the coin + @param _to address of the coin + @return coin indices and whether the coin swap involves an underlying market or not + """ + coin1: int128 = 0 + coin2: int128 = 0 + is_underlying: bool = False + + (coin1, coin2) = self.base_registry.get_coin_indices(_pool, _from, _to) + + # due to a bug in original factory contract, `is_underlying`` is always True + # to fix this, we first check if it is a metapool, and if not then we return + # False. If so, then we check if basepool lp token is one of the two coins, + # in which case `is_underlying` would be False + if self._is_meta(_pool): + base_pool_lp_token: address = self.base_registry.get_coins(_pool)[1] + if base_pool_lp_token not in [_from, _to]: + is_underlying = True + + return (coin1, coin2, is_underlying) + + +@external +@view +def get_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + """ + @notice Get the coins of the pool + @param _pool address of the pool + @return coins of the pool + """ + return self._get_coins(_pool) + + +@external +@view +def get_decimals(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Get the decimals of coins in the pool + @param _pool address of the pool + @return decimals of coins in the pool + """ + return self._get_decimals(_pool) + + +@external +@view +def get_fees(_pool: address) -> uint256[10]: + """ + @notice Get the fees of the pool + @param _pool address of the pool + @return fees of the pool + """ + fees: uint256[10] = empty(uint256[10]) + pool_fees: uint256[2] = self.base_registry.get_fees(_pool) + for i in range(2): + fees[i] = pool_fees[i] + return fees + + +@external +@view +def get_virtual_price_from_lp_token(_pool: address) -> uint256: + """ + @notice Get the virtual price of the pool + @param _pool address of the pool + @return virtual price of the pool + """ + return CurvePool(_pool).get_virtual_price() + + +@external +@view +def get_gauges(_pool: address) -> (address[10], int128[10]): + """ + @notice Get the gauges and gauge types of the pool + @param _pool address of the pool + @return gauges of the pool + """ + gauges: address[10] = empty(address[10]) + types: int128[10] = empty(int128[10]) + gauges[0] = self.base_registry.get_gauge(_pool) + types[0] = self._get_gauge_type(gauges[0]) + return (gauges, types) + + +@external +@view +def get_lp_token(_pool: address) -> address: + """ + @notice Get the lp token of the pool + @dev for stableswap factory pools, the pool is the lp token itself + @param _pool address of the pool + @return lp token of the pool + """ + return _pool + + +@external +@view +def get_n_coins(_pool: address) -> uint256: + """ + @notice Get the number of coins in the pool + @param _pool address of the pool + @return number of coins in the pool + """ + return self._get_n_coins(_pool) + + +@external +@view +def get_n_underlying_coins(_pool: address) -> uint256: + """ + @notice Get the number of underlying coins in the pool + @param _pool address of the pool + @return number of underlying coins in the pool + """ + # need to check if any of the token is a base pool LP token + # since a metapool can be lptoken:lptoken, and it would count + # underlying coins as 1 + base_pool_n_coins instead of 2 x base_pool_n_coins + coins: address[MAX_METAREGISTRY_COINS] = self._get_coins(_pool) + base_pool: address = empty(address) + num_coins: uint256 = 0 + for i in range(MAX_METAREGISTRY_COINS): + + if coins[i] == empty(address): + break + + base_pool = self.base_pool_registry.get_base_pool_for_lp_token(coins[i]) + if base_pool == empty(address) and coins[i] != empty(address): + num_coins += 1 + else: + num_coins += self.base_pool_registry.get_n_coins(base_pool) + + return num_coins + + +@external +@view +def get_pool_asset_type(_pool: address) -> uint256: + """ + @notice Get the asset type of the coins in the pool + @dev 0 = USD, 1 = ETH, 2 = BTC, 3 = Other + @param _pool address of the pool + @return pool asset type of the pool + """ + return self.base_registry.get_pool_asset_type(_pool) + + +@external +@view +def get_pool_from_lp_token(_lp_token: address) -> address: + """ + @notice Get the pool of the lp token + @dev This is more or less like a pass through method. Can be ignored but + We leave it in for consistency across registry handlers. + @param _lp_token address of the lp token (which is also the pool) + @return pool of the lp token + """ + if self._get_n_coins(_lp_token) > 0: + return _lp_token + return empty(address) + + +@external +@view +def get_pool_name(_pool: address) -> String[64]: + """ + @notice Get the name of the pool + @dev stable factory pools are ERC20 tokenized + @return name of the pool + """ + if self._get_n_coins(_pool) == 0: + # _pool is not in base registry, so we ignore: + return "" + return ERC20(_pool).name() + + +@external +@view +def get_pool_params(_pool: address) -> uint256[20]: + """ + @notice Get the parameters of the pool + @param _pool address of the pool + @return parameters of the pool + """ + stableswap_pool_params: uint256[20] = empty(uint256[20]) + stableswap_pool_params[0] = self.base_registry.get_A(_pool) + return stableswap_pool_params + + +@external +@view +def get_underlying_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Get the underlying balances of the pool + @param _pool address of the pool + @return underlying balances of the pool + """ + if not self._is_meta(_pool): + return self._get_balances(_pool) + return self._get_meta_underlying_balances(_pool) + + +@external +@view +def get_underlying_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + """ + @notice Get the underlying coins of the pool + @param _pool address of the pool + @return underlying coins of the pool + """ + if not self._is_meta(_pool): + return self._get_coins(_pool) + return self._get_underlying_coins(_pool) + + +@external +@view +def get_underlying_decimals(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Get the underlying decimals of the pool + @dev If it is a metapool, method uses the base registry. Else it uses a + custom getter. This is because the base registry cannot unpack decimals + (stored as a bitmap) if there is no metapool. So it returns the decimals of + only the first coin. + @param _pool Address of the pool + @return underlying decimals of the pool + """ + if not self._is_meta(_pool): + return self._get_decimals(_pool) + return self._pad_uint_dynarray(self.base_registry.get_underlying_decimals(_pool)) + + +@external +@view +def is_meta(_pool: address) -> bool: + """ + @notice Check if the pool is a metapool + @param _pool address of the pool + @return True if the pool is a metapool + """ + return self._is_meta(_pool) + + +@external +@view +def is_registered(_pool: address) -> bool: + """ + @notice Check if a pool belongs to the registry using get_n_coins + @param _pool The address of the pool + @return A bool corresponding to whether the pool belongs or not + """ + return self._get_n_coins(_pool) > 0 + + +@external +@view +def pool_count() -> uint256: + """ + @notice Get the number of pools in the registry + @return number of pools in the registry + """ + return self.base_registry.pool_count() + + +@external +@view +def pool_list(_index: uint256) -> address: + """ + @notice Get the address of the pool at the given index + @param _index The index of the pool + @return The address of the pool + """ + return self.base_registry.pool_list(_index) diff --git a/contracts/ng/CurveTricryptoFactoryHandler.vy b/contracts/ng/CurveTricryptoFactoryHandler.vy new file mode 100644 index 0000000..ca1b35a --- /dev/null +++ b/contracts/ng/CurveTricryptoFactoryHandler.vy @@ -0,0 +1,487 @@ +# pragma version 0.3.10 +# pragma evm-version paris +""" +@title CurveTricryptoFactoryHandler +@author Curve.Fi +@license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved +@notice TricryptoNG Registry Handler for the MetaRegistry +""" + +interface BaseRegistry: + def find_pool_for_coins(_from: address, _to: address, i: uint256 = 0) -> address: view + def get_coin_indices(_pool: address, _from: address, _to: address) -> (uint256, uint256): view + def get_balances(_pool: address) -> uint256[MAX_COINS]: view + def get_coins(_pool: address) -> address[MAX_COINS]: view + def get_decimals(_pool: address) -> uint256[MAX_COINS]: view + def get_gauge(_pool: address) -> address: view + def get_n_coins(_pool: address) -> uint256: view + def get_token(_pool: address) -> address: view + def pool_count() -> uint256: view + def pool_list(pool_id: uint256) -> address: view + +interface TricryptoNG: + def adjustment_step() -> uint256: view + def admin_fee() -> uint256: view + def allowed_extra_profit() -> uint256: view + def A() -> uint256: view + def balances(i: uint256) -> uint256: view + def D() -> uint256: view + def fee() -> uint256: view + def fee_gamma() -> uint256: view + def gamma() -> uint256: view + def get_virtual_price() -> uint256: view + def ma_time() -> uint256: view + def mid_fee() -> uint256: view + def out_fee() -> uint256: view + def virtual_price() -> uint256: view + def xcp_profit() -> uint256: view + def xcp_profit_a() -> uint256: view + +interface ERC20: + def name() -> String[64]: view + def balanceOf(_addr: address) -> uint256: view + def totalSupply() -> uint256: view + def decimals() -> uint256: view + +interface GaugeController: + def gauge_types(gauge: address) -> int128: view + def gauges(i: uint256) -> address: view + +interface Gauge: + def is_killed() -> bool: view + + +# ---- constants ---- # +GAUGE_CONTROLLER: constant(address) = 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB +MAX_COINS: constant(uint256) = 3 +MAX_METAREGISTRY_COINS: constant(uint256) = 8 +MAX_POOLS: constant(uint256) = 65536 +N_COINS: constant(uint256) = 3 + + +# ---- storage variables ---- # +base_registry: public(BaseRegistry) + + +# ---- constructor ---- # +@external +def __init__(_registry_address: address): + self.base_registry = BaseRegistry(_registry_address) + + +# ---- internal methods ---- # +@internal +@view +def _pad_uint_array(_array: uint256[MAX_COINS]) -> uint256[MAX_METAREGISTRY_COINS]: + _padded_array: uint256[MAX_METAREGISTRY_COINS] = empty(uint256[MAX_METAREGISTRY_COINS]) + for i in range(MAX_COINS): + _padded_array[i] = _array[i] + return _padded_array + + +@internal +@view +def _get_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + return self._pad_uint_array(self.base_registry.get_balances(_pool)) + + +@internal +@view +def _get_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + _coins: address[MAX_COINS] = self.base_registry.get_coins(_pool) + _padded_coins: address[MAX_METAREGISTRY_COINS] = empty(address[MAX_METAREGISTRY_COINS]) + for i in range(MAX_COINS): + _padded_coins[i] = _coins[i] + return _padded_coins + + +@internal +@view +def _get_decimals(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + return self._pad_uint_array(self.base_registry.get_decimals(_pool)) + + +@internal +@view +def _get_n_coins(_pool: address) -> uint256: + + if (self.base_registry.get_coins(_pool)[0] != empty(address)): + return N_COINS + return 0 + + +@internal +@view +def _get_gauge_type(_gauge: address) -> int128: + + # try to get gauge type registered in gauge controller + success: bool = False + response: Bytes[32] = b"" + success, response = raw_call( + GAUGE_CONTROLLER, + concat( + method_id("gauge_type(address)"), + convert(_gauge, bytes32), + ), + max_outsize=32, + revert_on_failure=False, + is_static_call=True + ) + + if success and not Gauge(_gauge).is_killed(): + return convert(response, int128) + + # if we are here, the call to get gauge_type failed. + # in such a case, return a default value. + # ethereum: mainnet crypto pools have gauge type 5 + return 5 + + +@internal +@view +def _is_registered(_pool: address) -> bool: + return self._get_n_coins(_pool) > 0 + + +# ---- view methods (API) of the contract ---- # +@external +@view +def find_pool_for_coins(_from: address, _to: address, i: uint256 = 0) -> address: + """ + @notice checks if either of the two coins are in a base pool and then checks + if the basepool lp token and the other coin have a pool. + This is done because the factory does not have `underlying` methods in + pools that have a basepool lp token in them + @param _from Address of the _from coin + @param _to Address of the _to coin + @param i Index of the pool to return + @return Address of the pool + """ + return self.base_registry.find_pool_for_coins(_from, _to, i) + + +@external +@view +def get_admin_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the balances of the admin tokens of the given pool + @dev Cryptoswap pools do not store admin fees in the form of + admin token balances. Instead, the admin fees are computed + at the time of claim iff sufficient profits have been made. + These fees are allocated to the admin by minting LP tokens + (dilution). The logic to calculate fees are derived from + cryptopool._claim_admin_fees() method. + @param _pool Address of the pool + @return uint256[MAX_METAREGISTRY_COINS] Array of admin balances + """ + + xcp_profit: uint256 = TricryptoNG(_pool).xcp_profit() + xcp_profit_a: uint256 = TricryptoNG(_pool).xcp_profit_a() + admin_fee: uint256 = TricryptoNG(_pool).admin_fee() + admin_balances: uint256[MAX_METAREGISTRY_COINS] = empty(uint256[MAX_METAREGISTRY_COINS]) + + # admin balances are non zero if pool has made more than allowed profits: + if xcp_profit > xcp_profit_a: + + # calculate admin fees in lp token amounts: + fees: uint256 = (xcp_profit - xcp_profit_a) * admin_fee / (2 * 10**10) + if fees > 0: + vprice: uint256 = TricryptoNG(_pool).virtual_price() + frac: uint256 = vprice * 10**18 / (vprice - fees) - 10**18 + + # the total supply of lp token is current supply + claimable: + lp_token_total_supply: uint256 = ERC20(_pool).totalSupply() + d_supply: uint256 = lp_token_total_supply * frac / 10**18 + lp_token_total_supply += d_supply + admin_lp_frac: uint256 = d_supply * 10 ** 18 / lp_token_total_supply + + # get admin balances in individual assets: + reserves: uint256[MAX_METAREGISTRY_COINS] = self._get_balances(_pool) + for i in range(MAX_METAREGISTRY_COINS): + admin_balances[i] = admin_lp_frac * reserves[i] / 10 ** 18 + + return admin_balances + + +@external +@view +def get_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the balances of the tokens of the given pool + @param _pool Address of the pool + @return uint256[MAX_METAREGISTRY_COINS] Array of balances + """ + return self._get_balances(_pool) + + +@external +@view +def get_base_pool(_pool: address) -> address: + """ + @notice Returns the base pool of the given pool + @dev Returns empty(address) if the pool isn't a metapool + @param _pool Address of the pool + @return Address of the base pool + """ + return empty(address) + + +@external +@view +def get_coin_indices(_pool: address, _from: address, _to: address) -> (uint256, uint256, bool): + """ + @notice Convert coin addresses to indices for use with pool methods + @param _pool Address of the pool + @param _from Address of the from coin + @param _to Address of the to coin + @return (uint256, uint256, bool) Tuple of indices of the coins in the pool, + and whether the market is an underlying market or not. + """ + i: uint256 = 0 + j: uint256 = 0 + + (i, j) = self.base_registry.get_coin_indices(_pool, _from, _to) + + return (i, j, False) + + +@external +@view +def get_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the coins of the given pool + @param _pool Address of the pool + @return address[MAX_METAREGISTRY_COINS] Array of coins + """ + return self._get_coins(_pool) + + +@external +@view +def get_decimals(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the decimals of the coins in a given pool + @param _pool Address of the pool + @return uint256[MAX_METAREGISTRY_COINS] Array of decimals + """ + return self._get_decimals(_pool) + + +@external +@view +def get_fees(_pool: address) -> uint256[10]: + """ + @notice Returns the fees of the given pool + @param _pool Address of the pool + @return uint256[10] Array of fees. Fees are arranged as: + 1. swap fee (or `fee`) + 2. admin fee + 3. mid fee (fee when cryptoswap pool is pegged) + 4. out fee (fee when cryptoswap pool depegs) + """ + fees: uint256[10] = empty(uint256[10]) + pool_fees: uint256[4] = [ + TricryptoNG(_pool).fee(), + TricryptoNG(_pool).admin_fee(), + TricryptoNG(_pool).mid_fee(), + TricryptoNG(_pool).out_fee(), + ] + for i in range(4): + fees[i] = pool_fees[i] + return fees + + +@external +@view +def get_gauges(_pool: address) -> (address[10], int128[10]): + """ + @notice Returns the gauges of the given pool + @param _pool Address of the pool + @return (address[10], int128[10]) Tuple of gauges. Gauges are arranged as: + 1. gauge addresses + 2. gauge types + """ + gauges: address[10] = empty(address[10]) + types: int128[10] = empty(int128[10]) + gauges[0] = self.base_registry.get_gauge(_pool) + types[0] = self._get_gauge_type(gauges[0]) + return (gauges, types) + + +@external +@view +def get_lp_token(_pool: address) -> address: + """ + @notice Returns the Liquidity Provider token of the given pool + @param _pool Address of the pool + @return Address of the Liquidity Provider token + """ + return _pool + + +@external +@view +def get_n_coins(_pool: address) -> uint256: + """ + @notice Returns the number of coins in the given pool + @param _pool Address of the pool + @return uint256 Number of coins + """ + return self._get_n_coins(_pool) + + +@external +@view +def get_n_underlying_coins(_pool: address) -> uint256: + """ + @notice Get the number of underlying coins in a pool + @param _pool Address of the pool + @return uint256 Number of underlying coins + """ + _coins: address[MAX_METAREGISTRY_COINS] = self._get_coins(_pool) + + for i in range(MAX_METAREGISTRY_COINS): + if _coins[i] == empty(address): + return i + raise + +@external +@view +def get_pool_asset_type(_pool: address) -> uint256: + """ + @notice Returns the asset type of the given pool + @dev Returns 4: 0 = USD, 1 = ETH, 2 = BTC, 3 = Other + @param _pool Address of the pool + @return uint256 Asset type + """ + return 3 + + +@external +@view +def get_pool_from_lp_token(_lp_token: address) -> address: + """ + @notice Returns the pool of the given Liquidity Provider token + @param _lp_token Address of the Liquidity Provider token + @return Address of the pool + """ + if self._get_n_coins(_lp_token) > 0: + return _lp_token + return empty(address) + + +@external +@view +def get_pool_name(_pool: address) -> String[64]: + """ + @notice Returns the name of the given pool + @param _pool Address of the pool + @return String[64] Name of the pool + """ + return ERC20(self.base_registry.get_token(_pool)).name() + +@external +@view +def get_pool_params(_pool: address) -> uint256[20]: + """ + @notice returns pool params given a cryptopool address + @dev contains all settable parameter that alter the pool's performance + @dev only applicable for cryptopools + @param _pool Address of the pool for which data is being queried. + """ + pool_params: uint256[20] = empty(uint256[20]) + pool_params[0] = TricryptoNG(_pool).A() + pool_params[1] = TricryptoNG(_pool).D() + pool_params[2] = TricryptoNG(_pool).gamma() + pool_params[3] = TricryptoNG(_pool).allowed_extra_profit() + pool_params[4] = TricryptoNG(_pool).fee_gamma() + pool_params[5] = TricryptoNG(_pool).adjustment_step() + pool_params[6] = TricryptoNG(_pool).ma_time() + return pool_params + + +@external +@view +def get_underlying_balances(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the underlying balances of the given pool + @param _pool Address of the pool + @return uint256[MAX_METAREGISTRY_COINS] Array of underlying balances + """ + return self._get_balances(_pool) + + +@external +@view +def get_underlying_coins(_pool: address) -> address[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the underlying coins of the given pool + @param _pool Address of the pool + @return address[MAX_METAREGISTRY_COINS] Array of underlying coins + """ + return self._get_coins(_pool) + + +@external +@view +def get_underlying_decimals(_pool: address) -> uint256[MAX_METAREGISTRY_COINS]: + """ + @notice Returns the underlying decimals of the given pool + @param _pool Address of the pool + @return uint256[MAX_METAREGISTRY_COINS] Array of underlying decimals + """ + return self._get_decimals(_pool) + + +@external +@view +def get_virtual_price_from_lp_token(_token: address) -> uint256: + """ + @notice Returns the virtual price of the given Liquidity Provider token + @param _token Address of the Liquidity Provider token + @return uint256 Virtual price + """ + return TricryptoNG(_token).get_virtual_price() + + +@external +@view +def is_meta(_pool: address) -> bool: + """ + @notice Returns whether the given pool is a meta pool + @param _pool Address of the pool + @return bool Whether the pool is a meta pool + """ + return False + + +@external +@view +def is_registered(_pool: address) -> bool: + """ + @notice Check if a pool belongs to the registry using get_n_coins + @param _pool The address of the pool + @return A bool corresponding to whether the pool belongs or not + """ + return self._get_n_coins(_pool) > 0 + + +@external +@view +def pool_count() -> uint256: + """ + @notice Returns the number of pools in the registry + @return uint256 Number of pools + """ + return self.base_registry.pool_count() + + +@external +@view +def pool_list(_index: uint256) -> address: + """ + @notice Returns the address of the pool at the given index + @param _index Index of the pool + @return Address of the pool + """ + return self.base_registry.pool_list(_index) diff --git a/scripts/boa_scripts/set_up_registries.py b/scripts/boa_scripts/set_up_registries.py deleted file mode 100644 index a7e8c72..0000000 --- a/scripts/boa_scripts/set_up_registries.py +++ /dev/null @@ -1,152 +0,0 @@ -# flake8: noqa - -import os -import sys -from dataclasses import dataclass -from typing import List - -import boa -import deployment_utils as deploy_utils -from boa.network import NetworkEnv -from deploy_infra import deployments -from eth_account import Account -from eth_typing import Address -from eth_utils import to_checksum_address -from rich.console import Console as RichConsole - -logger = RichConsole(file=sys.stdout) - - -def deploy_factory_handler(): - pass - - -def set_up_registries( - network: str, url: str, account: str, fork: bool = False -): - """ - Set up registries for the Curve StableSwapNG factory. - :param network: Network to deploy to. - :param url: URL to connect to. - :param account: Account to use. - :param fork: Whether to deploy to a fork (test) network. - """ - - logger.log(f"Connecting to {network} ...") - if fork: - boa.env.fork(url) - boa.env.eoa = deploy_utils.FIDDYDEPLOYER - logger.log("Forkmode") - else: - logger.log("Prodmode") - boa.set_env(NetworkEnv(url)) - boa.env.eoa = Account.from_key(os.environ[account]) - - data = next( - data - for _network, data in deploy_utils.curve_dao_network_settings.items() - if _network in network - ) - - owner = data.dao_ownership_contract - fee_receiver = data.fee_receiver_address - assert owner, f"Curve's DAO contracts may not be on {network}." - assert fee_receiver, f"Curve's DAO contracts may not be on {network}." - address_provider = Contract(data.address_provider) - - # -------------------------- Register into AddressProvider -------------------------- - - max_id = address_provider.max_id() - description = "Curve StableSwapNG" - boss = Contract(address_provider.admin()) - - # check if account can handle boss: - account_is_boss_handler = any( - account.address.lower() == boss.admins(i).lower() for i in range(2) - ) - assert account_is_boss_handler # only authorised accounts can write to address provider # noqa: E501 - - is_new_deployment = not any( - address_provider.get_id_info(i).description is description - for i in range(max_id + 1) - ) - - if is_new_deployment: - logger.info( - f"Adding a new registry provider entry at id: {max_id + 1}" - ) - - # we're adding a new id - with accounts.use_sender(account) as account: - boss.execute( - address_provider.address, - address_provider.add_new_id.encode_input(factory, description), - gas_limit=400000, - **deploy_utils._get_tx_params(), - ) - - else: - assert address_provider.get_id_info(index).description == description - - logger.info( - f"Updating existing registry provider entry at id: {index}" - ) - - # we're updating an existing id with the same description: - with accounts.use_sender(account) as account: - boss.execute( - address_provider.address, - address_provider.set_address.encode_input(index, factory), - gas_limit=200000, - **deploy_utils._get_tx_params(), - ) - - assert address_provider.get_id_info(index).addr.lower() == factory.lower() - - logger.info("AddressProvider integration complete!") - - # -------------------------- Set up metaregistry -------------------------- - - metaregistry_address = deploy_utils.curve_dao_network_settings[ - network - ].metaregistry_address - base_pool_registry_address = deploy_utils.curve_dao_network_settings[ - network - ].base_pool_registry_address - - if metaregistry_address: - metaregistry = Contract(metaregistry_address) - boss = Contract(metaregistry.owner()) - - # set up metaregistry integration: - logger.info("Integrate into Metaregistry ...") - logger.info( - "Deploying Factory handler to integrate it to the metaregistry:" - ) - factory_handler = account.deploy( - project.CurveStableSwapFactoryNGHandler, - factory.address, - base_pool_registry_address, - **deploy_utils._get_tx_params(), - ) - - boss.execute( - metaregistry.address, - metaregistry.add_registry_handler.encode_input(factory_handler), - **deploy_utils._get_tx_params(), - ) - - logger.info("Metaregistry integration complete!") - - -def main(): - set_up_registries( - "ethereum:mainnet", - os.environ["RPC_ETHEREUM"], - "FIDDYDEPLOYER", - fork=False, - ) - - -if __name__ == "__main__": - main() diff --git a/scripts/change_registry_handler.py b/scripts/change_registry_handler.py index 38e25ae..e7126d2 100644 --- a/scripts/change_registry_handler.py +++ b/scripts/change_registry_handler.py @@ -3,8 +3,8 @@ import boa from rich.console import Console as RichConsole -from scripts.constants import ADDRESS_PROVIDER, ZERO_ADDRESS from scripts.deployment_utils import get_deployed_contract, setup_environment +from scripts.utils.constants import ADDRESS_PROVIDER, ZERO_ADDRESS RICH_CONSOLE = RichConsole(file=sys.stdout) CRYPTO_REGISTRY_HANDLER = "0x5f493fEE8D67D3AE3bA730827B34126CFcA0ae94" diff --git a/scripts/deploy.py b/scripts/deploy.py index 15b8757..880a193 100644 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -8,16 +8,16 @@ requires the URL and ACCOUNT environment variables to be set """ import boa -from constants import ( +from eth_abi import encode +from rich import Console as RichConsole + +from scripts.deployment_utils import setup_environment +from scripts.utils.constants import ( ADDRESS_PROVIDER, CRYPTO_FACTORY_ADDRESS, STABLE_FACTORY_ADDRESS, STABLE_REGISTRY_ADDRESS, ) -from eth_abi import encode -from rich import Console as RichConsole - -from scripts.deployment_utils import setup_environment def main(): diff --git a/scripts/setup_metaregistry.py b/scripts/setup_metaregistry.py index 57674e5..bc38303 100644 --- a/scripts/setup_metaregistry.py +++ b/scripts/setup_metaregistry.py @@ -13,12 +13,12 @@ import boa from rich.console import Console as RichConsole -from scripts.constants import ( +from scripts.deployment_utils import get_deployed_contract, setup_environment +from scripts.utils.constants import ( ADDRESS_PROVIDER, BASE_POOLS, CRYPTO_REGISTRY_POOLS, ) -from scripts.deployment_utils import get_deployed_contract, setup_environment RICH_CONSOLE = RichConsole(file=sys.stdout) diff --git a/scripts/deployment_utils.py b/scripts/utils/__init__.py similarity index 59% rename from scripts/deployment_utils.py rename to scripts/utils/__init__.py index e894cd6..d1b5f1c 100644 --- a/scripts/deployment_utils.py +++ b/scripts/utils/__init__.py @@ -5,9 +5,41 @@ from boa.network import NetworkEnv from boa.vyper.contract import VyperContract from eth_account import Account +from eth_utils import keccak from rich.console import Console as RichConsole -from scripts.constants import BASE_DIR, FIDDY_DEPLOYER +from scripts.utils.constants import BASE_DIR, FIDDY_DEPLOYER + + +def get_create2_deployment_address( + compiled_bytecode, + abi_encoded_ctor, + salt, + create2deployer, + blueprint=False, + blueprint_preamble=b"\xFE\x71\x00", +): + deployment_bytecode = compiled_bytecode + abi_encoded_ctor + if blueprint: + # Add blueprint preamble to disable calling the contract: + blueprint_bytecode = blueprint_preamble + deployment_bytecode + # Add code for blueprint deployment: + len_blueprint_bytecode = len(blueprint_bytecode).to_bytes(2, "big") + deployment_bytecode = ( + b"\x61" + + len_blueprint_bytecode + + b"\x3d\x81\x60\x0a\x3d\x39\xf3" + + blueprint_bytecode + ) + + return ( + create2deployer.computeAddress(salt, keccak(deployment_bytecode)), + deployment_bytecode, + ) + + +def deploy_via_create2_factory(deployment_bytecode, salt, create2deployer): + create2deployer.deploy(0, salt, deployment_bytecode) def setup_environment(console: RichConsole): diff --git a/scripts/class_diagram.py b/scripts/utils/class_diagram.py similarity index 100% rename from scripts/class_diagram.py rename to scripts/utils/class_diagram.py diff --git a/scripts/constants.py b/scripts/utils/constants.py similarity index 100% rename from scripts/constants.py rename to scripts/utils/constants.py diff --git a/scripts/print_missing.py b/scripts/utils/print_missing.py similarity index 100% rename from scripts/print_missing.py rename to scripts/utils/print_missing.py diff --git a/tests/conftest.py b/tests/conftest.py index 0d2b50a..e7a90ff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,9 +27,13 @@ def _get_stable_registry_pools(): @cache def _get_stable_factory_pools(): logging.info("Retrieving stable factory pools") - return get_contract_pools( + factory_pools_mainnet = get_contract_pools( "StableFactory", "0xB9fC157394Af804a3578134A6585C0dc9cc990d4" ) + factory_ng_pools_mainnet = get_contract_pools( + "StableFactoryNG", "0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf" + ) + return factory_pools_mainnet + factory_ng_pools_mainnet @cache