feat(evm): wallet-declared privatePending hint for nonce + gas routing (backs blockbook#1639) - #30212
Closed
pragmaxim wants to merge 3 commits into
Closed
feat(evm): wallet-declared privatePending hint for nonce + gas routing (backs blockbook#1639)#30212pragmaxim wants to merge 3 commits into
pragmaxim wants to merge 3 commits into
Conversation
… + estimate helper) First of three stacked changes wiring the wallet-declared privatePending hint that blockbook trezor/blockbook#1639 consumes to route EVM nonce lookups and gas estimates deterministically instead of via its per-instance recentSenders heuristic. This PR is pure plumbing with no behavior change (all fields transmit undefined): - shared PrivatePendingHint type on AccountInfoParams (top-level, drives blockbook's declaredFloor) and a presence-only {nonces} on EstimateFeeParams.specific; - forward privatePending through connect getAccountInfo (validateParams whitelist + the explicit forward object), mirroring confirmedNonce; the blockchain-link worker already forwards the payload untouched, so no worker change is needed; - doc-mirror the field on WsAccountInfoReq in the generated blockbook-api.ts; - buildEvmEstimateSpecific() helper (wallet-utils) that attaches the hint to an EVM estimate `specific` ONLY when there is at least one in-flight private nonce - the length>0 guard is the trezor/blockbook#1629 over-declaration guard - with unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…delivery + prune) Second of three stacked changes for the blockbook privatePending hint (trezor/blockbook#1639). Adds the durable model and the delivery/prune machinery; no producer writes yet, so the slice stays empty and this is still a functional no-op end to end. Why a dedicated slice: the private/relay bit is transient (computed only at broadcast in getMevProtectedTxData) and lives on neither the fake nor the real tx object, and blockbook overwrites the fake pending tx with its real (unmarked) tx mid-flight - so a tx-object marker cannot survive and a tx-list-derived set cannot tell private from public. A small Redux slice keyed by (accountKey, nonce) is the sole authoritative source; nonce is invariant across the fake->real swap. - new suite-common/wallet-core/src/privatePending slice (Redux only for now; IndexedDB persistence deferred - Redux already survives WS reconnect, only an app restart mid-tx regresses, matching today's heuristic fallback); - DELIVERY: attach the hint (and confirmedNonce:true) to the UNCONDITIONAL basic getAccountInfo call in fetchAndUpdateAccountThunk, gated on there being an in-flight private nonce. It must ride the basic call, not the gated details:'txs' one, because a blockbook-invisible private tx does not make the account "outdated" so the txs call is skipped for exactly the flows that need the hint; - PRUNE (STOP): drop entries with nonce < confirmedNonce (mined) or past a TTL backstop, on every basic refresh - instance-agnostic, so it self-heals across load-balanced blockbook replicas. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… privatePending Third of three stacked changes for the blockbook privatePending hint (trezor/blockbook#1639). First change with observable behavior: it fills the slice from the two flows whose signed nonce is already in scope, and routes their gas estimates. - isMevPrivateSend(symbol, isMevProtectionEnabled) predicate (wallet-utils) - the exact condition under which getMevProtectedTxData broadcasts through the relay; - record the private nonce+txid after a successful pushTransaction in the send flow (pushSendFormTransactionThunk) and in WalletConnect eth_sendTransaction, gated by isMevPrivateSend so public sends never declare; - adopt buildEvmEstimateSpecific at the send and allowance estimate sites, passing the account's private-pending nonces so the estimate simulates against the relay's pending-private state - presence-only, so a keystroke with nothing in flight never routes to the relay. Deferred (see PR description): staking / Merkl-claim / stablecoin-yield writes need signed-nonce capture first (PrecomposedTransactionFinal carries no nonce); until then those private flows fall back to blockbook's recentSenders heuristic, which is acceptable because #1639 keeps that heuristic as the fallback. IndexedDB persistence also deferred. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pragmaxim
force-pushed
the
feat/evm-privatepending-hint
branch
from
July 23, 2026 09:00
a39c8b4 to
315b63a
Compare
pragmaxim
marked this pull request as ready for review
July 27, 2026 10:17
pragmaxim
requested review from
marekrjpolak,
martykan,
mroz22 and
szymonlesisz
as code owners
July 27, 2026 10:17
Contributor
Author
|
Solved in #30534 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Populates the wallet-declared
privatePendinghint that blockbook trezor/blockbook#1639 consumes, so Blockbook can route EVM nonce lookups (getAccountInfo) and gas estimates (estimateFee) deterministically from authoritative wallet state, instead of inferring "did this instance recently accept a private send" via its per-instancerecentSendersheuristic — which is fragile across restarts, load-balanced replicas or different websocket connections on different replica.Why this needed real design (the two original blockers)
getMevProtectedTxData(dropdisableAlternativeRPC⇒ relay-routed) and is stored on neither the fake pending tx nor the real tx. Worse, the reducer whole-object-overwrites the fake pending tx with blockbook's real (unmarked) tx mid-flight, so any per-tx marker is wiped exactly when it's still needed. And the staking / WalletConnect / Merkl-claim / stablecoin-yield flows create no fake tx at all.getAccountInfo(validateParams whitelist + the explicit forward object); the estimate hint is hand-built at ~8 independentspecificcall sites with no choke point.Design decisions
(accountKey, nonce)(suite-common/wallet-core/src/privatePending) is the sole source of truth. Nonce is invariant across the fake→real swap; the slice is independent of the tx list, so it also covers the no-fake-tx flows. A tx-object marker or agetOwnEvmNonceSets-derived set both provably fail (marker wiped mid-flight; derivation can't tell private from public → would over-declare public txs to the rate-limited relay and under-declare the no-tx-list flows).getAccountInfocall infetchAndUpdateAccountThunk, not the gateddetails:'txs'call. A blockbook-invisible private tx does not make the account "outdated", so the txs call is skipped for exactly the flows that need the hint. Sent only when the account has an in-flight private nonce (also the over-declaration guard).nonce < confirmedNonce(mined) or past a TTL backstop, on every basic refresh — so it self-heals after reconnecting to a different (load-balanced) blockbook instance, and a relay-dropped tx that never confirms can't be declared forever (bounds the eth: every gas estimate is sent to the alternative provider, burning its rate-limit quota blockbook#1629 quota concern).buildEvmEstimateSpecific()attaches the hint to an EVM estimatespecificonly whennonces.length > 0— the presence-only routing means a keystroke with nothing in flight never hits the relay.Commits
feat(blockchain-link)— plumbing, no behavior change: sharedPrivatePendingHinttype onAccountInfoParams+ presence-only{nonces}onEstimateFeeParams.specific; forward through connectgetAccountInfo(mirroringconfirmedNonce); doc-mirror onWsAccountInfoReq;buildEvmEstimateSpecifichelper + unit tests.feat(wallet-core)— durable Redux slice + delivery on the basic call + confirmedNonce/TTL prune. Still a no-op (no producer writes yet).feat(wallet-core)— first observable behavior:isMevPrivateSendpredicate; record private sends in the send + WalletConnect flows (signed nonce already in scope); adoptbuildEvmEstimateSpecificat the send + allowance estimate sites.Scope / deferred
PrecomposedTransactionFinalcarries none), so recording them faithfully needs signed-nonce capture (RLP-decode the serialized tx, or thread the computed nonce through compose/sign) — declaring them with a guessed nonce would be worse than the fallback. Until then they fall back to blockbook's heuristic, which Various fixes #1639 keeps as the fallback, so partial coverage never regresses below today.Related
🤖 Generated with Claude Code
🌐 Preview deployments
🌐 Suite Web preview: https://dev.suite.sldev.cz/suite-web/feat/evm-privatepending-hint/web/
🔍 Currents Test Results
🔍 Suite desktop test results: View in Currents
🔍 Suite web test results: View in Currents
🔍 Suite native android test results: View in Currents
🔒 Quarantined E2E Tests
Trezor Suite (web) — 3 test(s)
Updated: 2026-07-23T09:21:48.175Z • 3 test(s) total
Trezor Suite (desktop) — 2 test(s)
Updated: 2026-07-23T09:23:55.528Z • 2 test(s) total