Skip to content

Commit

Permalink
Minor fixes for aero pool (#1725)
Browse files Browse the repository at this point in the history
- Adding whale account in fork fuzzing.
- Fund as much as possible in the case where whale doesn't have enough
balance.
- Call `totalShares` in `get_vault_shares` with fallback.
- Set `vault_shares_token_contract` to None if it's the zero address.
- `get_variable_rate` returns None if `vault_shares_token_contract` is
None.
  • Loading branch information
Sheng Lundquist authored Oct 31, 2024
1 parent a4665c5 commit ac331b7
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 50 deletions.
3 changes: 3 additions & 0 deletions scripts/fork_fuzz_bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
# with the value as the whale address.
# Note that if a token is missing in this mapping, we will try to
# call `mint` on the trading token to fund.
# TODO we need a better method to ensure this list of whales doesn't get stale
MAINNET_WHALE_ADDRESSES = {
# stETH
"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84": "0x7F39C581F595B53C5CB19BD0B3F8DA6C935E2CA0",
Expand Down Expand Up @@ -71,6 +72,8 @@
"0xe66E3A37C3274Ac24FE8590f7D84A2427194DC17": "0x5E564c1905fFF9724621542f58d61BE0405C4879",
# snARS
"0xC1F4C75e8925A67BE4F35D6b1c044B5ea8849a58": "0x54423d0A5c4e3a6Eb8Bd12FDD54c1e6b42D52Ebe",
# LP Token for Areo USD pool
"0x6cDcb1C4A4D1C3C6d054b27AC5B77e89eAFb971d": "0xD22B2890A6812414B544598fC77AC04382008754",
}

# We build an outer lookup based on chain id
Expand Down
10 changes: 8 additions & 2 deletions src/agent0/core/hyperdrive/interactive/hyperdrive_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import asyncio
import logging
import threading
from functools import partial
from typing import TYPE_CHECKING, Literal, Type, overload
Expand Down Expand Up @@ -273,10 +274,15 @@ def add_funds(
else:
base_scaled_value = base.scaled_value

# If whale doesn't have enough balance, give it as much as possible
if whale_balance < base_scaled_value:
raise ValueError(
f"Whale does not have enough base to transfer. {whale_balance=}, {base_scaled_value=}."
logging.warning(
"Whale %s doesn't have enough base to transfer %s, only supplying %s base.",
whale_account_addr,
FixedPoint(scaled_value=base_scaled_value),
FixedPoint(scaled_value=whale_balance),
)
base_scaled_value = whale_balance

# RPC anvil call to impersonate account
response = self.chain._web3.provider.make_request(
Expand Down
56 changes: 31 additions & 25 deletions src/agent0/ethpy/hyperdrive/interface/_contract_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from hyperdrivetypes.types.IHyperdrive import IHyperdriveContract, Options
from hyperdrivetypes.types.MockERC4626 import MockERC4626Contract
from hyperdrivetypes.types.MockLido import MockLidoContract
from packaging.version import Version
from pypechain.core import PypechainCallException
from web3 import Web3
from web3.exceptions import BadFunctionCallOutput, ContractLogicError
Expand Down Expand Up @@ -96,34 +97,39 @@ def _get_vault_shares(
) -> FixedPoint:
"""See API for documentation."""

# TODO call `hyperdrive_contract.functions.totalShares` instead of custom logic between pools
if interface.hyperdrive_kind == interface.HyperdriveKind.STETH:
# Type narrowing
assert interface.vault_shares_token_contract is not None
vault_shares = interface.vault_shares_token_contract.functions.sharesOf(hyperdrive_contract.address).call(
# `totalShares` is only available after hyperdrive version `1.0.17`
if Version(interface.hyperdrive_version) >= Version("1.0.17"):
vault_shares = interface.hyperdrive_contract.functions.totalShares().call(
block_identifier=block_identifier or "latest"
)
elif interface.hyperdrive_kind == interface.HyperdriveKind.MORPHO:
# Type narrowing
assert interface.morpho_contract is not None
assert interface.morpho_market_id is not None

# TODO pypechain requires bytes input (not HexBytes) for the position function call.
# Fix to allow for bytes input to be interchangeable.
morpho_market_id = bytes(interface.morpho_market_id)

# Get token balances
vault_shares = (
interface.morpho_contract.functions.position(morpho_market_id, hyperdrive_contract.address)
.call(block_identifier=block_identifier or "latest")
.supplyShares
)
else:
# Type narrowing
assert interface.vault_shares_token_contract is not None
vault_shares = interface.vault_shares_token_contract.functions.balanceOf(hyperdrive_contract.address).call(
block_identifier=block_identifier or "latest"
)
if interface.hyperdrive_kind == interface.HyperdriveKind.STETH:
# Type narrowing
assert interface.vault_shares_token_contract is not None
vault_shares = interface.vault_shares_token_contract.functions.sharesOf(hyperdrive_contract.address).call(
block_identifier=block_identifier or "latest"
)
elif interface.hyperdrive_kind == interface.HyperdriveKind.MORPHO:
# Type narrowing
assert interface.morpho_contract is not None
assert interface.morpho_market_id is not None

# TODO pypechain requires bytes input (not HexBytes) for the position function call.
# Fix to allow for bytes input to be interchangeable.
morpho_market_id = bytes(interface.morpho_market_id)

# Get token balances
vault_shares = (
interface.morpho_contract.functions.position(morpho_market_id, hyperdrive_contract.address)
.call(block_identifier=block_identifier or "latest")
.supplyShares
)
else:
# Type narrowing
assert interface.vault_shares_token_contract is not None
vault_shares = interface.vault_shares_token_contract.functions.balanceOf(hyperdrive_contract.address).call(
block_identifier=block_identifier or "latest"
)
return FixedPoint(scaled_value=vault_shares)


Expand Down
8 changes: 6 additions & 2 deletions src/agent0/ethpy/hyperdrive/interface/read_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def __init__(
The signature for transactions. Defaults to `0xa0`.
"""
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
if txn_signature is None:
self.txn_signature = AGENT0_SIGNATURE
else:
Expand Down Expand Up @@ -265,7 +266,10 @@ def __init__(
# >= 1.0.17
if Version(self.hyperdrive_version) < Version("1.0.17"):
self.txn_signature = bytes(0)

elif vault_shares_token_address == ADDRESS_ZERO:
# Some contracts don't have a vault shares token contract. We set the contract to None
# in this case.
self.vault_shares_token_contract = None
else:
# TODO Although the underlying function might not be a MockERC4626Contract,
# the pypechain contract factory happily accepts any address and exposes
Expand Down Expand Up @@ -584,7 +588,7 @@ def get_variable_rate(self, block_identifier: BlockIdentifier | None = None) ->
if block_identifier is None:
block_identifier = "latest"
if self.vault_shares_token_contract is None:
raise ValueError("Vault shares token contract is not set")
return None
return _get_variable_rate(self.vault_shares_token_contract, block_identifier)

def get_standardized_variable_rate(self, time_range: int = 604800) -> FixedPoint:
Expand Down
36 changes: 15 additions & 21 deletions src/agent0/hyperfuzz/system_fuzz/run_fuzz_bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from agent0.ethpy.hyperdrive import HyperdriveReadWriteInterface
from agent0.hyperfuzz import FuzzAssertionException
from agent0.hyperfuzz.system_fuzz.invariant_checks import run_invariant_checks
from agent0.hyperlogs.rollbar_utilities import log_rollbar_exception
from agent0.hyperlogs.rollbar_utilities import log_rollbar_exception, log_rollbar_message

ONE_HOUR_IN_SECONDS = 60 * 60
ONE_DAY_IN_SECONDS = ONE_HOUR_IN_SECONDS * 24
Expand Down Expand Up @@ -55,7 +55,7 @@
LP_SHARE_PRICE_GOVERNANCE_LP_FEE_RANGE: tuple[float, float] = (0, 0)
LP_SHARE_PRICE_GOVERNANCE_ZOMBIE_FEE_RANGE: tuple[float, float] = (0, 0)

TRADE_COUNT_PERIODIC_CHECK = 100
TRADE_COUNT_CHECK = 100


# pylint: disable=too-many-locals
Expand Down Expand Up @@ -159,9 +159,8 @@ def _check_trades_made_on_pool(

logging.info("Trade counts: %s", trade_counts)

# After 50 iterations, we expect all pools to make at least one trade
# Iteration at this point has already been incremented
if iteration % TRADE_COUNT_PERIODIC_CHECK == 0:
# After some iterations, we expect all pools to make at least one trade
if iteration == TRADE_COUNT_CHECK:
trade_counts = trade_counts.reset_index()
# Omission of rows means no trades of that type went through
for pool in hyperdrive_pools:
Expand Down Expand Up @@ -208,9 +207,10 @@ def _check_trades_made_on_pool(
)

if has_err:
error_message = "FuzzBots: " + error_message
logging.error(error_message)
# We log message to get rollbar to group these messages together
log_rollbar_exception(ValueError(error_message), logging.ERROR, rollbar_log_prefix="FuzzBots:")
log_rollbar_message(error_message, logging.ERROR)


def run_fuzz_bots(
Expand Down Expand Up @@ -489,21 +489,15 @@ def run_fuzz_bots(
logging.info("Refunding agents...")
if run_async:
raise NotImplementedError("Running async not implemented")
try:
_ = [
agent.add_funds(
base=base_budget_per_bot,
eth=eth_budget_per_bot,
pool=hyperdrive_pool,
whale_accounts=whale_accounts,
)
for agent in agents
]
except ValueError as e:
# Look for the case when the whale doesn't have enough base to transfer, and
# ignore when refunding.
if "Whale does not have enough base to transfer." not in e.args[0]:
raise e
_ = [
agent.add_funds(
base=base_budget_per_bot,
eth=eth_budget_per_bot,
pool=hyperdrive_pool,
whale_accounts=whale_accounts,
)
for agent in agents
]

if random_advance_time:
# We only allow random advance time if the chain connected to the pool is a
Expand Down

0 comments on commit ac331b7

Please sign in to comment.