Summary
extendViaFootprint has four guards that only fire against a live network: the empty-key-list
refusal, the isSimulationRestore refusal, the status !== "PENDING" submission rejection, and the
status !== "SUCCESS" post-poll failure. Only the first is reachable from a unit test today.
test/futility.test.ts establishes the pattern this needs — a structural object cast to
rpc.Server that answers the calls under test and fails loudly on the ones that should not happen.
The guards it does not cover are precisely the ones that decide whether an operator learns why a
transaction failed.
The isSimulationRestore guard matters most. It exists because ExtendFootprintTTLOp cannot
restore an archived entry — unlike InvokeHostFunctionOp, which restores automatically since
Protocol 23 — so Path B must refuse rather than submit something that cannot work. Nothing currently
proves it refuses.
Acceptance Criteria
Tech Stack
TypeScript 5.7 on Node 22+ (CI runs 22 and 24), ESM with moduleResolution: "NodeNext" — relative
imports need the .js extension. @stellar/stellar-sdk is pinned to an exact version; verify RPC
API shape against the installed copy, not against documentation. Tests are Vitest under test/ and
run without a network. npm run format:check, npm run lint, npm run build and npm test must
all be clean.
Summary
extendViaFootprinthas four guards that only fire against a live network: the empty-key-listrefusal, the
isSimulationRestorerefusal, thestatus !== "PENDING"submission rejection, and thestatus !== "SUCCESS"post-poll failure. Only the first is reachable from a unit test today.test/futility.test.tsestablishes the pattern this needs — a structural object cast torpc.Serverthat answers the calls under test and fails loudly on the ones that should not happen.The guards it does not cover are precisely the ones that decide whether an operator learns why a
transaction failed.
The
isSimulationRestoreguard matters most. It exists becauseExtendFootprintTTLOpcannotrestore an archived entry — unlike
InvokeHostFunctionOp, which restores automatically sinceProtocol 23 — so Path B must refuse rather than submit something that cannot work. Nothing currently
proves it refuses.
Acceptance Criteria
extendViaFootprintthrough each guard.isSimulationRestorepath asserts that no transaction was submitted, not just that itthrew.
TRY_AGAIN_LATERandDUPLICATEsubmission statuses are covered, and the test assertspolling is never reached — the point of that guard is not burning the poll budget on a hash
the network does not have.
FAILEDpoll outcome asserts the result code reaches the error message.the pull request which mutations you ran.
Tech Stack
TypeScript 5.7 on Node 22+ (CI runs 22 and 24), ESM with
moduleResolution: "NodeNext"— relativeimports need the
.jsextension.@stellar/stellar-sdkis pinned to an exact version; verify RPCAPI shape against the installed copy, not against documentation. Tests are Vitest under
test/andrun without a network.
npm run format:check,npm run lint,npm run buildandnpm testmustall be clean.