Skip to content

Commit 95a9228

Browse files
authored
Merge pull request #14 from Golem-Base/rvdp/dev
feat: rerun failed tx with eth_call to improve error messages
2 parents c277938 + 1db19ff commit 95a9228

2 files changed

Lines changed: 116 additions & 19 deletions

File tree

golem_base_sdk/__init__.py

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
from eth_typing import ChecksumAddress, HexStr
2020
from web3 import AsyncWeb3, WebSocketProvider
2121
from web3.contract import AsyncContract
22-
from web3.exceptions import ProviderConnectionError
22+
from web3.exceptions import ProviderConnectionError, Web3RPCError
2323
from web3.method import Method, default_root_munger
2424
from web3.middleware import SignAndSendRawMiddlewareBuilder
25-
from web3.types import LogReceipt, RPCEndpoint, TxReceipt
25+
from web3.types import LogReceipt, RPCEndpoint, TxParams, TxReceipt, Wei
2626
from web3.utils.subscriptions import (
2727
LogsSubscription,
2828
LogsSubscriptionContext,
@@ -75,6 +75,8 @@
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(

golem_base_sdk/types.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from eth_typing import ChecksumAddress, HexStr
1212
from web3 import AsyncWeb3
13+
from web3.types import Wei
1314

1415

1516
@dataclass(frozen=True)
@@ -106,21 +107,31 @@ class GolemBaseTransaction:
106107

107108
def __init__(
108109
self,
110+
*,
109111
creates: Sequence[GolemBaseCreate] | None = None,
110112
updates: Sequence[GolemBaseUpdate] | None = None,
111113
deletes: Sequence[GolemBaseDelete] | None = None,
112114
extensions: Sequence[GolemBaseExtend] | None = None,
115+
gas: int | None = None,
116+
maxFeePerGas: Wei | None = None,
117+
maxPriorityFeePerGas: Wei | None = None,
113118
):
114119
"""Initialise the GolemBaseTransaction instance."""
115120
object.__setattr__(self, "creates", creates or [])
116121
object.__setattr__(self, "updates", updates or [])
117122
object.__setattr__(self, "deletes", deletes or [])
118123
object.__setattr__(self, "extensions", extensions or [])
124+
object.__setattr__(self, "gas", gas)
125+
object.__setattr__(self, "maxFeePerGas", maxFeePerGas)
126+
object.__setattr__(self, "maxPriorityFeePerGas", maxPriorityFeePerGas)
119127

120128
creates: Sequence[GolemBaseCreate]
121129
updates: Sequence[GolemBaseUpdate]
122130
deletes: Sequence[GolemBaseDelete]
123131
extensions: Sequence[GolemBaseExtend]
132+
gas: int | None
133+
maxFeePerGas: Wei | None
134+
maxPriorityFeePerGas: Wei | None
124135

125136

126137
@dataclass(frozen=True)

0 commit comments

Comments
 (0)