Problem
EthereumTypeEstimateGas (bchain/coins/eth/ethrpc.go:1961, provider block at ~1985) routes every eth_estimateGas request to the alternative send-tx provider's urls[0] whenever *_ALTERNATIVE_SENDTX_URLS is configured — the guard is a bare nil-check, with a fresh RPC client dialed per call and a silent fallback to the primary backend on error (the provider error is discarded, so the problem is invisible in logs).
This is the same pathology as #1627, one function further down the file: the routing is unconditional, but almost no request needs the provider. The path is hit from the websocket estimateFee handler (server/websocket.go:1124) — i.e. every time a user sits on a wallet's send screen, with estimates re-firing as the amount is edited.
Example
- An ETH instance has
ETH_ALTERNATIVE_SENDTX_URLS pointed at a private relay whose API key allows ~10 req/s.
- Alice types an amount into the send form. The wallet requests a gas estimate; Blockbook sends
eth_estimateGas to the relay, although Alice never submitted a private transaction — the primary backend could have answered, and the relay call spends quota for nothing.
- With ~50 users composing transactions concurrently, the relay quota is exhausted → 429s → every estimate pays a wasted round-trip (dial + call + rejection) before the primary answers, making the send form slower for everyone.
- Bob, who actually just broadcast a private transaction through the relay, is the one harmed: the quota his nonce lookup and next broadcast need was burned by everyone else's gas estimates.
Why not simply "never use the provider for estimates"
There is one legitimate case: a sender with a private transaction still pending. If Bob composes a second transaction that builds on the pending state of his first (private) one, only the relay sees that pending transaction and can estimate correctly — the primary backend cannot.
Proposed fix
Reuse the gating introduced by #1628 for nonces: route eth_estimateGas through the alternative provider only for recent private senders (useForNonces / the recentSenders map), directed at the URL that accepted their send (nonceURL); all other requests go straight to the primary backend. Also stop discarding the provider error silently — log it at warning level like the nonce fallback does.
Related: #1627, #1628 (deliberately scoped the estimateGas path out).
🤖 Generated with Claude Code
Problem
EthereumTypeEstimateGas(bchain/coins/eth/ethrpc.go:1961, provider block at ~1985) routes everyeth_estimateGasrequest to the alternative send-tx provider'surls[0]whenever*_ALTERNATIVE_SENDTX_URLSis configured — the guard is a bare nil-check, with a fresh RPC client dialed per call and a silent fallback to the primary backend on error (the provider error is discarded, so the problem is invisible in logs).This is the same pathology as #1627, one function further down the file: the routing is unconditional, but almost no request needs the provider. The path is hit from the websocket
estimateFeehandler (server/websocket.go:1124) — i.e. every time a user sits on a wallet's send screen, with estimates re-firing as the amount is edited.Example
ETH_ALTERNATIVE_SENDTX_URLSpointed at a private relay whose API key allows ~10 req/s.eth_estimateGasto the relay, although Alice never submitted a private transaction — the primary backend could have answered, and the relay call spends quota for nothing.Why not simply "never use the provider for estimates"
There is one legitimate case: a sender with a private transaction still pending. If Bob composes a second transaction that builds on the pending state of his first (private) one, only the relay sees that pending transaction and can estimate correctly — the primary backend cannot.
Proposed fix
Reuse the gating introduced by #1628 for nonces: route
eth_estimateGasthrough the alternative provider only for recent private senders (useForNonces/ therecentSendersmap), directed at the URL that accepted their send (nonceURL); all other requests go straight to the primary backend. Also stop discarding the provider error silently — log it at warning level like the nonce fallback does.Related: #1627, #1628 (deliberately scoped the estimateGas path out).
🤖 Generated with Claude Code