Skip to content

Commit

Permalink
Handle more test errors
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSchiavini committed Nov 29, 2023
1 parent 8883c78 commit 9049d6b
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ repos:
args: ["--profile", "black", --line-length=79]

default_language_version:
python: python3.11
python: python3.10
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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

```
Expand Down Expand Up @@ -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:

Expand Down
7 changes: 6 additions & 1 deletion scripts/change_registry_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
import boa
from rich.console import Console as RichConsole

from scripts.deployment_utils import setup_environment, ZERO_ADDRESS, get_deployed_contract
from scripts.deployment_utils import (
ADDRESS_PROVIDER,
ZERO_ADDRESS,
get_deployed_contract,
setup_environment,
)

RICH_CONSOLE = RichConsole(file=sys.stdout)
CRYPTO_REGISTRY_HANDLER = "0x5f493fEE8D67D3AE3bA730827B34126CFcA0ae94"
Expand Down
1 change: 1 addition & 0 deletions scripts/deployment_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def setup_environment(console: RichConsole):
boa.env.eoa = FIDDY_DEPLOYER
return False


def get_deployed_contract(contract_name: str, address: str) -> VyperContract:
"""
Loads a contract and retrieves a deployed instance of it with the given address.
Expand Down
6 changes: 5 additions & 1 deletion scripts/setup_metaregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
import boa
from rich.console import Console as RichConsole

from scripts.deployment_utils import ZERO_ADDRESS, get_deployed_contract, setup_environment
from scripts.deployment_utils import (
ZERO_ADDRESS,
get_deployed_contract,
setup_environment,
)

RICH_CONSOLE = RichConsole(file=sys.stdout)

Expand Down
29 changes: 19 additions & 10 deletions tests/mainnet/metaregistry/api/test_find_pool_for_coins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import itertools
from itertools import combinations
from os import environ

import pytest

from tests.utils import ZERO_ADDRESS

Expand All @@ -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)
Expand All @@ -29,16 +36,18 @@ 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:
all_combinations = _get_all_combinations(populated_metaregistry, pool)
for coin1, coin2 in all_combinations:
pools_containing_pair = populated_metaregistry.find_pools_for_coins(
*combination
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
27 changes: 6 additions & 21 deletions tests/mainnet/metaregistry/api/test_get_admin_balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
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 (
assert_negative_coin_balance,
check_decode_error,
get_deployed_token_contract,
)


def pre_test_checks(metaregistry, pool):
Expand All @@ -29,31 +33,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
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,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:
Expand Down
61 changes: 39 additions & 22 deletions tests/mainnet/metaregistry/api/test_get_virtual_price.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
import boa
import pytest
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 (
ZERO_ADDRESS,
assert_negative_coin_balance,
check_decode_error,
get_deployed_token_contract,
)

# ---- sanity checks since vprice getters can revert for specific pools states ----

Expand Down Expand Up @@ -36,18 +42,21 @@ 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

Expand All @@ -69,23 +78,26 @@ 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)
Expand Down Expand Up @@ -139,6 +151,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(
Expand Down
29 changes: 29 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import boa
from boa.vyper.contract import VyperContract
from eth.codecs.abi.exceptions import DecodeError as ABIDecodeError
from eth_account.signers.local import LocalAccount

ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
Expand Down Expand Up @@ -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)
)

0 comments on commit 9049d6b

Please sign in to comment.