Skip to content

feat: soroban tx builder, per-route rate limits, spam heuristics, dark mode persistence - #1421

Merged
joelpeace48-cell merged 1 commit into
FinesseStudioLab:mainfrom
leojay-net:feat/backend-tx-builder-route-rate-limits-moderation-dark-mode
Oct 18, 2026
Merged

joelpeace48-cell merged 1 commit into
FinesseStudioLab:mainfrom
leojay-net:feat/backend-tx-builder-route-rate-limits-moderation-dark-mode

Conversation

@leojay-net

Copy link
Copy Markdown
Contributor

Summary

#460 – Soroban transaction builder. New backend/src/services/sorobanTransactionBuilder.js (createSorobanTransactionBuilder) centralises the contract-call lifecycle: build (load account, Contract.call), simulate, assemble, sign (Keypair or async signer function), submit (send + poll getTransaction), and invoke which runs all of it. Retryable failures (RPC errors, TRY_AGAIN_LATER) rebuild from a fresh sequence number up to maxAttempts with linear backoff; simulation errors, rejected and on-chain-failed transactions fail fast. Every failure is a SorobanTransactionError with stage, retryable and (when known) the tx hash. The RPC server, passphrase, sleep and assemble function are injected, so backend routes and admin scripts can share it and tests need no network. This PR adds the service only; call sites can migrate to it separately.

#465 – per-route rate limits. createRateLimiter accepts resolveRouteLimit. A matching rule gets its own counter bucket and can only tighten the limit that would otherwise apply (a stricter per-API-key tier still wins). Rules live in config/routeRateLimits.js (method + Express-style path, version-prefix independent). Defaults: POST /campaigns 30/min, POST /campaigns/:id/clone 20/min, POST /campaigns/:id/image 10/min, PUT /campaigns/:id 30/min, DELETE /campaigns/:id 20/min; reads keep the global limit. Override with RATE_LIMIT_ROUTES (JSON array) or options.rateLimit.routes.

#469 – content moderation. The campaign create/update moderation middleware and blocklist already existed. This adds built-in spam/phishing heuristics to the local provider (detectSpamSignals): link stuffing (>3 links) and URL shorteners, 10+ repeated characters, all-caps shouting, and wallet-phishing phrases (seed phrase / private key lures). They run alongside the blocklist and return spam or phishing categories. Ordinary copy with a single link is not flagged; provider: none still skips everything.

#470 – dark mode. The toggle already saved its value, but it also overwrote the choice with the system default on every load, so the OS preference was never followed. Now an explicit toggle is the only thing that writes trivela-theme; with no saved choice the app follows prefers-color-scheme, including live OS changes. An inline script in index.html applies the theme before first paint (no flash). Storage errors are handled.

Also: src/index.js was missing the closing ); after the createIpfsPinRoutes mount (syntax error on main), and src/lib/logger.js was missing although several services import it; added a one-line re-export of the existing logger.

Testing

  • routeRateLimits.test.js (route matching, env parsing, write route throttled from its own bucket while reads are unaffected, route limit never loosens a stricter limit) and the existing rateLimit.test.js: pass.
  • sorobanTransactionBuilder.test.js (9): lifecycle, retries, fail-fast cases, confirmation polling, async signer: pass.
  • moderation.test.js unit tests for the new heuristics: pass.
  • frontend/src/theme.test.js (6): stored vs. system preference, invalid values, unavailable storage, change subscription: pass.

Notes

  • createApp-based backend tests (including the integration half of moderation.test.js, index.test.js and the OpenAPI contract test) cannot start on current main for unrelated reasons: migration 046_session_tokens.js has no numeric version export (duplicate 046/047/048 migration numbers also exist). I did not touch those migrations, so the new route-limit wiring in index.js was verified at the middleware level, not through a running app.
  • Frontend full run: 2 existing failures unrelated to this change (CampaignDetail.countdown, contractErrors).
  • backend/openapi.yaml is untouched (no API surface changed).

Closes #460
Closes #465
Closes #469
Closes #470

@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@leojay-net 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

@joelpeace48-cell
joelpeace48-cell merged commit bce998e into FinesseStudioLab:main Oct 18, 2026
9 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants