Skip to content

chore: back-merge main → dev (pre-release 2026-08-05) - #2611

Merged
Hugo0 merged 82 commits into
devfrom
chore/backmerge-main-into-dev-20260805
Aug 5, 2026
Merged

chore: back-merge main → dev (pre-release 2026-08-05)#2611
Hugo0 merged 82 commits into
devfrom
chore/backmerge-main-into-dev-20260805

Conversation

@kushagrasarathe

Copy link
Copy Markdown
Contributor

Back-merge of prod hotfixes from main into dev before the next release, so dev is a clean superset of main. dev's localization layer is preserved throughout — every conflicted screen keeps its t() strings while adopting main's logic fixes.

What main had that dev did not (highlights)

Conflict resolution policy

Prod hotfix logic kept in every case; dev's i18n (t() strings, IntlWrapper) preserved on top. Notables:

  • MantecaPixQrDeposit — main's onDone exit contract + dev's localized strings
  • MantecaReviewStep — main's sell-rate display fix + dev's localized label
  • AppLock — kept dev's copy: its localization (Jul 24) postdates and supersedes main's friendlier-copy hotfix (Jul 23)
  • exchange-rate/route.ts — main's fetchDisplayRate; dev's dead Manteca-set import dropped
  • add/add test conflict on MantecaAddMoney.test.tsx — split into two files: dev's denomination suite + main's exit-contract regression suite (MantecaAddMoney.exits.test.tsx)
  • main's new test suites wrapped in IntlWrapper; NITA test ported to dev's plural resolveCampaigns API; localeApplied() mocked so splash asserts don't race dev's locale hold

Verification

  • npm run typecheck clean
  • npm test: 201/201 suites, 2612 passed, 0 failed
  • pnpm prettier --check . clean

Supersedes #2586 (stale, 53 commits behind main).

innolope-dev and others added 30 commits July 23, 2026 14:27
'Peanut is locked / Could not confirm it is you' read as scary and
technical. Reframe as a plain log-in ask: 'Welcome back!' with
'Please log in (again) to access the app.' and a 'Log in' button.
…ssification, full auth clear

Ports the applicable subset of mobile-release 9cdb8ae:
- demo: add /rain/cards fixture so overview consumers never see the {}
  fallback (crashes deref of .status/.cards — PEANUT-UI-RM6 on native)
- passkey: classify WebKit 'Load failed' fetch errors as NETWORK instead
  of the LOGIN_ERROR modal (PEANUT-UI-QV6)
- auth: clearAuthState delegates to clearAuthToken so native token stores
  are cleared too, not just the jwt cookie
Review follow-up: clearAuthToken's Preferences/cookie-jar clears are async;
make clearAuthState await them so callers can't proceed on a half-cleared
session (in-memory clear was already synchronous).
…new deposit

The BRL/PIX success screen wired both its exits — the Done CTA and the
NavHeader back arrow — to onBack, which the parent defines as
setUrlState({ step: 'inputAmount' }). Tapping Done on a completed deposit
therefore dropped the user back on the amount input, i.e. straight into a
new deposit: the deposit read as unfinished and invited an accidental
repeat payment.

Give the screen a distinct onDone exit (router.replace('/home')). replace,
not push, so device/browser back can't pop into the finished flow — whose
step=showQR would redirect to inputAmount and reproduce the same bug.
The expired-QR "Go back" keeps onBack; that one is a real go-back.
Users whose Bridge customer owes 'additional verification' tasks (the
hosted Persona re-verification and/or a ToS (re-)acceptance) could only
see them in Bridge's dashboard — in-app there was nothing actionable:
the activation card deliberately stands down for anyone who can already
transact, and advisory (future-dated) tasks arrive as orphan nextActions
no rail references.

New self-hiding PendingVerificationTasks card on /home, sibling of
ActivationCTAs (outside its stand-down on purpose). It reads top-level
capability nextActions — the only surface that catches both blocking
tasks and advisory orphans — and renders one row per task:

- accept-tos → mounts the existing BridgeTosStep wholesale (link fetch,
  iframe, signedAgreementId confirm, rail await)
