diff --git a/README.md b/README.md index 6c788ee..957f2de 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Mainnet: 1. Curve Stable Registry: A registry of custom pool implementations deployed by Curve Core. 2. Curve Stable Factory: A permissionless [StableSwap](https://curve.fi/files/stableswap-paper.pdf) pool factory, which also acts as a registry for pools that its users create. -3. Curve Crypto Registry: A registry of custom CryptoSwap pool implementaions deployed by Curve Core. +3. Curve Crypto Registry: A registry of custom CryptoSwap pool implementations deployed by Curve Core. 4. Curve Crypto Factory: A permissionless [CryptoSwap](https://curve.fi/files/crypto-pools-paper.pdf) pool factory, which also acts as a registry for pools that its users create. Each of the child registries are accompanied by a RegistryHandler, which is a contract that wraps around the child registry and enforces the abi implemented in the MetaRegistry. These registry handlers are then added to the MetaRegistry using the `MetaRegistry.add_registry_handler` method. @@ -67,9 +67,9 @@ Out[1]: '3pool' #### `MetaRegistry.is_meta` -Metapools are pools that pair a coin to a base pool comprising of multiple coins. +Meta-pools are pools that pair a coin to a base pool comprising multiple coins. -An example is the [`LUSD-3CRV`](https://etherscan.io/address/0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca) pool which pairs [Liquity's](https://www.liquity.org/) [`LUSD`](https://etherscan.io/address/0x5f98805a4e8be255a32880fdec7f6728c6568ba0) against [`3CRV`](https://etherscan.io/address/0x6c3f90f043a72fa612cbac8115ee7e52bde6e490), where `3CRV` is a liquidity pool token that represents a share of a pool containing `DAI`, `USDC` and `USDT`: +An example is the [`LUSD-3CRV`](https://etherscan.io/address/0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca) pool which pairs [Liquidity's](https://www.liquity.org/) [`LUSD`](https://etherscan.io/address/0x5f98805a4e8be255a32880fdec7f6728c6568ba0) against [`3CRV`](https://etherscan.io/address/0x6c3f90f043a72fa612cbac8115ee7e52bde6e490), where `3CRV` is a liquidity pool token that represents a share of a pool containing `DAI`, `USDC` and `USDT`: ``` In [1]: metaregistry.is_meta("0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca") @@ -294,7 +294,7 @@ For CryptoSwap, the getter returns: 4. Allowed extra profit 5. Fee gamma 6. Adjustment step -7. MA (moving average) half time +7. MA (moving average) half-time ``` @@ -338,7 +338,7 @@ Out[1]: '0xc4AD29ba4B3c580e6D59105FFf484999997675Ff' #### `MetaRegistry.get_pool_asset_type` -Gets the asset type of a pool. `0` = `USD`, `1` = `ETH`, `2` = `BTC`, `3` = Other, `4` = CryptoPool token. The asset type is a property of StableSwaps, and is not enforced in CryptoSwap pools (which always return `4`). +Gets the asset type of pool. `0` = `USD`, `1` = `ETH`, `2` = `BTC`, `3` = Other, `4` = CryptoPool token. The asset type is a property of StableSwaps, and is not enforced in CryptoSwap pools (which always return `4`). StableSwap pool example for `LUSD-3CRV` pool which is a `USD` stablecoin pool: diff --git a/tests/mainnet/metaregistry/api/test_find_pool_for_coins.py b/tests/mainnet/metaregistry/api/test_find_pool_for_coins.py index fce1773..d087187 100644 --- a/tests/mainnet/metaregistry/api/test_find_pool_for_coins.py +++ b/tests/mainnet/metaregistry/api/test_find_pool_for_coins.py @@ -1,4 +1,7 @@ -import itertools +from itertools import combinations +from os import environ + +import pytest from tests.utils import ZERO_ADDRESS @@ -11,10 +14,14 @@ def _get_all_combinations(metaregistry, pool): pool_coins = [ coin for coin in metaregistry.get_coins(pool) if coin != ZERO_ADDRESS ] - all_combinations = list(itertools.combinations(pool_coins, 2)) + all_combinations = list(combinations(pool_coins, 2)) first_coin = pool_coins[0] - if metaregistry.is_meta(pool): + # there exist some pools with an LP token as the first coin, that's incorrect + # example: 0xf5d5305790c1af08e9df44b30a1afe56ccda72df + is_first_coin_lp_token = metaregistry.get_pool_from_lp_token(first_coin) + + if metaregistry.is_meta(pool) and not is_first_coin_lp_token: underlying_coins = [ coin for coin in metaregistry.get_underlying_coins(pool) @@ -29,16 +36,16 @@ def _get_all_combinations(metaregistry, pool): return all_combinations +@pytest.mark.skipif( + condition=environ.get("TEST_ALL") == "False", + reason="This test is too slow, don't run it locally every time.", +) def test_all(populated_metaregistry, pool): - combinations = _get_all_combinations(populated_metaregistry, pool) - for combination in combinations: - pools_containing_pair = populated_metaregistry.find_pools_for_coins( - *combination - ) + all_combinations = _get_all_combinations(populated_metaregistry, pool) + for coin1, coin2 in all_combinations: + pools_containing_pair = populated_metaregistry.find_pools_for_coins(coin1, coin2) assert pool in pools_containing_pair for i, found_pool in enumerate(pools_containing_pair): - assert ( - populated_metaregistry.find_pool_for_coins(*combination, i) - == found_pool - ) + pool = populated_metaregistry.find_pool_for_coins(coin1, coin2, i) + assert pool == found_pool diff --git a/tests/mainnet/metaregistry/api/test_get_admin_balances.py b/tests/mainnet/metaregistry/api/test_get_admin_balances.py index 32f4122..f0c9b9a 100644 --- a/tests/mainnet/metaregistry/api/test_get_admin_balances.py +++ b/tests/mainnet/metaregistry/api/test_get_admin_balances.py @@ -3,7 +3,7 @@ from boa import BoaError from eth.codecs.abi.exceptions import DecodeError as ABIDecodeError -from tests.utils import ZERO_ADDRESS, get_deployed_token_contract +from tests.utils import get_deployed_token_contract, check_decode_error, assert_negative_coin_balance def pre_test_checks(metaregistry, pool): @@ -29,31 +29,12 @@ def pre_test_checks(metaregistry, pool): if contract.totalSupply() == 0: return pytest.skip("LP token supply is zero") except ABIDecodeError as e: - assert e.msg == "Value length is not the expected size of 32 bytes" - assert len(e.value) == 4096 + check_decode_error(e) return pytest.skip( f"Pool {pool} cannot decode the total supply of its LP token {lp_token}" ) -def assert_negative_coin_balance(metaregistry, pool): - """ - The implementation of get_balance calculates (balance - admin_balance) but sometimes the coin - balance might be lower than the admin balance, resulting in an uint underflow. - """ - coins = [ - coin for coin in metaregistry.get_coins(pool) if coin != ZERO_ADDRESS - ] - coin_balances = [ - get_deployed_token_contract(coin).balanceOf(pool) for coin in coins - ] - admin_balances = metaregistry.get_admin_balances(pool) - assert any( - coin_balance < admin_balance - for coin_balance, admin_balance in zip(coin_balances, admin_balances) - ) - - def test_stable_registry_pools( populated_metaregistry, stable_registry_pool, stable_registry ): diff --git a/tests/mainnet/metaregistry/api/test_get_underlying_decimals.py b/tests/mainnet/metaregistry/api/test_get_underlying_decimals.py index 264ef97..1594667 100644 --- a/tests/mainnet/metaregistry/api/test_get_underlying_decimals.py +++ b/tests/mainnet/metaregistry/api/test_get_underlying_decimals.py @@ -16,9 +16,6 @@ def _test_underlying_decimals_getter(metaregistry, registry, pool): - metaregistry_output = metaregistry.get_underlying_decimals(pool) - assert metaregistry_output[1] != 0 # there has to be a second coin! - pool_is_metapool = metaregistry.is_meta(pool) if pool in EXCEPTIONS: actual_output = EXCEPTIONS[pool] diff --git a/tests/mainnet/metaregistry/api/test_get_virtual_price.py b/tests/mainnet/metaregistry/api/test_get_virtual_price.py index cc6c858..91fded6 100644 --- a/tests/mainnet/metaregistry/api/test_get_virtual_price.py +++ b/tests/mainnet/metaregistry/api/test_get_virtual_price.py @@ -4,7 +4,8 @@ import pytest from boa import BoaError -from tests.utils import ZERO_ADDRESS, get_deployed_token_contract +from tests.utils import ZERO_ADDRESS, get_deployed_token_contract, check_decode_error, assert_negative_coin_balance +from eth.codecs.abi.exceptions import DecodeError as ABIDecodeError # ---- sanity checks since vprice getters can revert for specific pools states ---- @@ -36,18 +37,17 @@ def _check_skem_tokens_with_weird_decimals( pool_balances_float.append(pool_balances[i] / 10 ** coin_decimals[i]) - if ( - coin_decimals[i] == 0 - and get_deployed_token_contract( - metaregistry.get_coins(pool)[0] - ).decimals() - == 0 - ): - with boa.reverts(): + first_coin = metaregistry.get_coins(pool)[0] + coin_contract = get_deployed_token_contract(first_coin) + if coin_decimals[i] == 0 and coin_contract.decimals() == 0: + try: + virtual_price = metaregistry.get_virtual_price_from_lp_token(lp_token) + warnings.warn(f"Pool {pool} virtual price {virtual_price}. continuing test") + except BoaError: metaregistry.get_virtual_price_from_lp_token(lp_token) - pytest.skip( - f"skem token {coins[i]} in pool {pool} with zero decimals" - ) + pytest.skip( + f"Skem token {coins[i]} in pool {pool} with zero decimals" + ) return pool_balances_float @@ -69,23 +69,22 @@ def _check_pool_is_depegged( and min(pool_balances_float) < 1 ): try: - with boa.reverts(): - metaregistry.get_virtual_price_from_lp_token(lp_token) - + virtual_price = metaregistry.get_virtual_price_from_lp_token(lp_token) + warnings.warn(f"Pool {pool} virtual price {virtual_price}. continuing test") + except BoaError: pytest.skip( f"skewed pool: {pool} as num coins (decimals divided) at index {i} is " f"{pool_balances[i] / 10 ** coin_decimals[i]}" ) - except ( - AssertionError - ): # ok to catch this assertion error since we continue testing - warnings.warn( - "pool virtual price getter did not revert. continuing test" - ) def pre_test_checks(metaregistry, pool): - pool_balances = metaregistry.get_balances(pool) + try: + pool_balances = metaregistry.get_balances(pool) + except BoaError: + assert_negative_coin_balance(metaregistry, pool) + return pytest.skip(f"Pool {pool} has coin balances lower than admin") + lp_token = metaregistry.get_lp_token(pool) _check_pool_has_no_liquidity(metaregistry, pool, pool_balances, lp_token) @@ -139,6 +138,11 @@ def test_stable_factory_pools( except BoaError: with boa.reverts(): populated_metaregistry.get_virtual_price_from_lp_token(lp_token) + except ABIDecodeError as e: + check_decode_error(e) + return pytest.skip( + f"Pool {stable_factory_pool} cannot decode the virtual price" + ) def test_crypto_registry_pools( diff --git a/tests/utils.py b/tests/utils.py index 57ced7c..c3640f1 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -5,6 +5,7 @@ import boa from boa.vyper.contract import VyperContract from eth_account.signers.local import LocalAccount +from eth.codecs.abi.exceptions import DecodeError as ABIDecodeError ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" BASE_DIR = path.join(path.dirname(path.abspath(__file__)), "..") @@ -98,3 +99,31 @@ def get_deployed_token_contract(address: str) -> VyperContract: ] ) return boa.loads_abi(abi).at(address) + + +def check_decode_error(e: ABIDecodeError): + """ + Checks that the error message is the expected decode error. + This seems to be happening in some pools, but it's not clear if it's a boa or contract issue. + :param e: The error to check. + """ + assert e.msg == "Value length is not the expected size of 32 bytes" + assert len(e.value) == 4096 + + +def assert_negative_coin_balance(metaregistry, pool): + """ + The implementation of get_balance calculates (balance - admin_balance) but sometimes the coin + balance might be lower than the admin balance, resulting in an uint underflow. + """ + coins = [ + coin for coin in metaregistry.get_coins(pool) if coin != ZERO_ADDRESS + ] + coin_balances = [ + get_deployed_token_contract(coin).balanceOf(pool) for coin in coins + ] + admin_balances = metaregistry.get_admin_balances(pool) + assert any( + coin_balance < admin_balance + for coin_balance, admin_balance in zip(coin_balances, admin_balances) + )