Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verbose tx logging #167

Merged
merged 5 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Current
# 0.22.24

- Internal change: more verbose logging for `wait_and_broadcast_multiple_nodes`

# 0.22.23

- API change: add `fetch_erc20_balances_by_token_list(block_identifier)`

Expand Down
9 changes: 8 additions & 1 deletion contracts/in-house/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ It's unlikely you want to use any of these contracts directly.
## Compile

```shell
# Need to fetch OpenZeppelin version we depend on
(cd contracts/enzyme && pnpm install)

# Proceed with our own project
cd contracts/in-house
forge build
```
Expand All @@ -30,7 +34,10 @@ and gasless transactions.
- Then deploy the USDC payment relay

```shell
# Address of deployed vault comptroller
# Address of deployed vault comptroller contract for the vault contract
#
# You can get this from vault.getAccessor() call
#
export VAULT_COMPTROLLER=

# Deployer account
Expand Down
3 changes: 3 additions & 0 deletions contracts/in-house/src/VaultUSDCPaymentForwarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ contract VaultUSDCPaymentForwarder {
IEIP3009 public token;

// The comptroller of the vault for which we are buying shares
//
// You can get this from vault by asking getAccessor()
//
IEnzymeComptroller public comptroller;

// Total USDC that has passed through this contract
Expand Down
8 changes: 4 additions & 4 deletions eth_defi/balances.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ def fetch_erc20_balances_by_transfer_event(


def fetch_erc20_balances_by_token_list(
web3: Web3,
owner: HexAddress,
tokens: Set[HexAddress],
block_identifier: BlockIdentifier = None,
web3: Web3,
owner: HexAddress,
tokens: Set[HexAddress],
block_identifier: BlockIdentifier = None,
) -> Dict[HexAddress, int]:
"""Get all current holdings of an account for a limited set of ERC-20 tokens.

Expand Down
34 changes: 15 additions & 19 deletions eth_defi/confirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,20 +305,21 @@ def broadcast_and_wait_transactions_to_complete(
return receipts


def _broadcast_multiple_nodes(providers: Collection[BaseProvider], signed_tx: SignedTransaction):
# Support different raw tx formats
SignedTxType = Union[SignedTransaction, SignedTransactionWithNonce]


def _broadcast_multiple_nodes(providers: Collection[BaseProvider], signed_tx: SignedTxType):
"""Attempt to broadcast a transaction through multiple provideres."""

for p in providers:

# See SignedTransactionWithNonce
nonce = getattr(signed_tx, "nonce", None)
address = getattr(signed_tx, "address", None)
source = getattr(signed_tx, "source", None)

name = get_provider_name(p)
logger.info(
"Broadcasting %s through %s",
signed_tx.hash,
name
)
logger.info("Broadcasting %s through %s", signed_tx.hash.hex(), name)

# Does not use any middleware
web3 = Web3(p)
Expand All @@ -327,27 +328,23 @@ def _broadcast_multiple_nodes(providers: Collection[BaseProvider], signed_tx: Si
except ValueError as e:
resp_data: dict = e.args[0]

logger.info("Broadcast JSON-RPC error %s from: %s, nonce: %s on provider: %s, got error: %s\n", signed_tx.hash.hex(), address, nonce, name, resp_data)
logger.info("Signed tx: %s", signed_tx)
logger.info("Source: %s", source)

# When we rebroadcast we are getting nonce too low errors,
# both for too high and too low nonces
if resp_data["message"] == "nonce too low":
continue

except Exception as e:
logger.warning("Provider %s failed with tx %s from address %s, nonce %s",
name,
signed_tx.hash.hex(),
address,
nonce
)
logger.warning("Provider %s failed with tx %s from address %s, nonce %s", name, signed_tx.hash.hex(), address, nonce)
logger.exception(e)


# Support different raw tx formats
TxType = Union[SignedTransaction, SignedTransactionWithNonce]


def wait_and_broadcast_multiple_nodes(
web3: Web3,
txs: Collection[TxType],
txs: Collection[SignedTxType],
confirmation_block_count: int = 0,
max_timeout=datetime.timedelta(minutes=5),
poll_delay=datetime.timedelta(seconds=1),
Expand Down Expand Up @@ -446,7 +443,6 @@ def wait_and_broadcast_multiple_nodes(
tx_log_level = logging.DEBUG

for tx_hash in unconfirmed_txs:

try:
receipt = web3.eth.get_transaction_receipt(tx_hash)
except TransactionNotFound as e:
Expand Down
32 changes: 28 additions & 4 deletions eth_defi/hotwallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,50 @@


class SignedTransactionWithNonce(NamedTuple):
"""Helper class to pass around the used nonce when signing txs from the wallet.
"""A better signed transaction structure.

- Emulate `SignedTransaction` from web3.py package
Helper class to pass around the used nonce when signing txs from the wallet.

- Compatible with :py:class:`eth_accounts.datastructures.SignedTransaction`. Emulates its behavior
and should be backwards compatible.

- Retains more information about the transaction source,
to allow us to diagnose broadcasting failures better

- Add some debugging helpers
"""

#: See SignedTransaction
rawTransaction: HexBytes

#: See SignedTransaction
hash: HexBytes

#: See SignedTransaction
r: int

#: See SignedTransaction
s: int

#: See SignedTransaction
v: int

#: What was the source nonce for this transaction
nonce: int

#: Whas was the source address for this trasaction
address: str

#: Undecoded transaction data as a dict.
#: Unencoded transaction data as a dict.
#:
#: If broadcast fails, retain the source so we can debug the cause,
#: like the original gas parameters.
#:
#: If broadcast fails, retain the source so we can debug the cause
source: Optional[dict] = None

def __repr__(self):
return f"<SignedTransactionWithNonce hash:{self.hash.hex()} payload:{self.rawTransaction.hex()}>"

@property
def raw_transaction(self) -> HexBytes:
"""Get the bytes to be broadcasted to the P2P network."""
Expand Down
23 changes: 3 additions & 20 deletions eth_defi/provider/fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,7 @@ def switch_provider(self):
old_provider_name = get_provider_name(provider)
self.currently_active_provider = (self.currently_active_provider + 1) % len(self.providers)
new_provider_name = get_provider_name(self.get_active_provider())
logger.log(
self.switchover_noisiness,
"Switched RPC providers %s -> %s\n",
old_provider_name,
new_provider_name
)
logger.log(self.switchover_noisiness, "Switched RPC providers %s -> %s\n", old_provider_name, new_provider_name)

def get_active_provider(self) -> NamedProvider:
"""Get currently active provider.
Expand Down Expand Up @@ -191,7 +186,6 @@ def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
return resp_data

except Exception as e:

if is_retryable_http_exception(
e,
retryable_rpc_error_codes=self.retryable_rpc_error_codes,
Expand All @@ -203,18 +197,7 @@ def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
if i < self.retries:
# Black messes up string new lines here
# See https://github.com/psf/black/issues/1837
logger.log(
self.switchover_noisiness,
"Encountered JSON-RPC retryable error %s when calling method:\n"
"%s(%s)\n "
"Retrying in %f seconds, retry #%d / %d",
e,
method,
params,
current_sleep,
i,
self.retries
)
logger.log(self.switchover_noisiness, "Encountered JSON-RPC retryable error %s when calling method:\n" "%s(%s)\n " "Retrying in %f seconds, retry #%d / %d", e, method, params, current_sleep, i, self.retries)
time.sleep(current_sleep)
current_sleep *= self.backoff
self.retry_count += 1
Expand Down Expand Up @@ -299,4 +282,4 @@ def get_fallback_provider(web3: Web3) -> FallbackProvider:
if call_provider:
return cast(FallbackProvider, call_provider)

raise AssertionError(f"Does not know how fallback provider is configured: {[provider]}")
raise AssertionError(f"Does not know how fallback provider is configured: {[provider]}")
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "web3-ethereum-defi"
version = "0.22.22"
version = "0.22.23"
description = "Python library for Uniswap, Aave, ChainLink, Enzyme and other protocols on BNB Chain, Polygon, Ethereum and other blockchains"
authors = ["Mikko Ohtamaa <[email protected]>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion tests/rpc/test_fallback_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,4 @@ def test_broadcast_and_wait_multiple(web3: Web3, deployer: str):
)

assert signed_tx2.hash in receipt_map
assert signed_tx3.hash in receipt_map
assert signed_tx3.hash in receipt_map