Repository navigation
Description
Summary
TransactionBuilderService.buildDepositTransaction builds an invokeContractFunction operation for the vault deposit and returns it unsigned. It never runs Soroban simulation or prepareTransaction, so the XDR has no sorobanData (footprint and resources), no auth entries for the user, and only STELLAR_BASE_FEE as its fee. Any wallet that signs and submits it gets rejected. The service also permanently reserves sequence numbers for the user's own account each time it builds, so one abandoned deposit breaks all later ones with tx_bad_seq. This issue makes the prepare step produce XDR that can be signed and submitted against the testnet vault.
Background and evidence
In src/services/transactionBuilder.ts:
- No simulation step.
buildDepositTransaction(from line 172) createsOperation.invokeContractFunction({ function: 'deposit', args: [Address(user), i128(amount)] })(about line 238), builds withfee = this.resolveFee()(line 195,STELLAR_BASE_FEE, default 100) andsetTimeout(timeout).build()(line 274). There is no RPC simulation call. - Dead error branch. The
SimulationErrorbranch (about lines 247-255) can never run, becauseinvokeContractFunctiondoes not simulate. - Burned sequence numbers.
sourceKey = params.sourceAccount ?? params.userPublicKey(line 199), so by default the source is the user's own account.PostgresSequenceStore.allocate(src/services/postgresSequenceStore.ts, lines 11-31) advancesnext_sequence = GREATEST(stored, ledger) + 1on every build.- If a user abandons one prepared transaction, the next build uses
account.seq + 2and is rejected withtx_bad_seq. The counter never decreases.
- User id as Stellar key.
src/controllers/depositController.tspassesuserPublicKey: user.id(about line 169) with the comment "Assuming user.id is the Stellar public key". - The ABI matches. The vault's
deposit(env, caller: Address, amount: i128)callscaller.require_auth(). The testnet vault isCBJZLUKQBWDMZPKNJAVONZ7GCCWADSETRXWOPN5Z3XWAUNI4F2R7XDYQ; a deposit smoke-test transaction is linked in Callora-Contractsdocs/DEPLOYMENTS.md.
Scope
In scope
- Simulate and assemble.
- After building, call
rpc.Server.prepareTransaction(tx)using the network's Soroban RPC URL fromconfig(SOROBAN_TESTNET_RPC_URL/SOROBAN_MAINNET_RPC_URL). - Return the assembled XDR, the resource fee, the minimum resource fee and the latest ledger in the response.
- Map simulation errors (including vault contract errors
BelowMinDeposit (8),Paused (4)) to the existingSimulationError, with redacted diagnostics.
- After building, call
- Sequence handling.
- Do not reserve sequences in
transaction_sequenceswhen the source account is the user's own key (the wallet owns its sequence). - Use
server.loadAccount/rpc.getAccountat build time and let the wallet's submission be authoritative. - Keep the sequence store only for backend-owned source accounts, and document which accounts those are.
- Do not reserve sequences in
- Stellar address source. Read the user's Stellar address from the user record (
users.stellar_addressviauserRepository), not fromuser.id. Return400 STELLAR_ADDRESS_REQUIREDwhen it is missing. - Docs and contract. Update
docs/deposit-transaction-builder.mdand thedocs/openapi.jsonresponse schema forPOST /api/vault/deposit/prepare(new fee fields), and keepnpm run validate:openapigreen.
Out of scope
- Submitting the signed transaction on the user's behalf.
- Fee-bump sponsorship (the fee-abstraction route).
- Vault balance snapshot syncing.
Design constraints
- Unsigned only. The returned XDR must carry zero signatures; the existing check stays.
- Simulation budget. Simulation obeys the configured RPC timeout and the existing
withRetrypolicy for transient RPC errors only. - No sequence burn. Building a transaction for a user-owned account must not change any persisted state.
- Testable. The RPC server is injectable.
Test plan
Extend src/services/transactionBuilder.test.ts and src/controllers/depositController.test.ts, plus an opt-in live smoke test next to src/services/transactionBuilder.smoke.test.ts.
| # | Scenario | Expected |
|---|---|---|
| 1 | Happy path with stubbed simulate | returned XDR has sorobanData, auth entries and fee ≥ base + resource fee |
| 2 | Simulation returns Error(Contract, #8) |
SimulationError → 422/502 per the existing mapping, with code 8 in diagnostics |
| 3 | RPC timeout | retried per policy, then a typed timeout error |
| 4 | Build twice for the same user without submitting | both XDRs use account.seq + 1; transaction_sequences untouched |
| 5 | Backend-owned source_account |
sequence store still used |
| 6 | User without a Stellar address | 400 STELLAR_ADDRESS_REQUIRED |
| 7 | Signatures on output | zero |
| 8 | Live (opt-in SOROBAN_LIVE_TESTNET=1) |
prepareTransaction against CBJZLUKQ...XDYQ for a funded testnet key returns assembled XDR |
Regression proof. Row 1 has no sorobanData and row 4 yields seq + 2 on current main.
Acceptance criteria
- Prepared XDR is accepted by
stellar tx simulate/ submission on testnet (row 8, documented in the PR). - No sequence reservation happens for user-owned source accounts.
-
npm run lint,npm run typecheck,npm run buildandnpm testpass, and CI is green. - At least 90% line coverage on
buildDepositTransactionand the controller changes. - OpenAPI updated and
npm run validate:openapipasses.
Getting started
git checkout -b fix/deposit-prepare-simulation
npm ci && npx prisma generate
npm run test:unit -- src/services/transactionBuilder src/controllers/depositController
stellar keys generate dep-test --network testnet --fundRead first: buildDepositTransaction and withSequenceAllocationTimeout in src/services/transactionBuilder.ts, src/services/postgresSequenceStore.ts, prepareDeposit in src/controllers/depositController.ts, and deposit in Callora-Contracts contracts/vault/src/lib.rs.
PR requirements
- Link this issue (
Closes #<n>). - Show a testnet simulation or submission result for a prepared XDR.
- Paste test output and coverage.
- Example commit:
fix(vault): simulate and assemble deposit transactions before returning XDR.
Priority rationale
High. Deposits are how users fund their vaults, and today every prepared deposit fails on submission, while abandoned attempts permanently break later ones. The fix needs Soroban resource assembly, auth entries and sequence-number semantics.
Activity
graceful-tech12 commentedon Oct 18, 2026
@graceful-tech12 has applied to work on this issue as part of the Stellar Wave Program's 10th wave.
ℹ️ Repo Maintainers: To accept this application, review their application or assign @graceful-tech12 to this issue.
IFEANYIBRIGHT commentedon Oct 18, 2026
@IFEANYIBRIGHT has applied to work on this issue as part of the Stellar Wave Program's 10th wave.
ℹ️ Repo Maintainers: To accept this application, review their application or assign @IFEANYIBRIGHT to this issue.
drips-wave commentedon Oct 19, 2026
Congratulations, @IFEANYIBRIGHT! 🎉 Your application was accepted by the repo's maintainers, and the issue is due on October 22, 2026.
🧑💻 @IFEANYIBRIGHT: Please resolve the issue such that the repo's maintainers have enough time to review your contribution before the due date. You'll earn Points for completing the issue on-time, which will make you eligible for a share of the Stellar Wave Program's reward pool.
Warning
When opening a PR, please link it to this issue to ensure it gets tracked accurately. Points are awarded when this issue is marked as completed by the maintainer.
🤠 Repo maintainers: Please keep an eye on the contributor's progress and review their work before the due date. You can manage this issue, including adjusting its complexity and points, here.
🌊 Happy Wave 🌊
grantfox-oss commentedon Oct 24, 2026
🎉 This issue has been marked as completed on GrantFox!
@IFEANYIBRIGHT's PR #1542 was approved and merged by @greatest0fallt1me.
🏆 @IFEANYIBRIGHT: You earned 35 FoxPoints for this contribution! Your current tier: Explorer (701 total points). Track your full progress on GrantFox.
👏 Great work, @IFEANYIBRIGHT! Keep contributing to CalloraOrg.
drips-wave commentedon Oct 24, 2026
This issue has been completed by @IFEANYIBRIGHT as part of the Stellar Wave Program's 10th Wave 🥳
😎 @IFEANYIBRIGHT: You earned 100 Points for completing this issue! Once the Wave's rewards are finalized, you'll be eligible for a percentage of the Wave's reward pool based on the percentage of total points you've earned. Learn more here. You can also Leave a review to share your experience working on this issue.
🧑💻 Repo maintainers: How'd the contributor do? Leave a review to share your experience working with them.