Skip to content

Conversation

10gic
Copy link
Contributor

@10gic 10gic commented Sep 21, 2025

Currently, we can get transaction receipts from the HTTP API eth_getTransactionReceipt. In this PR, I add support for getting transaction receipts from the WebSocket API: Add a new subscription_name: transactionReceipts, which accepts an optional transaction hashes filter.

Why is this PR valuable?

  1. It provides users with faster access to transaction receipts than the HTTP API.
  2. It saves many HTTP RPC calls. Some libraries, for example waitForTransactionReceipt, need to monitor transaction and must use polling, which wastes a lot of RPC calls.

Usage:
Subscribe to a transaction receipt by transaction hash (multiple transaction hashes are also supported):

# Subscribe (WebSocket client --> WebSocket server):
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "eth_subscribe",
	"params": ["transactionReceipts", {
		"transactionHashes": [
			"0xffc4978dfe7ab496f0158ae8916adae6ffd0c1fca4f09f7a7134556011357424"
		]
	}]
}

# Notification (WebSocket server --> WebSocket client):
# Data format is the same as eth_getTransactionReceipt
{
    "jsonrpc": "2.0",
    "method": "eth_subscription",
    "params": {
        "subscription": "0xf9a92ce3f834c5f23100f4a9f6b8ddaf",
        "result": [
            {
                "blockHash": "0x53a6208ec0efcc8de6380eedc8c34f8dda0fc8156de94281f5539576e929b1be",
                "blockNumber": "0x2",
                "contractAddress": null,
                "cumulativeGasUsed": "0x5208",
                "effectiveGasPrice": "0x3b9aca00",
                "from": "0x71562b71999873db5b286df957af199ec94617f7",
                "gasUsed": "0x5208",
                "logs": [],
                "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
                "status": "0x1",
                "to": "0x71562b71999873db5b286df957af199ec94617f7",
                "transactionHash": "0xffc4978dfe7ab496f0158ae8916adae6ffd0c1fca4f09f7a7134556011357424",
                "transactionIndex": "0x0",
                "type": "0x0"
            }
        ]
    }
}

Subscribe to all transaction receipts:

# Subscribe (WebSocket client --> WebSocket server):
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "eth_subscribe",
	"params": ["transactionReceipts"]
}

# Notification (WebSocket server --> WebSocket client):
# Data format is the same as eth_getTransactionReceipt
{
    "jsonrpc": "2.0",
    "method": "eth_subscription",
    "params": {
        "subscription": "0x9374a0f623d50dd1477b4a3e25fc3b76",
        "result": [
            {
                "blockHash": "0x9fd9d918653b8b40406df7d230e3a3ab80bcc24da2bb5411c1f64aaa0d3546af",
                "blockNumber": "0x4",
                "contractAddress": null,
                "cumulativeGasUsed": "0x5208",
                "effectiveGasPrice": "0x3b9aca00",
                "from": "0x71562b71999873db5b286df957af199ec94617f7",
                "gasUsed": "0x5208",
                "logs": [],
                "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
                "status": "0x1",
                "to": "0x71562b71999873db5b286df957af199ec94617f7",
                "transactionHash": "0x9687a6a6805ada78575b8485d9ce2afdf4483ce51745f352fabeaf723c1e5e08",
                "transactionIndex": "0x0",
                "type": "0x0"
            },
            ......
        ]
    }
}

As mentioned in the above example, filtering is not performed when no second parameter is provided. The following describes three additional special cases where no filtering is performed:

  1. The transactionHashes field in the second parameter is set to null;
  2. The transactionHashes field in the second parameter is set to [] (empty array);
  3. The second parameter is {} (empty object).

All of the above are treated as subscribing to all transaction receipts:

# Setting `transactionHashes` field to `null` also means no filter
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "eth_subscribe",
	"params": ["transactionReceipts", {
		"transactionHashes": null
	}]
}

# Setting `transactionHashes` field to `[]` also means no filter
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "eth_subscribe",
	"params": ["transactionReceipts", {
		"transactionHashes": []
	}]
}

# Setting the second parameter to `{}` also means no filter
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "eth_subscribe",
	"params": ["transactionReceipts", {}]
}

