Repository navigation
fix(api): derive the plan transaction count from the builder splitter - #596
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
What
execution.estimatedTransactionCountandexecution.transactions[].coverson the close plan are now derived from the same splitter the builder packs transactions with (splitCloseOps, the single place the 100-operation cap is applied), instead of the hard-coded1.null(withtransactionsempty) when it cannot be known safely at plan time: the plan contains a DeFi exit (its transaction count depends on live debt and simulation), a held Soroban token has no decision yet, or no destination was given (an exchange adds a transaction).fused-closemodule and its tests are renamed toclose-operations(CloseOperationsInput,assembleCloseOps[Tagged],packCloseTransactions,buildCloseTx), and the "fused" wording is removed fromapps/api/srcand the SDK doc comment.Why
CLAUDE.md and
docs/architecture.mdsay there is no fused mode: exchange closes, claimable balances, Soroban token moves and more than 100 operations all need more transactions. The plan always said 1, so an integrator showing "1 transaction" before signing was wrong for many accounts.Commits
refactor(builder): rename the fused close module to close operations- rename only.fix(api): derive the plan transaction count from the builder splitter- behavior, types, OpenAPI, SDK.Evidence the rename does not change emitted operations
A throwaway script (not committed) built a fixed matrix with pinned keys, sequence and clock through
packFusedCloseTransactions,buildFusedCloseTxandassembleFusedCloseOpsTaggedbefore the rename and through the renamed functions after: 13 inputs (empty, 3 and 150 trustlines, memo id and text, no merge, 150 claims, claim plus add-trustline, issuer, transfer, data plus offers, sponsorship revoke, signer normalization) x funded and underfunded (sponsored-fee) accounts. Output covers every XDR,covers,summaryandneedsSponsoredFee.diff before.json after.jsonis empty; both files have sha2564b0d875b681c17e190208d15014c3aa287fe6ee528403530dc1a44dbe601f5dd. The only builder change in the second commit isbatchItems(tagged, OP_BATCH_LIMIT)becomingsplitCloseOps(tagged), which is that exact call.Tests
plan-transaction-count.test.ts(new) compares the plan against the number of transactionsbuildCloseTransactionsreturns summed across rounds, and the per-transactioncovers, for: a single-transaction account (1), an exchange via the mediator (2), a claimable-balance account (2) and a 150-trustline account over the cap (2).close-api-plan-response.test.tscovers single, exchange, claim round, cap split withreason: "op_batch", token moves, exit and undecided input (null) and empty (0).bun run type-check,lint,test(api 1645, web 844, sdk 50, playground 55) andformat:checkpass locally.Consumers of the nullable field
apps/web,apps/playground, the SDK anddocs/do not read the value anywhere in rendering: the only readers are the web duplicate type (updated),examples/headless-close.ts(now prints no figure fornull) and the testnet e2e spec (still1for its account). No UI shows the count, so nothing can render "1" or "null" for an unknown count.SDK
Breaking type change, so
0.4.2to0.5.0(package.json,src/version.ts,etc/sdk.api.md, CHANGELOG naming who must change code: anyone reading the field as a number).Issue checklist
@lumenwipe/typesdoc comments describe the field; SDK comment no longer says "fused close": done.grep -rniw fused apps/api/src packages/sdk/srcis empty. The issue's literalgrep -rnistill matches the substring in "refused" (for exampledecisions.ts), which is unrelated.Risks and scope
remainingTransactionBound(fix(builder): preflight the merge destination and source sequence before the first round #595) is intentionally left unchanged: it runs on bare account state with no decisions (also on/close/transactionswith no plan), and reusing the plan count would needbuildPlanplus decisions there and would returnnullfor exits and undecided tokens. Its conservative over-reservation is the right property for a sequence headroom guard; not strictly safe or small, so left alone.verify()is untouched, and nothing signs from this count.step-engine.tsimports) and [api] Give every request an id and log under a written privacy policy #488 (SDK version).Security review
Security-sensitive (transaction construction naming and splitting). The
security-reviewskill ran from the worktree but saw an empty diff (it inspects the main checkout), so this was hand-reviewed: no operation, verify allowlist, key handling or network input changes; the new code only counts plan steps with no I/O; the rename is proven byte-identical above. No findings.Closes #481