Skip to content

Commit

Permalink
Preview on pending (#1595)
Browse files Browse the repository at this point in the history
Fixes #1593
  • Loading branch information
slundqui committed Jul 3, 2024
1 parent 8fd0563 commit 82149d1
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 384 deletions.
32 changes: 19 additions & 13 deletions src/agent0/core/hyperdrive/crash_report/crash_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,22 @@ def build_crash_trade_result(
policy=policy,
trade_object=trade_object,
)
current_block_number = interface.get_block_number(interface.get_current_block())

# Best effort to get the crash block number
if isinstance(exception, ContractCallException) and exception.block_identifier is not None:
crash_block_number = interface.get_block_number(interface.get_block(exception.block_identifier))
crash_block_identifier = exception.block_identifier
else:
crash_block_number = interface.get_block_number(interface.get_current_block())
crash_block_identifier = crash_block_number
trade_result.block_number = crash_block_number

## Check if the exception came from a contract call & determine block number
# If it did, we fill various trade result data with custom data from
# the exception
trade_result.exception = exception
if isinstance(exception, ContractCallException):
trade_result.orig_exception = exception.orig_exception
if exception.block_number is not None:
trade_result.block_number = exception.block_number
else:
# Best effort to get the block it crashed on
# We assume the exception happened in the previous block
trade_result.block_number = current_block_number - 1
trade_result.contract_call = {
"contract_call_type": exception.contract_call_type,
"function_name_or_signature": exception.function_name_or_signature,
Expand All @@ -118,9 +120,6 @@ def build_crash_trade_result(
}
trade_result.raw_transaction = exception.raw_txn
else:
# Best effort to get the block it crashed on
# We assume the exception happened in the previous block
trade_result.block_number = current_block_number - 1
# We still build this structure so the schema stays the same
trade_result.contract_call = {
"contract_call_type": None,
Expand All @@ -132,8 +131,15 @@ def build_crash_trade_result(
# We trust the caller to provide the correct pool state if it's being passed in.
if pool_state is None:
# Get the pool state at the desired block number
# If the exception's block identifier is "pending", we need to get the state using a mined block
# so we grab the latest
if crash_block_identifier == "pending":
query_block_id = "latest"
else:
query_block_id = trade_result.block_number

try:
pool_state = interface.get_hyperdrive_state(interface.get_block(trade_result.block_number))
pool_state = interface.get_hyperdrive_state(interface.get_block(query_block_id))
except Exception: # pylint: disable=broad-except
pass

Expand All @@ -156,8 +162,8 @@ def build_crash_trade_result(
trade_result.block_timestamp = pool_state.block.get("timestamp", None)
if trade_result.raw_pool_info is not None and trade_result.block_timestamp is not None:
trade_result.pool_info = asdict(pool_state.pool_info)
trade_result.pool_info["timestamp"] = datetime.utcfromtimestamp(trade_result.block_timestamp)
trade_result.pool_info["block_number"] = trade_result.block_number
trade_result.pool_info["timestamp"] = datetime.fromtimestamp(pool_state.block_time, tz=timezone.utc)
trade_result.pool_info["block_number"] = pool_state.block_number
trade_result.pool_info["total_supply_withdrawal_shares"] = pool_state.total_supply_withdrawal_shares
else:
trade_result.pool_info = None
Expand Down
19 changes: 3 additions & 16 deletions src/agent0/core/hyperdrive/crash_report/known_error_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,22 +126,9 @@ def check_for_invalid_balance(trade_result: TradeResult, interface: HyperdriveRe
)

case HyperdriveActionType.REDEEM_WITHDRAW_SHARE:
# If we're crash reporting, pool_info should exist
assert trade_result.pool_info is not None
ready_to_withdraw = trade_result.pool_info["withdrawal_shares_ready_to_withdraw"]
if trade_amount > wallet.withdraw_shares:
invalid_balance = True
add_arg = (
f"Invalid balance: {trade_type.name} for {trade_amount} withdraw shares, "
f"balance of {wallet.withdraw_shares} withdraw shares."
)
# Also checking that there are enough withdrawal shares ready to withdraw
elif trade_amount > ready_to_withdraw:
invalid_balance = True
add_arg = (
f"Invalid balance: {trade_type.name} for {trade_amount} withdraw shares, "
f"not enough ready to withdraw shares in pool ({ready_to_withdraw})."
)
# We can't check if the user has enough withdraw shares to redeem
# since the contract function clamps to however much is ready to withdraw.
pass

case _:
assert_never(trade_type)
Expand Down
5 changes: 3 additions & 2 deletions src/agent0/ethpy/base/errors/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from enum import Enum
from typing import Any

from eth_typing import BlockIdentifier
from eth_utils.conversions import to_hex
from eth_utils.crypto import keccak
from web3.contract.contract import Contract
Expand Down Expand Up @@ -36,15 +37,15 @@ def __init__(
fn_args: tuple | None = None,
fn_kwargs: dict[str, Any] | None = None,
raw_txn: dict[str, Any] | None = None,
block_number: int | None = None,
block_identifier: BlockIdentifier | None = None,
):
super().__init__(*args)
self.orig_exception = orig_exception
self.contract_call_type = contract_call_type
self.function_name_or_signature = function_name_or_signature
self.fn_args = fn_args
self.fn_kwargs = fn_kwargs
self.block_number = block_number
self.block_identifier: BlockIdentifier | None = block_identifier
self.raw_txn = raw_txn

def __repr__(self):
Expand Down
30 changes: 16 additions & 14 deletions src/agent0/ethpy/base/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Any, Sequence

from eth_account.signers.local import LocalAccount
from eth_typing import BlockNumber, ChecksumAddress
from eth_typing import BlockIdentifier, BlockNumber, ChecksumAddress
from hexbytes import HexBytes
from web3 import Web3
from web3._utils.threads import Timeout
Expand Down Expand Up @@ -113,7 +113,7 @@ def smart_contract_read(
function_name_or_signature=function_name_or_signature,
fn_args=fn_args,
fn_kwargs=fn_kwargs,
block_number=block_number,
block_identifier=block_number,
) from err

# If there is a single value returned, we want to put it in a list of length 1
Expand Down Expand Up @@ -149,7 +149,7 @@ def smart_contract_preview_transaction(
signer_address: ChecksumAddress,
function_name_or_signature: str,
*fn_args,
block_number: BlockNumber | None = None,
block_identifier: BlockIdentifier | None = None,
read_retry_count: int | None = None,
txn_options_value: int | None = None,
nonce: int | None = None,
Expand All @@ -167,8 +167,8 @@ def smart_contract_preview_transaction(
The name of the function
*fn_args: Unknown
The arguments passed to the contract method.
block_number: BlockNumber | None
If set, will query the chain on the specified block
block_identifier: BlockIdentifier | None, optional
If set, will query the chain on the specified block. Defaults to the `pending` block.
read_retry_count: int | None
The number of times to retry the read call if it fails. Defaults to 5.
txn_options_value: int | None
Expand All @@ -191,6 +191,8 @@ def smart_contract_preview_transaction(
"""
if read_retry_count is None:
read_retry_count = DEFAULT_READ_RETRY_COUNT
if block_identifier is None:
block_identifier = "pending"

# get the callable contract function from function_name & call it
if "(" in function_name_or_signature:
Expand Down Expand Up @@ -226,7 +228,7 @@ def smart_contract_preview_transaction(
_retry_preview_check,
function.call,
transaction_kwargs,
block_identifier=block_number,
block_identifier=block_identifier,
)
# Wraps the exception with a contract call exception, adding additional information
# If block number is set in the preview call, will add to crash report,
Expand All @@ -241,7 +243,7 @@ def smart_contract_preview_transaction(
fn_args=fn_args,
fn_kwargs=fn_kwargs,
raw_txn=dict(raw_txn),
block_number=block_number,
block_identifier=block_identifier,
) from err
except Exception as err:
raise ContractCallException(
Expand All @@ -252,7 +254,7 @@ def smart_contract_preview_transaction(
fn_args=fn_args,
fn_kwargs=fn_kwargs,
raw_txn=dict(raw_txn),
block_number=block_number,
block_identifier=block_identifier,
) from err

if not isinstance(return_values, Sequence): # could be list or tuple
Expand Down Expand Up @@ -647,7 +649,7 @@ async def _async_build_send_and_wait():
fn_args=fn_args,
fn_kwargs=fn_kwargs,
raw_txn=dict(unsent_txn),
block_number=block_number,
block_identifier=block_number,
) from err
except UnknownBlockError as err:
# Unknown block error means the transaction went through, but was rejected
Expand All @@ -664,7 +666,7 @@ async def _async_build_send_and_wait():
signer.address,
function_name_or_signature,
*fn_args,
block_number=BlockNumber(block_number),
block_identifier=BlockNumber(block_number),
read_retry_count=1, # No retries for this preview
**fn_kwargs,
)
Expand All @@ -685,7 +687,7 @@ async def _async_build_send_and_wait():
fn_args=fn_args,
fn_kwargs=fn_kwargs,
raw_txn=dict(unsent_txn),
block_number=block_number,
block_identifier=block_number,
) from err
except Exception as err:
# Race condition here, other transactions may have happened when we get the block number here
Expand All @@ -699,7 +701,7 @@ async def _async_build_send_and_wait():
fn_args=fn_args,
fn_kwargs=fn_kwargs,
raw_txn=dict(unsent_txn),
block_number=block_number,
block_identifier=block_number,
) from err


Expand Down Expand Up @@ -890,7 +892,7 @@ def _build_send_and_wait():
signer.address,
function_name_or_signature,
*fn_args,
block_number=BlockNumber(block_number),
block_identifier=BlockNumber(block_number),
read_retry_count=1, # No retries for this preview
**fn_kwargs,
)
Expand All @@ -911,7 +913,7 @@ def _build_send_and_wait():
fn_args=fn_args,
fn_kwargs=fn_kwargs,
raw_txn=dict(unsent_txn),
block_number=block_number,
block_identifier=block_number,
) from err
except Exception as err:
raise ContractCallException(
Expand Down
Loading

0 comments on commit 82149d1

Please sign in to comment.