Compare commits

1 Commits
gmx ... master

Author SHA1 Message Date
tim
4936150c3b bugfixes; startall works 2025-12-09 2025-12-09 15:11:58 -04:00
3 changed files with 31 additions and 9 deletions

View File

@@ -42,10 +42,12 @@ class Account (LocalAccount):
# log.debug(f'available accounts: {Account._pool.qsize()}')
try:
async with asyncio.timeout(1):
result = await Account._pool.get()
result: "Account" = await Account._pool.get()
except asyncio.TimeoutError:
log.error('waiting for an available account')
result = await Account._pool.get()
# mark as out of pool
result._in_pool = False
metric.account_available.set(Account._pool.qsize())
return result
@@ -59,17 +61,20 @@ class Account (LocalAccount):
if Account._main_account is None:
Account._main_account = account
Account._pool.put_nowait(account)
account._in_pool = True # this account is now in the pool
Account._all.append(account)
metric.account_available.set(Account._pool.qsize())
metric.account_total.set(len(Account._all))
log.info(f'Account pool {[a.address for a in Account._all]}')
def __init__(self, local_account: LocalAccount): # todo chain_id?
super().__init__(local_account._key_obj, local_account._publicapi) # from digging into the source code
def __init__(self, local_account: LocalAccount): # todo chain_id?
super().__init__(local_account._key_obj, local_account._publicapi) # from digging into the source code
self.chain_id = current_chain.get().id
self.signing_middleware = construct_sign_and_send_raw_middleware(self)
self._nonce: Optional[int] = None
self.tx_id: Optional[str] = None # current transaction id
# release() idempotency tracking
self._in_pool: bool = False
async def next_nonce(self):
if self._nonce is None:
@@ -86,8 +91,21 @@ class Account (LocalAccount):
return current_w3.get().eth.get_balance(self.address)
def release(self):
metric.account_available.set(Account._pool.qsize() + 1)
"""
Return this Account to the pool.
Idempotent: calling release() multiple times without a new acquire()
will only enqueue the account once.
"""
# If we're already in the pool, do nothing.
if self._in_pool:
# Optional debug log; comment out if too noisy.
# log.debug(f'Account {self.address} already in pool; ignoring extra release()')
return
Account._pool.put_nowait(self)
self._in_pool = True
metric.account_available.set(Account._pool.qsize())
def __str__(self):
return self.address

View File

@@ -33,7 +33,8 @@ class ContractTransaction:
async def wait(self) -> TxReceipt:
if self.receipt is None:
self.receipt = await current_w3.get().eth.wait_for_transaction_receipt(self.id)
self.account.release()
if self.account is not None:
self.account.release()
return self.receipt
async def sign(self, account: Account):
@@ -153,10 +154,14 @@ class ContractProxy:
def __getattr__(self, item):
if item == 'constructor':
found = self.contract.constructor
elif item in self.contract.functions:
found = self.contract.functions[item]
else:
raise AttributeError(item)
funcs = self.contract.functions
# In web3.py v6+, contract functions are exposed as attributes, not via __getitem__.
# Using getattr ensures we obtain the callable factory for the function; indexing may return None.
# Additionally, guard against unexpected None to fail fast with a clear error.
found = getattr(funcs, item, None)
if not callable(found):
raise AttributeError(f"Function '{item}' not found on contract {self._interface_name} at {self.address}")
return self._wrapper(self.address, item, found)
def __repr__(self):

View File

@@ -3,7 +3,6 @@ from dataclasses import dataclass
from typing import Optional, Union, Any
from uuid import UUID
from triton.profiler import deactivate
from web3.exceptions import ContractPanicError, ContractLogicError
from web3.types import EventData