docs: document gas-sponsorship fallback and the insufficient_balance failure - #2268
docs: document gas-sponsorship fallback and the insufficient_balance failure#2268n-dlms wants to merge 2 commits into
Conversation
…failure Sponsorship falls back silently to direct signing when an eligibility condition is not met (credits exhausted, unsupported network, Safe sender, private mempool, blocked RPC). Operators only see the downstream "Insufficient <SYMBOL> balance ... Fund <address> ..." failure (code insufficient_balance) and nothing in the docs connected that error to the fallback. - gas.md: add "When sponsorship falls back" (conditions, observable outcome, the named-address error, both remedies) - error-codes.md: document the structured insufficient_balance action failure and its sponsorship-fallback cause - troubleshooting.md: cross-link from Transaction Failures Verified against the behavior in lib/web3/sponsored-transaction-manager.ts (executeSponsoredTransaction returns null on every ineligible condition, triggering the direct-signing fallback) and lib/execute/native-balance.ts (describeNativeShortfall message and insufficient_balance code) at commit d249519 (staging).
suisuss
left a comment
There was a problem hiding this comment.
Welcome, and thanks for this - the contributing guide is in CONTRIBUTING.md, and ISSUES.md covers when a change needs an issue first.
What this changes
Three docs, additions only. docs/keeper-runs/error-codes.md gains a section for action failures carrying structured codes, with insufficient_balance as its worked example. docs/keeper-runs/troubleshooting.md gains a callout under Transaction Failures reading the Insufficient ETH balance message as a sponsorship fallback. docs/wallet-management/gas.md gains a "When sponsorship falls back" subsection with a sample error block and remediation.
Does it match the description
Matches. Three docs, no code, and the description says so.
Blocking
-
docs/keeper-runs/error-codes.md:41-44,docs/wallet-management/gas.md:116,docs/keeper-runs/troubleshooting.md:78-79- a run does not carry theinsufficient_balancecode.describeNativeShortfallbuilds{ code: INSUFFICIENT_BALANCE_CODE, message }(lib/execute/native-balance.ts:82), butpreflightGasBalancereturns{ affordable: false, message: shortfall.message }(lib/web3/gas-preflight.ts:122) and discards the code, andwrite-contract-core.ts:585-586then returns{ success: false, error: gasCheck.message }. The code survives only on the simulate path, throughSimulateFailureCodeinlib/execute/simulate.ts, which is whatdocs/api/direct-execution.mdanddocs/agent/mcp-server.mdalready document. -> An integrator reads/keeper-runs/error-codes, branches a run webhook oncode === "insufficient_balance", and it never fires; a user searches the Runs panel for that string and finds nothing. -> Scope the claim to/api/execute/*simulate responses and MCP simulate results, and key the run-side guidance on the message text. -
docs/wallet-management/gas.md:104-106- "Nothing in the run output tells you sponsorship was skipped" is the opposite of what ships.workflow-runs.tsx:874-876renders a "Gas sponsored" badge whenever a step output carriessponsored: true, andruns-filters.tsx:274offers aSponsoredrun filter. -> A user reads this, does not check the run, and opens a support ticket the missing badge would have answered. -> "The run output does not say why sponsorship was skipped - a fallback run simply lacks the Gas sponsored badge." -
docs/keeper-runs/error-codes.md:46-47- "did not hold at broadcast time" contradicts the code andgas.md:107.preflightGasBalanceruns beforewithNonceSession(write-contract-core.ts:579-587), so an unaffordable wallet never signs and nothing is broadcast. -> A user searches an explorer for a transaction hash that was never created. -> "before the transaction was broadcast". -
docs/keeper-runs/troubleshooting.md:75-77- the message does not imply a sponsorship fallback.gas-preflight.tshas no sponsorship condition in it; theInsufficient ETH balance ... Fund <address>string is emitted on every direct-signing write path, including orgs with sponsorship disabled, Safe senders, and chains that were never sponsorship-eligible. -> A Safe-routed workflow on an unsupported chain fails with this message, the reader goes hunting for exhausted gas credits, and the actual answer is to fund the Safe. -> Invert it: on a sponsorship-eligible network this message additionally means sponsorship fell back; otherwise it means the funding address cannot cover gas. -
docs/keeper-runs/error-codes.md:59-62,docs/wallet-management/gas.md:118-120- the four listed conditions are not sufficient to restore sponsorship.executeSponsoredTransactionalso requirescreateSponsoredClientto return non-null (sponsored-transaction-manager.ts:119-126), and that returns null when the org's active wallet hasturnkeySubOrgId === null(sponsored-client.ts:56-58). -> An org on a self-imported wallet, on Base, with credits remaining, satisfies all four listed conditions, retries expecting sponsorship, and pays gas again. -> Either say the list is the user-controllable subset and sponsorship may still be unavailable per organization and wallet, or add the Turnkey-managed-wallet condition.
Mechanical - actionable as-is
docs/wallet-management/gas.md:112- the sample block embeds0x26833b05be49036d4de306b1f4fba7713cc84de5. Every other example in these docs uses a placeholder (docs/api/direct-execution.md:625uses0x...orgWallet). Use a placeholder here too.docs/keeper-runs/error-codes.md:42- "The most common one on sponsored-capable networks" is a frequency claim with nothing behind it. Drop it or cite the source.
Verdict
Changes requested - the insufficient_balance code is not present on the run path these pages describe, and three of the other four claims contradict what the code does.
…es, correct fallback visibility and conditions - error-codes.md: the insufficient_balance code survives only on the simulate surface (/api/execute simulate, MCP simulate); run steps carry the plain message, so run-side guidance keys on message text - gas.md: a fallback run simply lacks the Gas sponsored badge (workflow-runs.tsx); reword the no-broadcast claim (gas preflight runs before signing); add the Turnkey-managed wallet condition and note the eligibility list is the user-controllable subset (sponsored-client.ts turns self-imported wallets away) - troubleshooting.md: the message is emitted on every direct-signing write path; on sponsorship-eligible networks it additionally means fallback - replace the real wallet address in the sample with a placeholder; drop the unsourced frequency claim
|
Thank you for the review — every point checked out against the code, and the PR is updated (a417e00). Point by point:
|
suisuss
left a comment
There was a problem hiding this comment.
What this changes
error-codes.md scopes insufficient_balance to simulate responses and MCP simulate results, and says run steps carry the plain message with no code. troubleshooting.md inverts its callout so the message reads as the preflight stopping the transaction, with sponsorship fallback as the additional reading on an eligible network. gas.md rewords the fallback trigger, adds the Gas-sponsored badge, and adds a caveat paragraph on Turnkey-managed versus self-imported wallets.
Does it match the description
Matches. Three files, all under docs/, nothing in code or CI. The specs/api-coverage.json gate does not apply - scripts/check-api-docs-routes.ts only parses docs/api/*.md.
Previously raised
- Scope the code to the simulate surface - partially addressed; the scoping is right and the explanation attached to it is not, see the first blocker.
- The Gas-sponsored badge - addressed.
- "did not hold at broadcast time" - addressed;
preflightGasBalanceatplugins/web3/steps/write-contract-core.ts:579does precedewithNonceSessionat:589. - Invert the fallback implication - addressed, then over-generalised, see the mechanical items.
- Four eligibility conditions are not the whole set - partially addressed; the new caveat lands but the reworded trigger above it now contradicts the list, see the third blocker.
- Address placeholder and the frequency claim - addressed.
Blocking
-
docs/keeper-runs/error-codes.md:47-50- the section is titled for simulate responses but explains the gas preflight. The simulate code comes fromnativeShortfallFailure, which compares the balance against the transfer value alone:lib/execute/native-balance.ts:17-19states "Neither caller adds gas to the requirement", andlib/execute/simulate.ts:314-316returns null forvalue <= 0. An integrator whose zero-valuewrite-contractsimulate fails on an empty wallet gets a revert message and never this code, and concludes the page describes a different endpoint. Say that the funding address could not cover the native value the call sends, that the simulator adds no gas term, and that a zero-value call never produces the code. -
docs/keeper-runs/error-codes.md:64-67- "restoring the sponsorship conditions also fixes the run without funding" is false for any value-bearing write, and contradictsgas.md:76-78in this same diff, which says sponsorship pays the fee only and native value is always debited from the user's own wallet. The preflight requiresvalueWei + MIN_TX_GAS_UNITS * gasPrice(lib/web3/gas-preflight.ts:111). A user with an empty wallet and a 0.1 ETH transfer step tops up gas credits, runs again, and fails again, on real funds. Qualify it to writes that send no native value. -
docs/wallet-management/gas.md:99-100- "falls back when the attempt returns no sponsored client" is narrower than the code and contradicts the eligibility list above it.executeSponsoredTransactionreturns null at six points: feature flag (lib/web3/sponsored-transaction-manager.ts:102), blocked RPC (:106), unsupported chain (:110), exhausted credits (:114-117), no client (:124), and a null Turnkey submit (:137). A reader whose credits ran out hunts the wrong cause. Restore "whenever any eligibility condition above is not met" and keep the per-org caveat as a separate sentence. -
docs/wallet-management/gas.md:123-127- the self-imported-wallet case is mine and it was wrong.createSponsoredClientdoes null-return on a nullturnkeySubOrgId, but no org can reach that state: the only production insert intoorganizationWalletsislib/turnkey/provision-org-wallet.ts:103-110, which always setsturnkeySubOrgIdfrom the Turnkey result, and there is no wallet-import route. Documenting it sends a reader looking for a wallet-type setting that does not exist instead of checking credits. Drop the parenthetical and keep only that Turnkey can reject an activity at submission time.
Mechanical - actionable as-is
docs/keeper-runs/error-codes.md:43-SimulateFailureCodeis an internal type alias (lib/execute/simulate.ts:102); the wire field iscode, and MCP surfaces it as aReason code:line. Name the field.docs/keeper-runs/error-codes.md:59anddocs/wallet-management/gas.md:118- "every direct-signing write path" overstates it. Six action cores callpreflightGasBalance;lib/safe/deployment.ts,lib/safe/roles-orchestrator.tsandapp/api/user/wallet/withdraw/route.tssign directly and do not. "Every web3 write action" is accurate.docs/wallet-management/gas.md:100-101- the badge renders per step, so a run with one sponsored and one fallback step still shows it. The run-levelSponsoredfilter is atcomponents/analytics/runs-filters.tsx:276.
Verdict
Changes requested - the scoping is right, but the explanation now attached to it describes the preflight rather than the simulator, and two statements would send someone with real funds to the wrong remedy.
Summary
Sponsorship falls back silently to direct signing whenever an eligibility condition is not met — gas credits exhausted, unsupported network, Safe sender, private mempool route, or a blocked RPC. The operator only sees the downstream failure:
(code
insufficient_balance, fromlib/execute/native-balance.tsdescribeNativeShortfall) — and nothing in the docs connected that error to the fallback. First-hand hit during a live integration on Base Sepolia.Changes
docs/wallet-management/gas.md: new When sponsorship falls back section — the fallback conditions, the two observable outcomes (wallet-pays success vs theinsufficient_balancefailure), and both remedies (fund the named address / restore sponsorship conditions)docs/keeper-runs/error-codes.md: document the structuredinsufficient_balanceaction failure (not a PREFIX-NNNN code), its sponsorship-fallback cause, and the fixdocs/keeper-runs/troubleshooting.md: cross-link from Transaction FailuresVerification
lib/web3/sponsored-transaction-manager.ts(executeSponsoredTransactionreturnsnullon every ineligible condition, triggering the direct-signing fallback) andlib/execute/native-balance.ts(describeNativeShortfallmessage +insufficient_balancecode)d249519c5d7dcf66b8405e065cd7988a647ac442Docs-only change; no behavior touched. Submitted to the KeeperHub — The Agent Economy hackathon bounty (DoraHacks) as a separate BUIDL.