Skip to content

Create new production release#1261

Merged
ebma merged 46 commits into
mainfrom
staging
Jul 8, 2026
Merged

Create new production release#1261
ebma merged 46 commits into
mainfrom
staging

Conversation

@ebma

@ebma ebma commented Jul 8, 2026

Copy link
Copy Markdown
Member

No description provided.

ebma and others added 30 commits June 2, 2026 18:58
Made it consistent for both Desktop and Mobile
…tched

The where clause nested Op.or inside the postCompleteState JSON path
object, which Sequelize's query builder rejects with 'Invalid value
{ cleanupCompleted: false }'. The error was caught and only logged, so
post-processing of completed ramps silently never ran. Use the
documented dotted-path JSONB syntax with Op.or at the top level, and
add a DB-backed regression test that fails against the old query.
…ute comparison

Adds an observational BlindPay payin FX quote (BRL -> USDT/USDB) alongside the
existing SquidRouter/Avenia/Nabla route quotes in compareRoutesUpfront, to
evaluate whether BlindPay is a cheaper source of stablecoin over time.

- New apps/rebalancer/src/services/blindpay: FX-rate client + types
- fetchBlindpayShadowQuoteUsdc returns a USDC-equivalent (6dp) quote, or null
  when unconfigured or below BlindPay's 5 BRL minimum
- Shadow only: logged in the route comparison but never added to candidates,
  so it is never actually routed through
- Optional BLINDPAY_* config; documented in .env.example
…orts

waitUntilTrueWithTimeout raced its poll loop against a timeout without
cancelling it, so every timed-out call leaked a loop polling forever
(the production CPU-leak mechanism). The timeout now aborts the loop
via an AbortController, and waitUntilTrue/checkEvmBalancePeriodically/
checkEvmNativeBalancePeriodically/checkEvmBalanceForToken accept an
optional AbortSignal so callers can cancel abandoned waits. Error
messages and BalanceCheckError semantics are unchanged.
The processor's 10-minute execution timeout used Promise.race, which
abandons handler.execute without stopping it. Abandoned executions kept
their polling loops running forever and piled up with every retry and
recovery-worker pass until the CPU pegged (production incidents
Jun 26 - Jul 8); a pegged event loop then slowed Postgres SCRAM
handshakes enough for Supavisor to fail new connections with
EAUTHTIMEOUT. Abandoned executions could also perform late side effects
(e.g. Avenia ticket creation) concurrently with the live retry.

The processor now aborts each timed-out execution via an AbortSignal
threaded through PhaseHandler.execute/executePhase. MAX_EXECUTION_TIME_MS
is env-overridable (PHASE_PROCESSOR_MAX_EXECUTION_TIME_MS) so the
regression test doesn't wait 10 minutes. Spec updated accordingly.
The Avenia balance wait ran 30 minutes per execution, which always
outlived the processor's 10-minute execution timeout - so the
30-minute payment-window cancellation (isPaymentTimeoutReached) never
ran for recovered ramps and never-paid onramps churned indefinitely.
The wait now runs in 5-minute chunks (like the EVM balance check) and
surfaces each chunk timeout as a recoverable error, letting the
wall-clock payment window cancel the ramp. Both waits also accept the
processor's AbortSignal. Spec updated accordingly.
With pool min 0 / idle 10s every quiet period dropped all connections,
so each request burst re-ran the full SCRAM handshake through the
Supabase pooler (Supavisor), which fails with EAUTHTIMEOUT when the
event loop is busy. Keep 2 warm connections and a 60s idle timeout.
…otification

Threads the observational BlindPay shadow quote from compareRoutesUpfront through
the preflight quotes and rebalance state into the Base completion Slack message,
rendered as a 'BlindPay shadow (not routed)' line with the delta vs the executed
route. Absent when BlindPay is unconfigured.
…rocess-query

Repair postProcessCompletedStates JSONB query that never matched
…on gap

Review feedback on #1257: register the abort listener and re-check
signal.aborted afterwards — a listener added to an already-aborted
signal never fires, so this ordering leaves no path where an abort is
missed. Adds a test for the pre-aborted case.
…ation test

Review feedback on #1257: a non-numeric PHASE_PROCESSOR_* env value
parsed to NaN, and setTimeout(..., NaN) fires immediately — a
misconfigured deploy would time out every phase instantly. Fall back
to the defaults for non-finite or non-positive values (tested).

The cancellation test now snapshots and restores the env overrides and
the shadowed nablaSwap registry entry in afterAll, per the repo's
restore convention.
Review feedback on #1257: fetch had no timeout, so a stalled BlindPay
request could hang the Promise.allSettled upstream and stall the whole
rebalance run — cap it at 30s via AbortSignal.timeout.

Also adds client tests (configuration gating, request shape incl. the
timeout signal, non-OK handling), which lifts the rebalancer function
coverage back above the CI ratchet floor (49.57% -> 53.45%).
… api

populateApi() returns the cached instance when one exists, but it was
also used as the refresh mechanism in getApi() (on spec version change)
and executeApiCall() (on bad-signature retry). In both cases the cached
ApiPromise was returned unchanged, so the refresh was a no-op and the
stale connection kept being used.

Add refreshApi(), which disconnects the cached instance and replaces it
with a fresh connection, and use it on the spec-version-change path,
the forceRefresh path, and the bad-signature retry. The retry also
rebuilds the extrinsic against the refreshed api, since the original
call is bound to the stale registry. populateApi/populateAllApis stay
idempotent for normal startup.

Add tests covering populateApi idempotency and both refresh paths; the
refresh tests fail against the previous implementation.
Change 'Buy & Sell' to 'Open App' in MobileMenu and DesktopNavbar
…hrows

Review feedback on #1257: handler.execute was invoked eagerly inside
the Promise.race array literal, so a handler that throws synchronously
would propagate before .finally was attached — clearTimeout never ran
and the orphaned timeout promise later rejected with nobody listening.
Wrap the call in Promise.resolve().then(...) so sync throws become a
rejection of the race and cleanup runs reliably. Regression test
asserts no unhandledRejection fires after the timeout window.
Address review feedback on refreshApi: the stale instance was removed
and disconnected before the replacement connection existed, so a failed
reconnect left the network without any cached api. Create the new
instance first, then swap and best-effort disconnect the stale one.

Also cache new instances only after connect/isReady so concurrent
readers never observe a still-connecting api.

Add a test asserting a failed refresh keeps the previous instance
cached and undisconnected.
ebma added 6 commits July 8, 2026 11:01
…noop

fix(shared): make ApiManager refresh paths actually replace the stale api
PR #1255 merged a mis-formatted translations file; every PR merge ref
now fails the format check. Formatted with biome.
The i18n commit in #1255 landed mis-formatted, so 'bun verify' fails on
staging and on every PR merge ref. Formatted with biome.
…ting

style(frontend): fix en.json formatting breaking bun verify on staging
…se-polling

Cancel abandoned phase executions (production CPU-leak root cause)
@netlify

netlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit f161a47
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a4e711a7b068600082887ca
😎 Deploy Preview https://deploy-preview-1261--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit f161a47
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a4e711a81bc7b000865bea1
😎 Deploy Preview https://deploy-preview-1261--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

ebma added 10 commits July 8, 2026 16:19
…-price-with-fastforex

# Conflicts:
#	apps/api/src/api/services/priceFeed.service.test.ts
#	apps/api/src/api/services/priceFeed.service.ts
#	docs/security-spec/03-ramp-engine/quote-lifecycle.md
#	docs/security-spec/06-cross-chain/fund-routing.md
… messages and logs

Provider (Avenia/BRLA, Alfredpay) failures raised during POST /v1/ramp/register,
/update and /start previously bubbled up as a plain Error, got masked to an opaque
`{"code":500,"message":"Internal server error"}`, and left no trace of which provider
call failed. A recent incident (BRLA returning `{"error":"user is blocked"}` on
createPixInputTicket) surfaced both gaps: the caller saw only a 500 and retried
uselessly, and the logs did not say which Avenia call rejected the account.

- Add ProviderApiError base class (packages/shared/.../providerApiError.ts) carrying
  provider/endpoint/method/status/responseBody. BrlaApiError and AlfredpayApiError now
  extend it and are thrown from their services' sendRequest paths. The message shape is
  kept identical (`Request failed with status '<code>'. Error: <body>`) so existing
  consumers (BRLA controller handleApiError, Alfredpay controller substring guards) still
  work.
- mapProviderFailure() in ramp.controller normalizes a ProviderApiError to 422 (upstream
  4xx — account/request rejected) or 502 (upstream 5xx/transport — provider unavailable)
  with a generic "payment provider" message; the raw upstream body is never forwarded to
  the caller. The register/update/start catches log the failing provider/endpoint/method/
  status (never query params, which may carry PII) and classify the event as
  provider_error instead of internal_error.
- Remove a stray console.log in createPixInputTicket.

Tests: mapProviderFailure unit tests (Avenia 4xx/5xx, Alfredpay, passthroughs); BRLA
controller regression test locking the message-format invariant. Security spec
(07-operations/api-surface.md) updated.
The whole-module @vortexfi/shared stub was never restored, so under bun's
process-wide module mocks it leaked into later test files. Files that resolve
real token configs at import (the Alfredpay corridor scenarios) then saw
EvmToken/evmTokenConfig as undefined and threw 'token config missing', which
surfaced as import errors and 500s on ramp registration in CI. Local runs
passed only because macOS file-discovery order was benign. Capture and restore
the real modules in afterAll, matching every other shared-mocking test; also
drop the vestigial ../../../index mock.
…-with-fastforex

Replace nabla swap price with fastforex
…ailures, log body

- Rename shared `ProviderApiError` -> `ProviderHttpError` (file providerHttpError.ts) to
  avoid colliding with the existing price-layer `ProviderApiError` in
  api/errors/providerErrors.ts.
- Restore the prototype chain in the base error (`Object.setPrototypeOf(this,
  new.target.prototype)`, `name = this.constructor.name`) so `instanceof` used by
  mapProviderFailure stays reliable after bundling — matches repo convention.
- Wrap `fetch()` in both BRLA and Alfredpay request paths: transport failures
  (DNS/timeout/connection reset) now throw the provider error with `status: 0`, so
  mapProviderFailure normalizes them to 502 instead of a generic 500.
- Log a truncated (300-char) provider response body in the structured log context so the
  failure reason (e.g. "user is blocked") is captured server-side; never returned to the
  caller. Keeps the security-spec claim accurate.
- Remove the duplicate `./squidrouter` export in the shared services barrel.

Tests updated for the new `providerResponseBody` log field, plus cases for the status-0
transport mapping and body truncation. Security spec wording updated.
…rror note

Address PR review nit: list POST /v1/ramp/update and /start with their full
prefixes instead of the ambiguous /update, /start shorthand.
…error-logging

Surface fiat-provider failures on ramp endpoints with clear messages and logs
@ebma ebma merged commit 2989ded into main Jul 8, 2026
10 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

Development

Successfully merging this pull request may close these issues.

3 participants