From e9134673193944e1e6785017d323816c8d9d67f2 Mon Sep 17 00:00:00 2001 From: KENDAL Date: Mon, 24 Aug 2026 11:53:29 +0300 Subject: [PATCH] fix(models): coerce sender and key_id to checksummed address hex in to_estimate_gas_request and support dict in _normalize_arg --- pytempo/contracts/_encode.py | 2 ++ pytempo/models.py | 24 ++++++++++++++++++------ tests/test_typed_models.py | 13 +++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pytempo/contracts/_encode.py b/pytempo/contracts/_encode.py index a2d0ebd..31da298 100644 --- a/pytempo/contracts/_encode.py +++ b/pytempo/contracts/_encode.py @@ -22,6 +22,8 @@ def _normalize_arg(value: object) -> object: """Auto-checksum bare hex addresses so callers don't have to.""" if isinstance(value, str) and is_hex_address(value): return to_checksum_address(value) + if isinstance(value, dict): + return {k: _normalize_arg(v) for k, v in value.items()} if isinstance(value, (list, tuple)): return type(value)(_normalize_arg(v) for v in value) return value diff --git a/pytempo/models.py b/pytempo/models.py index e4f22d3..15cee60 100644 --- a/pytempo/models.py +++ b/pytempo/models.py @@ -7,7 +7,7 @@ import attrs import rlp from eth_account import Account -from eth_utils import keccak, to_checksum_address +from eth_utils import is_hex_address, keccak, to_checksum_address from .types import ( Address, @@ -556,14 +556,14 @@ def sign_access_key( def to_estimate_gas_request( self, - sender: str, - key_id: str | None = None, + sender: BytesLike, + key_id: BytesLike | None = None, key_authorization: dict | SignedKeyAuthorization | None = None, ) -> dict: """Build an eth_estimateGas request dict from this transaction. Args: - sender: Address of the sender (hex string). + sender: Address of the sender (hex string or bytes). key_id: Optional access key address for keychain signature gas estimation. key_authorization: Optional :class:`SignedKeyAuthorization` or pre-built JSON dict. @@ -578,8 +578,14 @@ def to_estimate_gas_request( data_hex = "0x" + data.hex() if data else "0x" + from_addr = ( + to_checksum_address(bytes(sender)) + if isinstance(sender, (bytes, bytearray, memoryview)) + else to_checksum_address(sender) + ) + request: dict = { - "from": sender, + "from": from_addr, "data": data_hex, } @@ -590,7 +596,13 @@ def to_estimate_gas_request( request["value"] = hex(value) if key_id is not None: - request["keyId"] = key_id + request["keyId"] = ( + to_checksum_address(bytes(key_id)) + if isinstance(key_id, (bytes, bytearray, memoryview)) + else to_checksum_address(key_id) + if is_hex_address(key_id) + else key_id + ) if key_authorization is not None: if isinstance(key_authorization, dict): diff --git a/tests/test_typed_models.py b/tests/test_typed_models.py index ea2c999..425927a 100644 --- a/tests/test_typed_models.py +++ b/tests/test_typed_models.py @@ -393,3 +393,16 @@ def test_immutability_preserved(self): assert tx1.gas_limit == 21000 assert tx2.gas_limit == 200000 assert tx1 is not tx2 + + def test_to_estimate_gas_request_with_address_and_bytes(self): + tx = TempoTransaction.create( + chain_id=42429, + calls=(Call.create(to="0xF0109fC8DF283027b6285cc889F5aA624EaC1F55", value=100),), + ) + sender_bytes = as_address("0xF0109fC8DF283027b6285cc889F5aA624EaC1F55") + req = tx.to_estimate_gas_request(sender=sender_bytes, key_id=sender_bytes) + + assert req["from"] == "0xF0109fC8DF283027b6285cc889F5aA624EaC1F55" + assert req["to"] == "0xF0109fC8DF283027b6285cc889F5aA624EaC1F55" + assert req["keyId"] == "0xF0109fC8DF283027b6285cc889F5aA624EaC1F55" + assert req["value"] == hex(100)