I noticed that all HTTP APIs have been standardized in the execution-apis project, but WebSocket APIs are not included in that project. In this PR, I only enhance the WebSocket API - no HTTP APIs are changed.

@s1na
Copy link
Contributor

s1na commented Sep 23, 2025

Hey thanks for this, I think it's a fair request. I will have a look

@10gic
Copy link
Contributor Author

10gic commented Sep 29, 2025

Hi @s1na, hope you're doing well. When you have a chance, could you take a look at this PR? Any feedback would be great. Thanks!

@s1na
Copy link
Contributor

s1na commented Sep 29, 2025

Pushed a commit to avoid an extra db read of the receipts in SetCanonical

@10gic
Copy link
Contributor Author

10gic commented Sep 30, 2025

Pushed a commit to avoid an extra db read of the receipts in SetCanonical

You're very thorough, thank you for the commit.

@10gic
Copy link
Contributor Author

10gic commented Oct 1, 2025

Hi @s1na, just following up to see if you have any additional feedback on this PR? Happy to make any other changes if needed.

@10gic
Copy link
Contributor Author

10gic commented Oct 8, 2025

Hi @s1na, I noticed a PR for EIP-7966 (eth_sendRawTransactionSync Method) is ready. I found that its implementation could be optimized after this PR is merged:
Once this PR is merged, we can implement eth_sendRawTransactionSync by simply calling SubscribeChainEvent (because we extended the chain event to include transaction receipt), rather than calling SubscribeChainHeadEvent and GetTransactionReceipt multiple times.
Could you continue reviewing this when you get a chance? I'd appreciate your feedback.

// TransactionReceiptsFilter defines criteria for transaction receipts subscription.
// If TransactionHashes is nil or empty, receipts for all transactions included in new blocks will be delivered.
// Otherwise, only receipts for the specified transactions will be delivered.
type TransactionReceiptsFilter struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why you made this a structure instead of taking []common.Hash as the parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @s1na, thanks for asking! I used a struct mainly for two reasons:

  1. Self-documenting API
    Having named fields like {"transactionHashes": [...]} makes the API more readable and self-explanatory compared to a bare array.
  2. Future extensibility
    A struct makes it easier to add new filter options in the future (e.g., from, to, or other filters) without breaking backward compatibility.

@s1na
Copy link
Contributor

s1na commented Oct 8, 2025

Once this PR is merged, we can implement eth_sendRawTransactionSync by simply calling SubscribeChainEvent (because we extended the chain event to include transaction receipt), rather than calling SubscribeChainHeadEvent and GetTransactionReceipt multiple times.

Yes that's a good point!

Copy link
Contributor

@s1na s1na left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good PR, thanks!

@s1na s1na added this to the 1.16.5 milestone Oct 9, 2025
@s1na s1na changed the title websocket: add transactionReceipts for receipts notification eth/filters: add transactionReceipts subscription Oct 9, 2025
@s1na s1na merged commit 1120855 into ethereum:master Oct 9, 2025
9 of 10 checks passed
@s1na
Copy link
Contributor

s1na commented Oct 9, 2025

I just realized, we didn't add this to ethclient. Let me know if you'd like to open a PR for that.

@10gic
Copy link
Contributor Author

10gic commented Oct 9, 2025

I just realized, we didn't add this to ethclient. Let me know if you'd like to open a PR for that.

Thanks for pointing that out. The PR is ready: #32869.

s1na pushed a commit that referenced this pull request Oct 10, 2025
Add `SubscribeTransactionReceipts` for ethclient. This is a complement
to #32697.
Sahil-4555 pushed a commit to Sahil-4555/go-ethereum that referenced this pull request Oct 12, 2025
- Introduce a new subscription kind `transactionReceipts` to allow clients to
  receive transaction receipts over WebSocket as soon as they are available.
- Accept optional `transactionHashes` filter to subscribe to receipts for specific
  transactions; an empty or omitted filter subscribes to all receipts.
- Preserve the same receipt format as returned by `eth_getTransactionReceipt`.
- Avoid additional HTTP polling, reducing RPC load and latency.

---------

Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com>
Sahil-4555 pushed a commit to Sahil-4555/go-ethereum that referenced this pull request Oct 12, 2025
Add `SubscribeTransactionReceipts` for ethclient. This is a complement
to ethereum#32697.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants