From 464e216cb94ef69860bc4b1f8379cad0932b4b46 Mon Sep 17 00:00:00 2001 From: AMATH <116212274+amathxbt@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:36:48 +0100 Subject: [PATCH 1/2] fix(alpha): add missing chainId to _send_tx_with_revert_handling All other build_transaction() calls in alpha.py include chainId, but _send_tx_with_revert_handling (used by infer()) was missing it. Without chainId, web3.py signs transactions without EIP-155 replay protection, causing them to be rejected on OpenGradient's network. --- src/opengradient/client/alpha.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opengradient/client/alpha.py b/src/opengradient/client/alpha.py index a4e633ec..da359691 100644 --- a/src/opengradient/client/alpha.py +++ b/src/opengradient/client/alpha.py @@ -163,6 +163,7 @@ def _send_tx_with_revert_handling(self, run_function): "nonce": nonce, "gas": gas_limit, "gasPrice": self._blockchain.eth.gas_price, + "chainId": self._blockchain.eth.chain_id, } ) From 6f8334680af8d0f21d78237967f35048b8ae7a55 Mon Sep 17 00:00:00 2001 From: AMATH <116212274+amathxbt@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:36:59 +0100 Subject: [PATCH 2/2] test(alpha): add test verifying chainId is set in inference transaction Adds TestAlphaInferenceChainId.test_send_tx_includes_chain_id to guard against regression of the missing chainId bug in _send_tx_with_revert_handling. --- tests/client_test.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/client_test.py b/tests/client_test.py index 6829fc98..39adb175 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -190,3 +190,48 @@ def test_settlement_modes_values(self): assert x402SettlementMode.PRIVATE == "private" assert x402SettlementMode.BATCH_HASHED == "batch" assert x402SettlementMode.INDIVIDUAL_FULL == "individual" + + +class TestAlphaInferenceChainId: + """Verify that _send_tx_with_revert_handling includes chainId in build_transaction. + + Without chainId, web3.py signs transactions without EIP-155 replay protection, + causing them to be rejected on OpenGradient's network (chain_id != 0 networks). + """ + + def test_send_tx_includes_chain_id(self, mock_web3): + """chainId must be present in the transaction built by _send_tx_with_revert_handling.""" + from opengradient.client.alpha import Alpha + + mock_web3.eth.chain_id = 12345 + + alpha = Alpha(private_key="0x" + "a" * 64) + + # Track build_transaction calls + built_transactions = [] + + mock_run_function = MagicMock() + mock_run_function.estimate_gas.return_value = 100000 + + def capture_build_transaction(tx_params): + built_transactions.append(tx_params) + return {"from": tx_params["from"], "nonce": 0, "gas": tx_params["gas"]} + + mock_run_function.build_transaction.side_effect = capture_build_transaction + mock_web3.eth.account.sign_transaction.return_value = MagicMock(raw_transaction=b"raw") + mock_web3.eth.send_raw_transaction.return_value = b"txhash" + mock_web3.eth.wait_for_transaction_receipt.return_value = {"status": 1} + + alpha._send_tx_with_revert_handling(mock_run_function) + + assert len(built_transactions) == 1, "Expected exactly one build_transaction call" + tx_params = built_transactions[0] + assert "chainId" in tx_params, ( + "_send_tx_with_revert_handling is missing chainId in build_transaction. " + "Without chainId, transactions are signed without EIP-155 replay protection " + "and will be rejected on OpenGradient's network." + ) + assert tx_params["chainId"] == 12345, ( + f"Expected chainId=12345, got chainId={tx_params.get('chainId')}" + ) +