1919from eth_typing import ChecksumAddress , HexStr
2020from web3 import AsyncWeb3 , WebSocketProvider
2121from web3 .contract import AsyncContract
22- from web3 .exceptions import ProviderConnectionError
22+ from web3 .exceptions import ProviderConnectionError , Web3RPCError
2323from web3 .method import Method , default_root_munger
2424from web3 .middleware import SignAndSendRawMiddlewareBuilder
25- from web3 .types import LogReceipt , RPCEndpoint , TxReceipt
25+ from web3 .types import LogReceipt , RPCEndpoint , TxParams , TxReceipt , Wei
2626from web3 .utils .subscriptions import (
2727 LogsSubscription ,
2828 LogsSubscriptionContext ,
7575 "STORAGE_ADDRESS" ,
7676 # Exports from this file
7777 "GolemBaseClient" ,
78+ # Re-exports
79+ "Wei" ,
7880]
7981
8082
@@ -381,37 +383,85 @@ async def query_entities(self, query: str) -> Sequence[QueryEntitiesResult]:
381383 async def create_entities (
382384 self ,
383385 creates : Sequence [GolemBaseCreate ],
386+ * ,
387+ gas : int | None = None ,
388+ maxFeePerGas : Wei | None = None ,
389+ maxPriorityFeePerGas : Wei | None = None ,
384390 ) -> Sequence [CreateEntityReturnType ]:
385391 """Create entities in Golem Base."""
386- return (await self .send_transaction (creates = creates )).creates
392+ return (
393+ await self .send_transaction (
394+ creates = creates ,
395+ gas = gas ,
396+ maxFeePerGas = maxFeePerGas ,
397+ maxPriorityFeePerGas = maxPriorityFeePerGas ,
398+ )
399+ ).creates
387400
388401 async def update_entities (
389402 self ,
390403 updates : Sequence [GolemBaseUpdate ],
404+ * ,
405+ gas : int | None = None ,
406+ maxFeePerGas : Wei | None = None ,
407+ maxPriorityFeePerGas : Wei | None = None ,
391408 ) -> Sequence [UpdateEntityReturnType ]:
392409 """Update entities in Golem Base."""
393- return (await self .send_transaction (updates = updates )).updates
410+ return (
411+ await self .send_transaction (
412+ updates = updates ,
413+ gas = gas ,
414+ maxFeePerGas = maxFeePerGas ,
415+ maxPriorityFeePerGas = maxPriorityFeePerGas ,
416+ )
417+ ).updates
394418
395419 async def delete_entities (
396420 self ,
397421 deletes : Sequence [GolemBaseDelete ],
422+ * ,
423+ gas : int | None = None ,
424+ maxFeePerGas : Wei | None = None ,
425+ maxPriorityFeePerGas : Wei | None = None ,
398426 ) -> Sequence [EntityKey ]:
399427 """Delete entities from Golem Base."""
400- return (await self .send_transaction (deletes = deletes )).deletes
428+ return (
429+ await self .send_transaction (
430+ deletes = deletes ,
431+ gas = gas ,
432+ maxFeePerGas = maxFeePerGas ,
433+ maxPriorityFeePerGas = maxPriorityFeePerGas ,
434+ )
435+ ).deletes
401436
402437 async def extend_entities (
403438 self ,
404439 extensions : Sequence [GolemBaseExtend ],
440+ * ,
441+ gas : int | None = None ,
442+ maxFeePerGas : Wei | None = None ,
443+ maxPriorityFeePerGas : Wei | None = None ,
405444 ) -> Sequence [ExtendEntityReturnType ]:
406445 """Extend the BTL of entities in Golem Base."""
407- return (await self .send_transaction (extensions = extensions )).extensions
446+ return (
447+ await self .send_transaction (
448+ extensions = extensions ,
449+ gas = gas ,
450+ maxFeePerGas = maxFeePerGas ,
451+ maxPriorityFeePerGas = maxPriorityFeePerGas ,
452+ )
453+ ).extensions
408454
409455 async def send_transaction (
410456 self ,
457+ * ,
411458 creates : Sequence [GolemBaseCreate ] | None = None ,
412459 updates : Sequence [GolemBaseUpdate ] | None = None ,
413460 deletes : Sequence [GolemBaseDelete ] | None = None ,
414461 extensions : Sequence [GolemBaseExtend ] | None = None ,
462+ gas : int | None = None ,
463+ maxFeePerGas : Wei | None = None ,
464+ maxPriorityFeePerGas : Wei | None = None ,
415465 ) -> GolemBaseTransactionReceipt :
416466 """
417467 Send a generic transaction to Golem Base.
@@ -420,10 +470,13 @@ async def send_transaction(
420470 extend operations.
421471 """
422472 tx = GolemBaseTransaction (
423- creates ,
424- updates ,
425- deletes ,
426- extensions ,
473+ creates = creates ,
474+ updates = updates ,
475+ deletes = deletes ,
476+ extensions = extensions ,
477+ gas = gas ,
478+ maxFeePerGas = maxFeePerGas ,
479+ maxPriorityFeePerGas = maxPriorityFeePerGas ,
427480 )
428481 return await self ._send_gb_transaction (tx )
429482
@@ -541,16 +594,49 @@ async def process_receipt(
541594 async def _send_gb_transaction (
542595 self , tx : GolemBaseTransaction
543596 ) -> GolemBaseTransactionReceipt :
544- txhash = await self .http_client ().eth .send_transaction (
545- {
546- # https://github.com/pylint-dev/pylint/issues/3162
547- # pylint: disable=no-member
548- "to" : STORAGE_ADDRESS .as_address (),
549- "value" : AsyncWeb3 .to_wei (0 , "ether" ),
550- "data" : rlp_encode_transaction (tx ),
551- }
552- )
597+ txData : TxParams = {
598+ # https://github.com/pylint-dev/pylint/issues/3162
599+ # pylint: disable=no-member
600+ "to" : STORAGE_ADDRESS .as_address (),
601+ "value" : AsyncWeb3 .to_wei (0 , "ether" ),
602+ "data" : rlp_encode_transaction (tx ),
603+ }
604+
605+ if tx .gas :
606+ txData |= {"gas" : tx .gas }
607+ if tx .maxFeePerGas :
608+ txData |= {"maxFeePerGas" : tx .maxFeePerGas }
609+ if tx .maxPriorityFeePerGas :
610+ txData |= {"maxPriorityFeePerGas" : tx .maxPriorityFeePerGas }
611+
612+ txhash = await self .http_client ().eth .send_transaction (txData )
553613 receipt = await self .http_client ().eth .wait_for_transaction_receipt (txhash )
614+
615+ # If we get a receipt and the transaction was failed, we run the same
616+ # transaction with eth_call, which will simulate it and get us back the
617+ # error that was reported by geth.
618+ # Otherwise the error is not actually present in the receipt and so we
619+ # don't have something useful to present to the user.
620+ # This only happens when the gas price was explicitly provided, since
621+ # otherwise there will be a call to eth_estimateGas, which will fail with
622+ # the same error message that we would get here (and so we'll never actually
623+ # get to submitting the transaction).
624+ # The status in the receipt is either 0x0 for failed or 0x1 for success.
625+ if not int (receipt ["status" ]):
626+ # This call will lead to an exception, but that's OK, what we want
627+ # is to raise a useful exception to the user with an error message.
628+ try :
629+ await self .http_client ().eth .call (txData )
630+ except Web3RPCError as e :
631+ if e .rpc_response :
632+ raise Exception (
633+ f"Error while processing transaction: {
634+ e .rpc_response ['error' ]['message' ]
635+ } "
636+ ) from e
637+ else :
638+ raise e
639+
554640 return await self ._process_golem_base_receipt (receipt )
555641
556642 async def watch_logs (
0 commit comments