- bridge-hosted (new kind, BE api#TBD) → exchanges the key for Bridge's
  hosted verification URL via start-action and opens it in the existing
  IframeWrapper (its Persona 'complete' postMessage handler already
  covers completion; refetch user on close)

Old-BE tolerance: an older backend never emits bridge-hosted and only
emits accept-tos for blocking users — the card simply renders less.
No NEXT_PUBLIC_API_VERSION bump (additive contract).
Adversarial review found the reported defect surviving one state earlier.
'processing' means the poller saw stage >= 2 — the fiat has already left
the user's bank — and the spinner's only affordance was a back arrow wired
to onBack, i.e. the amount input, still pre-filled from the URL with the
amount just paid. One tap from paying twice, while the first deposit is
mid-settlement. Route it to onDone like the settled state.

Leaving is safe: the credit is posted server-side by the webhook/poller,
not by this screen's polling.

Also cover the line that actually fixes the user-visible bug — the parent's
router.replace('/home'). Without it a regression that rewires onDone back
to setUrlState({ step: 'inputAmount' }) restores the bug with a green suite.
- Open flows are snapshotted at tap time and mounted ABOVE the
  self-hiding early return: the task list re-derives from the ~4s user
  auto-refresh, and the previous shape force-unmounted an OPEN ToS
  modal / hosted iframe mid-verification when the task flapped away.
  The card hides; the flow finishes.
- Each ToS row opens the modal for ITS variant (a user can owe base AND
  SEPA v2 simultaneously — the single find() gave the second row the
  first row's copy).
- IframeWrapper ignores window messages while hidden: surfaces keep a
  wrapper mounted after manual close (multi-phase KYC ToS), and a
  sibling iframe's signedAgreementId event fired BOTH handlers — double
  ToS confirms + phantom flow transitions. This PR adds the second ToS
  surface on /home that made the latent hazard reachable, so it carries
  the guard.
- Copy is population-aware (advisory 'keep … available' vs blocking
  'enable'), start-action failures show friendly copy + resync the user
  (a 403 means the action aged out), malformed effectiveDate renders no
  deadline instead of 'Invalid Date', errors clear when the task set
  changes, and all task buttons disable while the hosted URL fetch is
  in flight (no stacked modals).
Aleks: the /home card should be closable like the carousel CTAs, without
losing the tasks. The X persists the dismissal per task-key set (a
DIFFERENT set of pending tasks re-shows the card); the same component
mounts non-dismissibly under Profile → Unlocked regions, which is where
dismissed users find their tasks again.
…0772)

Carry locale + invite code + campaign tag + destination path from mobile
web through the app-store hop. Android rides the Play Install Referrer
(app-local InstallReferrerPlugin, resolve-once + 5s timeout); iOS rides a
clipboard hand-off double-gated by prompt-free UIPasteboard checks
(hasStrings + detectPatterns probable-web-url) so unrelated clipboard
content never triggers the paste alert. One-shot restore on first launch:
30-day normalized inviteCode/campaignTag cookies (setup flow already
reads them), locale persisted under the app-locale key the upcoming
in-app i18n (on dev) reads — @capacitor/preferences added so the
preference lands in native storage and the plugin ships in this binary —
and navigation to the destination only when no real deep link landed.
Restore is not awaited in init so a pending paste prompt or slow
referrer service can never block the push listener or splash hide.
Base is main (not dev) so the native release pipeline can cut a
testable binary; back-merge to dev will need a small locale-handling
reconcile with dev's src/i18n/app (marked in deferred-link.ts).
ENS names can hold a different address per chain (ENSIP-11). The
withdraw flow resolved every name to its mainnet record and sent on
the selected chain — funds loss when the name points elsewhere on
that chain (reported by an external ENS-savvy tester with
test.ses.eth: mainnet and Arbitrum records differ).

- resolveEns/validateAndResolveRecipient accept the destination
  chainId and forward it to /ens/:name?chainId= (api-ts #1236).
- Switching chains after typing a name re-resolves it for the new
  chain instead of silently keeping the old address.
- The resolved address is now shown under the input as soon as the
  name validates, and again in the compatibility warning modal — the
  user sees where funds go BEFORE any warning/confirm step.

(cherry picked from commit f2c0aac)
…flight

Between switching chains and the new resolution landing, recipient.address
still holds the previous chain's address and Review stayed clickable —
the exact wrong-chain send this PR fixes, in a narrow race window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 679c359)
crypto-withdraw-confirm.test.tsx (new on dev) mocks general.utils with only
the functions the page used at the time; the compatibility-modal change now
also renders printableAddress from that module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 4e56b63)
The backend's new consent_records ledger stores what the user was
actually shown, so every acceptance surface now echoes the displayed
document versions instead of letting the server assume: signup sends
terms+privacy via the x-accepted-legal header (the ZeroDev SDK owns the
register/verify body), card apply sends the exact regional doc set from
CardTermsScreen, and versions+hashes are generated from the src/content
legal frontmatter at build time — no hardcoded versions anywhere.

