Skip to content

feat(api): Swagger response docs, shared amount util, solver PATCH, race-safe idempotent create (#271-#274) - #347

Open
Meshmulla wants to merge 1 commit into
stellar-vortex-protocol:mainfrom
Meshmulla:feature/271-274-stellar-wave-api-improvements
Open

feat(api): Swagger response docs, shared amount util, solver PATCH, race-safe idempotent create (#271-#274)#347
Meshmulla wants to merge 1 commit into
stellar-vortex-protocol:mainfrom
Meshmulla:feature/271-274-stellar-wave-api-improvements

Conversation

@Meshmulla

@Meshmulla Meshmulla commented Aug 30, 2026

Copy link
Copy Markdown

Summary

Issues:

Changes

#271 — Swagger response docs

  • Every route in SorobanController (/api/v1/chain/health|ledger|network|account/:publicKey) and TokensController (/api/v1/tokens, /api/v1/tokens/stellar) now carries an @ApiOkResponse with an inline schema matching the actual return shape.
  • The account route additionally documents @ApiBadRequestResponse (invalid key) and @ApiTooManyRequestsResponse (AccountRateLimitGuard's 429), plus @ApiParam.
  • New src/tokens/dto/token-response.dto.ts (StellarTokenDto, StellarTokensResponseDto) gives /tokens/stellar a typed class.
  • test/openapi-contract.e2e-spec.ts extended with assertions that all six routes expose a 200 schema and that the account route documents 400/429.
  • src/generated/ regenerated via npm run generate:client — the new schemas appear in openapi.json/api-types.ts. Regeneration also picked up /intents/:id/audit and /intents/:id/quote, which had been added in earlier merged PRs without a client regen.

#272 — Shared amount utility

  • New src/common/amount.ts: parseBaseUnits, toDecimalNumber, toBaseUnits, calculateProtocolFee (the documented 0.05% = dstAmount * 5 / 10000), varianceScaleFromPerfScore, applyVarianceScale. All arithmetic is BigInt; the only Number conversion is the final display-scaling step, and it splits whole/fraction as strings first so large amounts don't lose precision.
  • IntentsController.quote() and fill() refactored onto the helpers. No behaviour change for valid inputs (fee %, variance formula and rounding preserved); large-amount paths are now precision-safe rather than lossy.
  • src/common/amount.spec.ts — 39 unit tests covering amounts near/beyond Number.MAX_SAFE_INTEGER, zero amounts, decimals 0–18, round-trips, and reproduction of the original inline formulas.

#273 — Solver profile PATCH

  • PATCH /api/v1/solvers/:address accepts a partial UpdateSolverDto (name, supportedChains, supportedTokens, avgFillTime — all optional) plus a required signature.
  • Signature verified via the existing verifyStellarSignature convention against a new buildUpdateSolverMessage(address)update-solver:<address>, before SolversService is touched.
  • Immutable fields (address, bondAmount, fillsCompleted, fillsFailed, totalVolume, registeredAt, isActive) are not on the DTO, so the global ValidationPipe({ whitelist: true }) strips them silently.
  • supportedChains/supportedTokens validated with the same @IsIn/@ArrayMaxSize rigor as RegisterSolverDto.
  • SolversService.update() applies the patch, ignoring undefined values so an absent field never clears data.
  • Docs: docs/solver-onboarding.md new section "1a. Updating Your Solver Profile"; Swagger decorators on the route; client SDK regenerated.

#274 — Race-safe idempotent create

  • IntentsService.create() now takes a synchronous reserve-then-create claim: it checks and sets an idempotencyInFlight map with no await in between, so two concurrent requests with the same key can never both proceed — the loser awaits the winner's in-flight promise and returns its intent.
  • The claim happens before the conditional registerOnChain() await (extracted into persistNewIntent()), so the window is closed, not shifted.
  • On failure the claim is released (.finally) and nothing is cached, so a later retry succeeds.
  • Comment documents the equivalent atomic INSERT ... ON CONFLICT (idempotency_key) DO NOTHING + read-back that issue feat: rebuild backend on NestJS, drop Express #1's Prisma adapter must use.
  • src/intents/intents.service.idempotency.spec.ts — 7 unit tests (25-way concurrent same-key → 1 intent, widened race window, sequential replay, distinct keys, no-key path, failure/retry, claim-before-on-chain-await).
  • test/load/concurrent-idempotent-create.test.ts — load test mirroring test/load/concurrent-accept.test.ts.

Incidental (merge-rot repairs, required to compile/run the above)

main currently does not type-check or build (pre-existing, ~46 tsc errors from earlier bad merges). This PR does not attempt a general fix, but four one-line drops in files it already touches were restored because the new code/tests can't compile otherwise:

  • import { INTENTS_REPOSITORY, IIntentsRepository } in intents.service.ts (present in 42a8c89, lost in a later merge)
  • import { Throttle } in intents.controller.ts (decorator was already in use)
  • import { ConfigService } in test/utils/create-test-app.ts (symbol already in use)
  • isActive: true (was bare isActive) in SolversService.reactivate()

intents.controller.ts net tsc error count goes 41 → 30; no new errors introduced (the two getPersistedQuote errors reported as "new" are the same pre-existing errors, shifted down by added import lines).

Testing / validation

  • npm run lint — 0 errors (37 pre-existing warnings, unchanged).
  • npm run typecheck — 30 pre-existing errors remain (down from 46 on main); none in files added or the regenerated client. Full green is blocked by unrelated pre-existing breakage in *.spec.ts, stats.service.ts, getPersistedQuote.
  • New unit tests all pass:
    • src/common/amount.spec.ts — 39/39
    • src/intents/intents.service.idempotency.spec.ts — 7/7
    • src/solvers/solvers.service.update.spec.ts — 5/5
    • src/solvers/solvers.service.spec.ts — 8/8 (still green after the reactivate fix)
  • npm run generate:client runs clean and the diff reflects the new endpoint + schemas.
  • E2E suite (test/*.e2e-spec.ts) is not runnable on main — the shared test/__mocks__/@stellar/stellar-sdk.ts stub is missing most exports (Networks, Keypair, …). The e2e specs added here (test/solvers-update.e2e-spec.ts, the openapi-contract additions) follow the existing pattern and will pass once that harness is repaired; this PR deliberately does not touch the shared mock.

Implements issues stellar-vortex-protocol#271, stellar-vortex-protocol#272, stellar-vortex-protocol#273, stellar-vortex-protocol#274.

stellar-vortex-protocol#271 - Typed Swagger response docs for SorobanController and TokensController
- Every /api/v1/chain/* and /api/v1/tokens/* route now declares an
  @ApiOkResponse schema; the account route also documents 400 and 429.
- Added StellarTokenDto / StellarTokensResponseDto for the /tokens/stellar shape.
- Extended test/openapi-contract.e2e-spec.ts to assert the new schemas.
- Regenerated src/generated/ (openapi.json + api-types.ts).

stellar-vortex-protocol#272 - Shared BigInt amount util (src/common/amount.ts)
- parseBaseUnits, toDecimalNumber, toBaseUnits, calculateProtocolFee (0.05%),
  varianceScaleFromPerfScore, applyVarianceScale - all BigInt-based, Number
  only at the final display-scaling step.
- IntentsController.quote()/fill() refactored onto the helpers with no
  behaviour change for valid inputs.
- 39 unit tests incl. amounts past Number.MAX_SAFE_INTEGER, zero, decimals 0-18.

stellar-vortex-protocol#273 - PATCH /api/v1/solvers/:address
- UpdateSolverDto (name / supportedChains / supportedTokens / avgFillTime,
  all optional) + required signature; immutable fields stripped by the
  whitelist ValidationPipe.
- buildUpdateSolverMessage() helper; signature verified before SolversService.
- SolversService.update() applies a partial patch, ignoring undefined values.
- Swagger + docs/solver-onboarding.md section 1a; regenerated client.

stellar-vortex-protocol#274 - Race-safe idempotency key in IntentsService.create()
- Synchronous reserve-then-create: concurrent requests with the same key
  claim an in-flight promise before any await, so exactly one intent is
  created and the losers replay its result.
- persistNewIntent() extracted; documents the INSERT ... ON CONFLICT approach
  the future Prisma adapter must use.
- 7 unit tests + test/load/concurrent-idempotent-create.test.ts.

Incidental: restored imports/values dropped by earlier bad merges
(INTENTS_REPOSITORY in intents.service, Throttle in intents.controller,
ConfigService in test/utils/create-test-app, isActive: true in
SolversService.reactivate) - all required for the code touched here to
compile and for the new tests to run.
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@Meshmulla Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant