Skip to content

Release: develop -> main#270

Merged
Danswar merged 1 commit into
mainfrom
develop
Jun 8, 2026
Merged

Release: develop -> main#270
Danswar merged 1 commit into
mainfrom
develop

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Automatic Release PR

This PR was automatically created after changes were pushed to develop.

Commits: 1 new commit(s)

Checklist

  • Review all changes
  • Verify CI passes
  • Approve and merge when ready for production

* fix: gate JUSD gateway deposits when savings disabled

* fix: harden JUSD gateway deposit gating

* refactor(gateway): telemetry, SWR probe, and shared deposit-disabled guard

Improvements on top of the issue-263 Phase 1 gating:

- Telemetry: expose JuiceGatewayService.getMetrics() (blocked-deposit
  counters keyed by chain:routeShape + SavingsRateProbe gauge/failure
  counters) via the existing JSON /metrics endpoint. Closes the
  acceptance-criteria telemetry gap without adding a Prometheus dep.
- SavingsRateProbe: true stale-while-revalidate — serve the cached rate
  immediately and refresh in a coalesced background probe, so request
  latency never pays for the RPC. Adds an idle() test hook.
- Observability: warn-log the fail-open path when an integrated chain has
  no registered vault.
- DRY: extract isGatewayDepositDisabledError()/sendGatewayDepositDisabled()
  into endpoints/gatewayDepositGuard.ts and drop the duplicated error
  blocks in quote/swap/lpCreate/lpIncrease.

typecheck clean, eslint 0 errors, jest 73/73 green (+4 new tests).

* test(gateway): cover deposit-disabled guard and testnet gating

Tester gap-analysis follow-ups:
- gatewayDepositGuard.test.ts: isGatewayDepositDisabledError type guard
  (positive/negative/null) + sendGatewayDepositDisabled with and without
  error.message — closes the FALLBACK_DETAIL branch (now 100% coverage).
- JuiceGatewayService: assert the deposit gate also fires on Citrea
  Testnet (5115), not just mainnet.

jest 78/78 green.

* test(gateway): live Citrea integration test + clearOverride coverage

- SavingsRateProbe.integration.test.ts: opt-in (RUN_CITREA_INTEGRATION=1)
  live check against Citrea Mainnet — probe reads currentRatePPM()==0,
  Savings.save reverts with ModuleDisabled() (0x6dff2fe8), JUSD->cBTC is
  gated while JUSD->CTUSD direct conversion stays open. Skipped by default
  so the unit CI matrix stays deterministic. Closes the issue-263
  acceptance-criteria RPC integration gap.
- Cover clearOverride() (per-chain and clear-all) — SavingsRateProbe rises
  to 95.6% line coverage; remaining uncovered lines are the default
  ethers factory (exercised by the integration test) and defensive
  no-vault/swallow branches.

jest 80 passed, 3 skipped (integration), green.

* fix(probe): coalesce cold-cache probes; clear override gauge

Fixer-Architect (Big Brother loop) finding on PR #266:

- Thundering herd: the cold-cache path of getCurrentRate() bypassed the
  inFlight coalescing, so N concurrent first-callers per chain each fired
  SAVINGS()+currentRatePPM(). Route both the cold path and the background
  SWR refresh through a single shared getOrStartRefresh() promise — a burst
  of startup requests now triggers exactly one probe. Regression test added.
- clearOverride() now drops the override-seeded ratePpmByChain gauge entry
  so /metrics never reports a stale forced rate after the override lifts.

jest 81 passed, 3 skipped; SavingsRateProbe branch coverage 89.5%.

* style: apply prettier formatting to gateway gating files

* fix(probe): override must beat a late in-flight probe

Round-2 Fixer-Architect finding on PR #266: refresh() persisted the probed
rate to cache+gauge unconditionally. A probe that resolved after setOverride()
resurrected the pre-override rate, so /metrics contradicted the active override
and (cache not cleared by clearOverride) a stale rate could be served within
TTL after the override lifted. refresh() now skips the cache/gauge write while
an override is active. Deterministic regression test added.

jest green.

* fix(gateway): keep LP Gateway routing JUSD-only, decouple deposit gate

detectLpGatewayRouting/getInternalPoolToken were widened to JUSD ||
bridged stablecoin || JUICE, but the LP handlers
(handleGatewayLpCreate/handleGatewayLpIncrease) only convert literal
JUSD legs to svJUSD. Once Savings is re-enabled, a USDC/cBTC or
JUICE/cBTC LP would route into the svJUSD pool with a raw, unconverted
amount fed into Position.fromAmount*, producing wrong amount0Raw/
amount1Raw. The new deposit gate masks this only while disabled.

Revert both routing functions to JUSD-only so non-JUSD LP legs fall back
to standard (pre-existing) handling. Decouple the deposit gate from
detectLpGatewayRouting: lpCreate/lpIncrease now always call
rejectIfLpRouteRequiresJusdDepositDisabled (a no-op unless a JUSD/
bridged/JUICE deposit leg is present), so bridged/JUICE LPs are still
blocked while the Savings rate is zero.

Add regression tests asserting bridged/JUICE LP legs do not route
through the Gateway and are never mapped to svJUSD.

---------

Co-authored-by: Josh - Agents <josh-agends@Mini-von-Josh.fritz.box>
Co-authored-by: Daniel Padrino <danswarrior1@gmail.com>
@Danswar Danswar self-requested a review June 8, 2026 16:34
@Danswar Danswar merged commit bb8bbd5 into main Jun 8, 2026
6 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.

2 participants