Adds the ToS §17 re-consent flow: a blocking click-through modal when a
published document version moves past the user's last provably-accepted
one. Fails open — a dead consent endpoint never locks the app.

(cherry picked from commit e1bdcf9)
The single-line entries exceeded printWidth 120, so every predev run
left a prettier-dirty tree; one field per line keeps the generated file
byte-stable under formatting.

(cherry picked from commit c0a3448)
A logout followed by login as a different account in the same SPA
session skipped the second user's consent-status check.

(cherry picked from commit a0bccaf)
Every dev/build entrypoint now regenerates the legal-version constants
(dev:clean, dev:fallback, analyze bypassed the predev hook and could
ship stale version/hash echoes). ReConsentModal state is fully isolated
per account: switching users clears the doc list, checkbox, and error,
a slow status response for the previous account is discarded, and the
checkbox resets after a successful acceptance.

(cherry picked from commit be5545b)
…ent failures to Sentry

Review follow-ups (Hugo on #2456):

- ReConsentModal tests: fail-open on status failure (must never lock the
  app), accept failure keeps the retry path, stale-response discard on
  account switch (regression already caught once in review), undisplayable
  slug filtering, once-per-user-per-session check.
- consent service tests: pin the exact signup/card doc sets per region and
  that every echoed entry carries its own generated version+hash — silent
  drift here mis-ledgers legal consent.
- Both catch blocks now Sentry.captureException instead of console.error:
  a systematic /accept failure traps every user behind an undismissable
  modal, and prod must be able to see that.

(cherry picked from commit f06fd71)
With ToS + hosted verification pending together the stacked card grew
tall; reuse the HomeCarouselCTA embla setup so each task is its own
full-width slide (identical footprint to the single-task card, dots +
swipe only when there's more than one). jest.setup gains matchMedia and
IntersectionObserver stubs — embla needs both at init and jsdom has
neither, same gap the existing ResizeObserver stub covers.
Lock/cancel sign a withdrawal whose purpose is draining Rain collateral
back to the wallet, but since cb302d3 removed the smartBalance:0n
input, routing fell through to live-balance strategy selection: any
user whose wallet USDC covered their spending power routed smart-only,
tripped the modals' own strategy check, and could neither lock nor
cancel ('Unexpected withdrawal strategy', prod, 3 users affected).

Also fail closed when the card overview hasn't loaded: undefined read
as zero spending power, silently skipping the withdrawal and getting
the action rejected server-side ('Withdrawal signature required').

Port of the dev-based #2570 (closed in favor of this main-based hotfix;
that branch holds the i18n-ified variant for the dev back-merge).
external-wallet withdrawals have no recipient username or parsed
identifier, so the drawer's userName was undefined and the header
rendered the literal string. fall back to the recipient address
(shortened by printableUserHandle) like recipientName already does.
Now that tasks are separate carousel slides, one X hiding ALL of them
reads as a bug — each slide gets its own X and the preference stores
dismissed task keys instead of the joined set. A dismissed key stays
hidden on /home until the task resolves (keys are a tiny stable
vocabulary, so no pruning); the Profile mount still shows everything.
…lash

a cold mount (mobile reopening the pwa reloads the page) showed the raw
address for a beat while the lookup ran — pay the 404, engage the client
fallback, resolve, flip. persist resolved names for 24h and paint them
immediately on mount while the lookup revalidates. an authoritative
server "no name" evicts the entry so stale names can't mask reality.
…hape

CodeRabbit: the JSDoc still described the pre-carousel comma-joined
set string.
coderabbit: JSON.parse('null') passes the try/catch but null[address]
throws during render. treat any non-object root as an empty cache.
…k href in sync

code-review findings: (1) with the server route erroring (today's prod
state) the only eviction path was server null, which never fires — a
client lookup settling '' (justaname's not-found) now also masks and
evicts, so a released/transferred ens name can't keep painting from
cache. (2) AddressLink's effect never reset urlAddress on the
name→address downgrade, leaving the href pointing at a name the address
may no longer own — reachable now that cached names can be evicted
mid-mount. (3) server '' responses treated same as null.
…0729-155858

content: publish latest to production (src/content → peanut-content@fc31f07)
…0729-160449

content: publish latest to production (src/content → peanut-content@f33d67f)
abalinda and others added 23 commits August 4, 2026 11:33
…tor branch

The /api/exchange-rate route and the Capacitor branch of useExchangeRate were
hand-mirrored copies that had already diverged twice: the route's cross-pair
branch in fetchFromCurrencyPrice was unreachable dead code (every caller passes
USD on one side), and the Frankfurter fallback applied the 0.995 spread twice
on the route's non-provider cross path vs once in Capacitor. Hoisting the
Capacitor implementation into utils/currency.ts (displayRateFromPrices +
fetchDisplayRate) makes future drift impossible and deletes ~115 lines of
route helpers, including the stale claim that callers stack a Peanut fee on
the fallback spread (the developer fee is 0).

Rate math is now pinned once in utils/__tests__/currency.test.ts; the route
test shrinks to wiring (delegation, 400 on bad params, 500 on total failure).
…l displayed

The commit test only asserted createOnramp was called; now it pins the
submitted amount to the same string the modal rendered, so a regression that
converts or mangles the amount between display and submission fails the suite.
…2026-05-08

Comments across the onramp-quote surface asserted Peanut charges a 50bps
developer fee. BRIDGE_DEVELOPER_FEE_RATE was zeroed on 2026-05-08 (the fee was
charged without in-app disclosure), so netRate === grossRate and
applyBridgeCrossCurrencyFee is an identity function today. Rewritten to state
the fee is currently 0 and that the net/gross split and fee helpers are kept
deliberately for the planned FX-margin re-enable. No behavior change.
…de of the pin

Adversarial review caught that the commit-path test asserted only the
submitted argument; the displayed-equals-submitted claim needs both sides.
…lls useCurrency into the server route

Vercel's webpack build rejected the API route importing utils/currency:
it re-exports through hooks/useCurrency (useState/useEffect), which is
client-only, and an RSC module may not depend on it. The local build had
passed on a stale .next cache. fx.utils.ts imports only actions/currency,
so both the server route and the Capacitor client branch can share it.
…ck pairs were breaking the reciprocal contract

CodeRabbit caught that the Frankfurter fallback multiplied each requested
orientation by 0.995, so A→B and B→A composed to 0.995² instead of 1 — the
exact asymmetry this PR exists to remove, resurfacing on every
fallback-served pair. The fallback now synthesizes a sell-side USD-leg price
per currency (mid × 0.995) and converts through displayRateFromPrices, the
same policy as the provider path, so one pair implies one price everywhere.
…ting

fix(add-money): PIX deposit Done exits to home instead of starting a new deposit
…sell-both-directions

fix: quote both exchange-rate display orientations off the sell rate + DRY the display-rate path
The proxy 307'd /setup → /home whenever a jwt-token cookie merely existed,
while (mobile-ui)/layout.tsx redirects /home → /setup whenever /users/me says
logged-out. Any 'cookie present but session dead' state (expired JWT,
half-completed signup, API 5xx/offline) ping-ponged forever — and the installed
PWA cold-starts on /home (manifest start_url) with no URL bar to escape, so
logged-out PWA users were fully locked out (TASK-21050).

Cookie presence can't prove a session. The authenticated-at-/setup case is
already handled client-side by the existing-session prompt in
(setup)/setup/page.tsx (trusts /users/me), which the middleware bounce was
shadowing on web. Delete the bounce; add an e2e regression test pinning
'stale cookie on /setup must stay on /setup'.
… /setup

Adversarial review finding: with the middleware bounce gone, an authenticated
iOS-Safari (non-PWA) user who opens /setup mounts the (setup) layout, which arms
showIosPwaInstallScreen. 'Continue as X' soft-navs to /home with the singleton
store alive, and (mobile-ui) renders the escape-proof ForceIOSPWAInstall wall.
Clear the flag in the continue handler — this visit was not a setup session.
(Clearing on layout unmount instead would break the intended post-signup wall.)
Adversarial review finding: URL-based assertions are vacuous on the old code —
a 401 from /users/me clears the stale cookie client-side and the loop's own
/home → /setup leg can restore the URL before it's read. Assert the raw document
status via context.request (maxRedirects: 0): old middleware 307s, fixed code
200s — deterministic, timing-free, API-free. Stub /users/me to 401 for the
browser-side smoke so the forged token never leaves for a real API.
fix: logged-out PWA /setup redirect loop (TASK-21050)
…ble; source-matched iframe messages

/code-review (high) 2026-08-04:

Finding 1 (CONFIRMED): handleHostedClose('tos_accepted') only refetched the
user — the resolver is pure, so the embedded acceptance was never recorded
until a Bridge webhook landed, and tapping the still-visible ToS card 409'd
into 'Could not load terms'. The hosted path now runs
confirmBridgeTosAndAwaitRails (the same canonical confirm every other ToS
surface uses) with the iframe staying open; on failure it still resyncs.

Finding 2 (CONFIRMED): a blocking task's dismissal fingerprint is constant
over time (accept-tos||due-now), so an old dismissal would hide a NEW
same-variant requirement while the user's rails are gated — and the orphan
bridge-hosted task has no other surface outside Profile. There is nothing
unique to fingerprint a new due-now round WITH, so the fix is one level up:
blocking (due-now) tasks no longer render an X and ignore stored
fingerprints (pre-fix localStorage entries included); advisory dismissal
behavior is unchanged.

Finding 3 (PLAUSIBLE): IframeWrapper now reacts only to messages whose
source is its OWN iframe instead of gating on visible — the sibling
double-confirm protection survives (now even against two concurrently
VISIBLE wrappers, which the visible-guard let through and finding 1's new
confirm call would have turned into a double confirm), while a completion
landing as the modal hides is no longer dropped. New IframeWrapper spec pins
own-handled / sibling-ignored / sourceless-ignored.

Finding 4: formatDeadline duplicated AdvisoryPreemptModal's formatter with a
different month style. One shared formatEffectiveDate in format.utils serves
both surfaces (long month everywhere); the add-money suite's format.utils
mock now spreads requireActual so partial mocks don't break on new exports.
…he PWA

The button synthesized an <a target=_blank> and called .click() — an untrusted
event. Chrome grants the popup (transient activation) but excludes untrusted
link activations from WebAPK link capturing, so every tap opened this same
screen in a plain browser tab instead of the installed app: the logged-out
'Open Peanut app' boomerang that TASK-21050's middleware fix unmasked
(session evidence: 4 identical /setup loads at ~1.4s cadence per tap).

A trusted tap on a real in-scope target=_blank anchor is what link capturing
honors. Styled with the same btn classes the Button would compose.
…rable

Adversarial review: the anchor fix bets on Chrome's link-capturing behavior
that can't be verified headless, and a failed handoff (plain new tab of the
same screen) was indistinguishable from success in telemetry. Repeat
pwa_open_app_clicked fires within one session now flag a device where capture
is not engaging. Capture runs without preventDefault, so the trusted
navigation is untouched.
…chor

fix: real anchor for 'Open Peanut app' — untrusted synthetic clicks skip WebAPK handoff
…regressions

fix(demo+passkey): /rain/cards demo fixture, WebKit 'Load failed' classification, full auth clear
Adds the 9JA artwork plus the BADGES entry and the ?campaign=naija mapping.
No INVITE_CODE_TO_CAMPAIGN_MAP entry: no invite code 'naija' exists, so that
route would dead-end on 'Invalid Invite Code'.

Merge the peanut-api-ts side first — this renders the badge but cannot award
it.
Adds the tereré jarra artwork plus the BADGES entry and the ?campaign=terere
mapping. Same shape as NAIJA: no INVITE_CODE_TO_CAMPAIGN_MAP entry, because no
invite code 'terere' exists.

Merge the peanut-api-ts side first — this renders the badge but cannot award
it.
feat(badges): NAIJA and TERERE badge artwork and rendering
…n-tasks

feat(home): pending Bridge verification tasks card
…into-dev-20260805

# Conflicts:
#	src/app/(mobile-ui)/card/page.tsx
#	src/app/(mobile-ui)/withdraw/crypto/page.tsx
#	src/app/api/exchange-rate/route.ts
#	src/components/AddMoney/components/MantecaAddMoney.tsx
#	src/components/AddMoney/components/MantecaPixQrDeposit.tsx
#	src/components/AddMoney/components/__tests__/MantecaAddMoney.test.tsx
#	src/components/AddMoney/components/__tests__/MantecaPixQrDeposit.test.tsx
#	src/components/Claim/Link/views/MantecaReviewStep.tsx
#	src/components/Global/AppLock/index.tsx
#	src/components/Global/GeneralRecipientInput/index.tsx
#	src/components/Global/IframeWrapper/index.tsx
#	src/components/Setup/Views/InstallPWA.tsx
#	src/components/Withdraw/views/Initial.withdraw.view.tsx
#	src/constants/analytics.consts.ts
#	src/services/rain.ts
#	src/utils/general.utils.ts
- port the NITA creator-link test to dev's plural resolveCampaigns API
- wrap main's new suites (IframeWrapper, LockCardModal, PIX exits) in IntlWrapper
- mock localeApplied() so splash-hide asserts don't race dev's 2s locale hold
- add useRouter to the MantecaAddMoney nav mock (needed by main's onDone routing)
- split the add/add MantecaAddMoney test conflict into two files: denomination
  suite (dev) + exit-contract suite (main)
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview Aug 5, 2026 2:24pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6ae971b-9c5f-4e99-936b-8024e876c230

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7008.58 → 7057.75 (+49.17)
Findings: +16 net (+194 new, -178 resolved)

🆕 New findings (194)

  • critical complexity — src/app/(mobile-ui)/card/page.tsx — CC 128, MI 56.73, SLOC 495
  • critical complexity — src/app/(mobile-ui)/add-money/[country]/bank/page.tsx — CC 112, MI 57.43, SLOC 392
  • critical complexity — src/utils/demo-api.ts — CC 107, MI 59.5, SLOC 917
  • critical complexity — src/services/rain.ts — CC 67, MI 59.72, SLOC 268
  • critical complexity — src/components/Profile/views/UnlockedRegions.view.tsx — CC 66, MI 60.12, SLOC 220
  • critical complexity — src/app/(setup)/setup/page.tsx — CC 63, MI 55.91, SLOC 231
  • critical complexity — src/components/AddMoney/components/MantecaAddMoney.tsx — CC 59, MI 56.98, SLOC 224
  • critical complexity — src/features/payments/shared/components/PaymentSuccessView.tsx — CC 58, MI 55.88, SLOC 171
  • critical complexity — src/components/Setup/Views/InstallPWA.tsx — CC 56, MI 62.94, SLOC 183
  • critical complexity — src/utils/deferred-link.ts — CC 56, MI 58.15, SLOC 204
  • high hotspot — src/app/(mobile-ui)/qr-pay/page.tsx — 97 commits, +1166/-1116 lines since 6 months ago
  • high hotspot — src/app/(mobile-ui)/add-money/[country]/bank/page.tsx — 59 commits, +587/-496 lines since 6 months ago
  • high complexity — src/components/Home/PendingVerificationTasks.tsx — CC 49, MI 61.9, SLOC 129
  • high hotspot — src/app/(mobile-ui)/card/page.tsx — 43 commits, +1064/-485 lines since 6 months ago
  • high hotspot — src/components/Badges/badge.utils.ts — 41 commits, +399/-120 lines since 6 months ago
  • high hotspot — src/constants/analytics.consts.ts — 41 commits, +345/-12 lines since 6 months ago
  • high method-complexity — src/app/(mobile-ui)/layout.tsx:41 — CC 39 SLOC 75
  • high complexity — src/hooks/useNativePlugins.ts — CC 39, MI 61.61, SLOC 191
  • high hotspot — src/app/(mobile-ui)/home/page.tsx — 37 commits, +239/-148 lines since 6 months ago
  • high hotspot — src/components/AddMoney/components/MantecaAddMoney.tsx — 37 commits, +354/-209 lines since 6 months ago

…and 174 more.

✅ Resolved (178)

  • src/app/(mobile-ui)/card/page.tsx — CC 127, MI 56.82, SLOC 492
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx — CC 108, MI 57.42, SLOC 394
  • src/utils/demo-api.ts — CC 106, MI 59.49, SLOC 908
  • src/components/Profile/views/UnlockedRegions.view.tsx — CC 66, MI 60.11, SLOC 220
  • src/services/rain.ts — CC 65, MI 59.85, SLOC 266
  • src/app/(setup)/setup/page.tsx — CC 63, MI 56.02, SLOC 229
  • src/components/AddMoney/components/MantecaAddMoney.tsx — CC 58, MI 56.56, SLOC 219
  • src/components/Setup/Views/InstallPWA.tsx — CC 56, MI 62.66, SLOC 187
  • src/features/payments/shared/components/PaymentSuccessView.tsx — CC 56, MI 55.94, SLOC 171
  • src/app/(mobile-ui)/qr-pay/page.tsx — 95 commits, +1156/-1110 lines since 6 months ago
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx — 57 commits, +572/-483 lines since 6 months ago
  • src/app/api/exchange-rate/route.ts — CC 48, MI 45.56, SLOC 172
  • src/app/(mobile-ui)/card/page.tsx — 42 commits, +1056/-483 lines since 6 months ago
  • src/app/(mobile-ui)/layout.tsx:40 — CC 39 SLOC 75
  • src/constants/analytics.consts.ts — 39 commits, +343/-12 lines since 6 months ago
  • src/components/Badges/badge.utils.ts — 37 commits, +375/-116 lines since 6 months ago
  • src/proxy.ts — CC 37, MI 56.85, SLOC 119
  • src/components/AddMoney/components/MantecaAddMoney.tsx — 36 commits, +349/-208 lines since 6 months ago
  • src/app/(mobile-ui)/home/page.tsx — 35 commits, +230/-146 lines since 6 months ago
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx — 34 commits, +422/-244 lines since 6 months ago

…and 158 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Home/PendingVerificationTasks.tsx 0.0 9.2 +9.2
src/components/Global/ReConsentModal/index.tsx 0.0 8.2 +8.2
src/utils/deferred-link.ts 0.0 8.0 +8.0
src/app/dev/deferred/page.tsx 0.0 6.7 +6.7
src/utils/fx.utils.ts 0.0 6.0 +6.0
src/constants/legal-versions.generated.ts 0.0 4.9 +4.9
src/components/Global/ReConsentModal/utils.ts 0.0 4.9 +4.9
src/services/consent.ts 0.0 4.7 +4.7
src/utils/bridge-tasks.utils.ts 0.0 2.8 +2.8
src/utils/clipboard-detect.ts 4.3 4.8 +0.5
src/utils/general.utils.ts 18.2 18.8 +0.5
src/components/Badges/badge.utils.ts 11.4 11.9 +0.5
src/app/api/exchange-rate/route.ts 10.9 8.1 -2.8

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2615 ran, 0 failed, 0 skipped, 45.4s

📊 Coverage (unit)

metric %
statements 63.8%
branches 47.4%
functions 53.4%
lines 64.3%
⏱ 10 slowest test cases
time test
3.3s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.2s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.4s src/utils/__tests__/auth-token.test.ts › authReady does not park — hydrates the plain token without an unlock
0.4s src/utils/__tests__/sentry.utils.test.ts › defaults to the client budget under a browser global
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/utils/__tests__/sentry.utils.test.ts › still lets a per-call timeoutMs win over the default
0.3s src/utils/__tests__/auth-token.test.ts › ignores the guarded marker and falls back to the plain token
0.3s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@Hugo0
Hugo0 merged commit e64bd37 into dev Aug 5, 2026
19 of 21 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.

6 participants