Repository navigation
🐛 Skip overhead cache when account contract is not yet deployed - #79
Merged
florian-bellotti merged 1 commit intoMay 12, 2026
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
florian-bellotti
force-pushed
the
fix/braavos-overhead-skip-cache-on-contract-not-found
branch
from
May 11, 2026 19:50
2a76676 to
f4794cc
Compare
`ValidationGasOverhead::fetch` used to convert every error from `client.call(get_signers)` into `Ok(Self::none())`, and the caller (`resolve_gas_overhead`) cached the result indefinitely. As a side effect, any address queried while its contract did not yet exist — typically during a `deploy_and_invoke` flow that deploys the wallet in the same transaction it is estimating — got permanently marked as "no overhead". Once the wallet was deployed and the user did follow-up sponsored invokes, the cached `none()` was returned instead of the Braavos overhead, the resource bound on `l2_gas` was set ~5x too low, and Braavos passkey transactions reverted with `Insufficient max L2Gas`. Distinguish error variants in `fetch`: - successful response → cache (Braavos or none, based on shape) - contract-level execution error (account exists but does not expose `get_signers`, e.g. Argent/OZ) → cache as `none()` - `ContractNotFound` (wallet not yet deployed, the case behind this bug) → return `none()` for the current request but skip the cache so the next request retries the detection - any other transient RPC error (timeout, rate-limit, ...) → same as `ContractNotFound`: do not cache Also extract the response-shape classification into a pure function (`from_get_signers_response`) and add unit tests covering the on-chain shapes observed for a classic Braavos (4 felts) and a Braavos with a passkey/secp256r1 signer (5 felts).
florian-bellotti
force-pushed
the
fix/braavos-overhead-skip-cache-on-contract-not-found
branch
from
May 11, 2026 19:52
f4794cc to
d25c198
Compare
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
0xlny
approved these changes
May 12, 2026
This branch was successfully deployed
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
ValidationGasOverhead::fetchused to convert every error fromclient.call(get_signers)intoOk(Self::none()), andresolve_gas_overheadcached every result indefinitely. As a side effect, any address queried while its contract did not yet exist — typically during adeploy_and_invokeflow that deploys the wallet in the same transaction it is estimating — got permanently marked as "no overhead" in the cache. Once the wallet was actually deployed and the user did follow-up sponsored invokes, the cachednone()was returned instead of the Braavos overhead, the resource bound onl2_gaswas set ~5x too low, and Braavos passkey transactions reverted withInsufficient max L2Gas: max amount: 9422157, actual used: 48940800.This PR distinguishes error variants in
fetch:get_signers, e.g. Argent / OZ / custom accounts) → cache asnone()ContractNotFound(wallet not yet deployed — the case behind this bug) → returnnone()for the current request but skip the cache so the next request retries the detection once the wallet is on-chainContractNotFound: do not cacheAlso extracts the response-shape classification into a pure function (
from_get_signers_response) and adds unit tests covering the on-chain shapes observed for a classic Braavos (4 felts) and a Braavos with a passkey/secp256r1 signer (5 felts).Diagnosis trail (for reference)
Confirmed on tx
0x4b7a1cb5215c43c9f60735b1361b9ad2e43f2f446a968e7de152f2edf82baee(mainnet, block 9680273):resource_bounds.l2_gas.max_amount = 0x8fc54d = 9 422 157on-chain/1.5estimate,/1.15provider) yields a raw simulatorl2_gas_consumed ≈ 5.46M— i.e. no Braavos overhead was added to the recomputed feeget_signersreturnedContractNotFoundduringpaymaster_buildTransactionandpaymaster_executeTransactionof thedeploy_and_invoke, which got swallowed intoOk(Self::none())and cachedScope
This PR fixes the cache-poisoning dimension of the bug, which is the one observed on the reported transaction (the wallet was queried before deployment, the cached
none()then persisted for the subsequent sponsored invoke).It does not address the related case where the failing transaction is itself the
deploy_and_invokeof a Braavos passkey wallet (get_signersis fundamentally unreachable at simulation time because the wallet is being deployed in the same transaction). That requires reading the account type from thedeployment.class_hash/sigdata, and will be handled in a follow-up.Test plan
cargo build --workspacepassescargo clippy -p paymaster-executionpassescargo test -p paymaster-execution --lib— all 36 existing tests pass + 3 new ones cover the response-shape classification