Skip to content

fix(balance): keep last-known spendable total instead of rendering $0 - #2482

Open
innolope-dev wants to merge 6 commits into
mainfrom
fix/spendable-balance-last-known-main
Open

fix(balance): keep last-known spendable total instead of rendering $0#2482
innolope-dev wants to merge 6 commits into
mainfrom
fix/spendable-balance-last-known-main

Conversation

@innolope-dev

@innolope-dev innolope-dev commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Rebased onto main from #2481 (which was branched off dev; force-push is blocked by repo rules, hence a new branch). Supersedes #2481 — identical 3 commits, no dev-only work dragged along.

Targets main because this needs to ship in the next mobile build, and main/dev have diverged (main is 53 ahead of dev, dev 26 ahead of main).

The bug

The home screen's headline number is smartAccountBalance + rainSpendingPower + rainInTransit. Both Rain terms come from a single /rain/cards call, and rainCentsToUsdcUnits(undefined) folds them to 0n — so a failed fetch was indistinguishable from "this user has no collateral".

For a card user whose funds the auto-balancer swept into collateral, the smart account is legitimately 0, so the total cratered to a confident, wrong $0 — no error, no retry affordance. Reported as mobile apps showing zero balance while activity loaded correctly (activity is a different endpoint and server-computed).

Pairs with peanutprotocol/peanut-api-ts#1226, but ships independently: balanceUnavailable is an optional field and absent is treated as "available".

Changes

  • lastKnownSpendable.ts (new) — per-user persisted last-known-good total on the existing {userId}:user-preferences store. A cold start paints the previous number immediately and corrects it in the background.
  • useWalletisRainReady gate; display resolves as stableSpendable ?? (isRainReady ? liveSum : cachedValue).
  • isRainBalanceKnown in balance.utils — shared by the display path and the spend path so they can't drift on "can this Rain figure be trusted".
  • useRainCardOverview — stop the 30s poll for users with no card application. It ran for every logged-in user, making this the most-called endpoint and the top source of client 10s timeouts, for a payload that reads hasApplication: false every time.
  • spendPreflight — a Rain outage was reported as error_kind: 'insufficient', hiding it inside the card-withdraw failure rate. Now rain-balance-unavailable. Analytics only; behaviour unchanged (blocking is correct — you can't size a collateral withdrawal you can't see — and users already get "try again in a few seconds", not "not enough funds").
  • features/limits/utils.ts — "You can add up to $0" when the remaining limit was merely unknown. Omits the sentence instead.
  • home/page.tsx — a cached number renders at 50% opacity so it reads as refreshing, not asserted.

Safety

The cache is display-only. Every affordability gate still runs on the live availableSpendableBalance, so a stale number can't green-light a spend into an orphan charge — there's a test pinning that. The cache is only written from a fully-settled read where Rain actually answered.

Two bugs caught in self-review

  1. Demo mode makes no /rain/cards call, so isRainReady was permanently false — the demo home screen would have sat on a loader forever. Verified the regression test catches it (reverting just that clause fails that test and nothing else).
  2. writeLastKnownSpendable was inside a setState updater, which StrictMode double-invokes. Moved to a ref-guarded write.

Testing

Full suite on the main base: 155 suites, 2073 passed, 3 skipped. Typecheck and prettier clean. 15 new tests across useWalletSpendable.test.tsx and balance.utils.test.ts.

Summary by CodeRabbit

  • New Features
    • Added a “stale spendable balance” state: when showing cached values, the displayed balance is now visually dimmed until live data is ready.
    • Wallet spendable display now seeds from a persisted last-known value on cold start for smoother rendering.
  • Bug Fixes
    • Rain “balance unavailable/unknown” is no longer treated as an authoritative zero for availability decisions.
    • Limit warnings avoid blank or misleading messaging when remaining limits are unknown.
    • Reduced unnecessary Rain polling when there is no application.
  • Tests
    • Added regression coverage for cached/stale spendable behavior, demo mode, user switching, and updated balance/limit edge cases.

The home screen's headline number is smart-account balance + Rain
spendingPower + Rain inTransit. Both Rain terms come from a single
/rain/cards call, and rainCentsToUsdcUnits(undefined) folds them to 0n —
so a failed fetch was indistinguishable from 'this user has no
collateral'. For a card user whose funds the auto-balancer swept into
collateral, the smart account is legitimately 0, and the total cratered
to a confident, wrong $0 with no error and no retry affordance.

/rain/cards times out at 10s for 621 users (PEANUT-UI-QD5), on web and
native alike; native compounds it because the Android WebView GET path
is edge-blocked on some devices (PEANUT-UI-R5F).

React Query retains the last good data across a failed refetch, so this
only bit on a session's first load. Persist the last fully-settled total
per user and seed the display from it, so a cold start paints the
previous number immediately and corrects it in the background. The
cached value is display-only and dimmed while stale — every
affordability gate still runs on the live availableSpendableBalance, so
a stale number can never green-light a spend into an orphan charge.
…r non-card users

Follow-on to the last-known-spendable cache, pairing with the API's new
`balanceUnavailable` flag.

- treat a response that arrived but couldn't read Rain (flag set, no
  balance to fall back on) as not-ready, so the displayed total holds the
  last-known value instead of summing an absent spendingPower as 0. A
  null balance WITHOUT the flag stays a real zero — a user with no card
  must still see their plain smart-account total.
- a served-but-stale balance is used as-is: it is fresher than the FE's
  own cache.
- stop the 30s /rain/cards poll for users with no card application. It
  ran for every logged-in user and made this the most-called endpoint,
  and with it the top source of client-side 10s timeouts, for a payload
  that reads `hasApplication: false` every time. Card users keep the 30s
  cadence, and the user_rail_status_changed WebSocket invalidation
  resumes polling the moment someone applies.
- guard on `!!rainOverview` rather than `!== undefined`: a null JSON body
  would pass the stricter check and then throw on property access.
…limits as $0

Two more instances of the same unknown-coerced-to-zero shape.

- extract isRainBalanceKnown into balance.utils so the display path
  (useWallet) and the spend path (useSpendBundle) answer 'can this Rain
  figure be trusted' identically. They were never allowed to disagree.
- spendPreflight: when the overview is missing, rainSpendingPower arrives
  as 0n and a Rain outage lands in the insufficient branch. Blocking is
  still the right call — we can't size a collateral withdrawal we can't
  see, and the user already gets BALANCE_SETTLING_MESSAGE ('try again in
  a few seconds'), not a false 'not enough funds'. But reporting it as
  error_kind 'insufficient' hid the outage inside the card-withdraw
  failure rate; it now reports 'rain-balance-unavailable'.
- limits: 'You can add up to $0' when remainingLimit is merely unknown.
  Omit the sentence rather than invent a figure.
@innolope-dev innolope-dev self-assigned this Jul 22, 2026
@vercel

vercel Bot commented Jul 22, 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, Comment Jul 24, 2026 4:22pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a15fbb57-6c2d-4487-b91c-1d4072e816a2

📥 Commits

Reviewing files that changed from the base of the PR and between fcf5854 and 1334166.

📒 Files selected for processing (6)
  • src/hooks/wallet/useSpendBundle.ts
  • src/hooks/wallet/useWallet.ts
  • src/services/rain.ts
  • src/utils/__tests__/balance.utils.test.ts
  • src/utils/balance.utils.ts
  • src/utils/general.utils.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/services/rain.ts
  • src/utils/balance.utils.ts
  • src/utils/general.utils.ts
  • src/utils/tests/balance.utils.test.ts
  • src/hooks/wallet/useWallet.ts

📝 Walkthrough

Walkthrough

useWallet distinguishes known, unavailable, cached, and live Rain balances, persists settled spendable totals, exposes staleness to the UI, and reports unavailable-balance analytics separately. Rain polling and limit warning rendering now handle unknown data explicitly.

Changes

Wallet spendable balance

Layer / File(s) Summary
Rain balance contracts and readiness
src/services/rain.ts, src/utils/balance.utils.ts, src/utils/general.utils.ts, src/hooks/wallet/lastKnownSpendable.ts, src/utils/__tests__/balance.utils.test.ts
Rain overview data includes unavailability metadata, readiness is centralized, and display-only spendable totals are persisted per user.
Freshness-aware spendable state
src/hooks/wallet/useWallet.ts
useWallet uses settled live totals when Rain is ready, otherwise cached totals, and exposes isSpendableBalanceStale.
Wallet display integration and regression coverage
src/app/(mobile-ui)/home/page.tsx, src/hooks/wallet/__tests__/useWalletSpendable.test.tsx
The home balance dims stale cached values, with coverage for cache, availability, account switching, demo mode, expiration, and affordability.
Rain-aware spend preflight analytics
src/hooks/wallet/spendPreflight.ts, src/hooks/wallet/useSpendBundle.ts
Spend strategy resolution receives Rain balance knowledge and emits rain-balance-unavailable for unavailable readings.

Rain overview polling

Layer / File(s) Summary
Application-aware polling
src/hooks/useRainCardOverview.ts
The 30-second refetch interval is disabled when no Rain application exists and retained otherwise.

Limit warning rendering

Layer / File(s) Summary
Unknown remaining-limit handling
src/features/limits/utils.ts
Unknown remaining limits no longer render as zero or add an empty warning item.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: jjramirezn

Sequence Diagram(s)

sequenceDiagram
  participant WalletUI
  participant useWallet
  participant RainOverview
  participant Preferences
  WalletUI->>useWallet: request spendable balance
  useWallet->>RainOverview: read current overview
  useWallet->>Preferences: read last settled total
  Preferences-->>useWallet: return cached total
  useWallet-->>WalletUI: return balance and stale state
  useWallet->>Preferences: write settled live total
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the core fix: preserving the last-known spendable total instead of showing $0 when Rain balance data is unavailable.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/spendable-balance-last-known-main

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


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

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 6270.06 → 6278.41 (+8.35)
Findings: +1 net (+44 new, -43 resolved)

🆕 New findings (44)

  • critical complexity — src/app/(mobile-ui)/home/page.tsx — CC 67, MI 62.92, SLOC 202
  • critical complexity — src/hooks/wallet/useWallet.ts — CC 62, MI 55.74, SLOC 220
  • critical complexity — src/features/limits/utils.ts — CC 50, MI 57.21, SLOC 122
  • high hotspot — src/app/(mobile-ui)/home/page.tsx — 33 commits, +178/-146 lines since 6 months ago
  • high complexity — src/utils/balance.utils.ts — CC 31, MI 62.73, SLOC 83
  • high complexity — src/hooks/wallet/useSpendBundle.ts — CC 19, MI 47.32, SLOC 220
  • medium high-mdd — src/hooks/wallet/useSpendBundle.ts:108 — useSpendBundle: MDD 59.5 (uses across many lines from declarations)
  • medium high-mdd — src/app/(mobile-ui)/home/page.tsx:55 — Home: MDD 48.6 (uses across many lines from declarations)
  • medium high-mdd — src/hooks/wallet/useSpendBundle.ts:123 — : MDD 46.6 (uses across many lines from declarations)
  • medium high-mdd — src/hooks/wallet/useWallet.ts:57 — useWallet: MDD 40.8 (uses across many lines from declarations)
  • medium high-dlt — src/hooks/wallet/useWallet.ts:57 — useWallet: DLT 40 (calls 40 distinct functions — high context load)
  • medium high-dlt — src/hooks/wallet/useSpendBundle.ts:108 — useSpendBundle: DLT 33 (calls 33 distinct functions — high context load)
  • medium method-complexity — src/services/rain.ts:345 — rainRequest CC 29 SLOC 62
  • medium hotspot — src/hooks/wallet/useSpendBundle.ts — 27 commits, +965/-604 lines since 6 months ago
  • medium hotspot — src/services/rain.ts — 27 commits, +869/-117 lines since 6 months ago
  • medium hotspot — src/hooks/wallet/useWallet.ts — 26 commits, +441/-198 lines since 6 months ago
  • medium hotspot — src/utils/general.utils.ts — 26 commits, +171/-178 lines since 6 months ago
  • medium high-mdd — src/hooks/useRainCardOverview.ts:22 — useRainCardOverview: MDD 23.7 (uses across many lines from declarations)
  • medium high-mdd — src/app/(mobile-ui)/home/page.tsx:312 — WalletBalance: MDD 23.0 (uses across many lines from declarations)
  • medium high-mdd — src/services/rain.ts:345 — rainRequest: MDD 22.7 (uses across many lines from declarations)

…and 24 more.

✅ Resolved (43)

  • src/app/(mobile-ui)/home/page.tsx — CC 66, MI 63.05, SLOC 200
  • src/features/limits/utils.ts — CC 47, MI 57.73, SLOC 118
  • src/hooks/wallet/useWallet.ts — CC 46, MI 57.28, SLOC 190
  • src/app/(mobile-ui)/home/page.tsx — 32 commits, +169/-143 lines since 6 months ago
  • src/hooks/wallet/useSpendBundle.ts — CC 19, MI 47.41, SLOC 218
  • src/hooks/wallet/useSpendBundle.ts:108 — useSpendBundle: MDD 59.3 (uses across many lines from declarations)
  • src/app/(mobile-ui)/home/page.tsx:55 — Home: MDD 48.5 (uses across many lines from declarations)
  • src/hooks/wallet/useSpendBundle.ts:123 — : MDD 46.5 (uses across many lines from declarations)
  • src/hooks/wallet/useWallet.ts:53 — useWallet: MDD 38.5 (uses across many lines from declarations)
  • src/hooks/wallet/useWallet.ts:53 — useWallet: DLT 36 (calls 36 distinct functions — high context load)
  • src/hooks/wallet/useSpendBundle.ts:108 — useSpendBundle: DLT 32 (calls 32 distinct functions — high context load)
  • src/services/rain.ts:335 — rainRequest CC 29 SLOC 62
  • src/utils/balance.utils.ts — CC 28, MI 62.21, SLOC 80
  • src/hooks/wallet/useSpendBundle.ts — 26 commits, +963/-603 lines since 6 months ago
  • src/services/rain.ts — 26 commits, +859/-117 lines since 6 months ago
  • src/utils/general.utils.ts — 25 commits, +167/-178 lines since 6 months ago
  • src/hooks/useMultiPhaseKycFlow.ts — 24 commits, +551/-115 lines since 6 months ago
  • src/services/rain.ts:335 — rainRequest: MDD 22.7 (uses across many lines from declarations)
  • hooks/wallet/useSignSpendBundle.ts:272 — 22 duplicate lines / 98 tokens with hooks/wallet/useSpendBundle.ts:282
  • src/app/(mobile-ui)/home/page.tsx:310 — WalletBalance: MDD 21.0 (uses across many lines from declarations)

…and 23 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/hooks/wallet/lastKnownSpendable.ts 0.0 5.8 +5.8
src/hooks/wallet/useWallet.ts 11.5 12.8 +1.3

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2173 ran, 0 failed, 0 skipped, 36.4s

📊 Coverage (unit)

metric %
statements 61.5%
branches 45.1%
functions 50.8%
lines 61.8%
⏱ 10 slowest test cases
time test
3.7s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.1s 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/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/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.3s src/utils/__tests__/demo-balance.test.ts › keeps a spent-down balance across a cold start within the TTL
0.2s src/utils/__tests__/demo-balance.test.ts › auto-refills a wallet older than the TTL on cold start
0.2s src/utils/__tests__/demo-balance.test.ts › resetDemoBalance refills and restarts the TTL window
0.2s src/utils/__tests__/url.utils.test.ts › uses the public BASE_URL in Capacitor, not the localhost WebView origin
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/hooks/wallet/useWallet.ts`:
- Around line 280-285: Update the spendable-balance persistence flow around
isRainReady and writeLastKnownSpendable so balanceUnavailable responses may
remain usable for display but never update persistedSpendableRef or
lastKnownSpendable; require an authoritative Rain response before persisting.
Extend the stale-served-balance test to assert that the existing cache value
remains unchanged.
- Around line 270-285: Scope stableSpendable and persistedSpendableRef to the
active user by storing each value together with its owning userId. Update the
effects and the render logic around stableSpendable (including the priority path
near line 292) to ignore or reset values whose owner differs from the current
user, ensuring account switches cannot display or suppress writes using the
previous user’s balance. Add a regression test covering an account switch
between users.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a266338-3bd7-494d-a817-4f6b18d2783e

📥 Commits

Reviewing files that changed from the base of the PR and between 5fd5969 and 2853a2d.

📒 Files selected for processing (12)
  • src/app/(mobile-ui)/home/page.tsx
  • src/features/limits/utils.ts
  • src/hooks/useRainCardOverview.ts
  • src/hooks/wallet/__tests__/useWalletSpendable.test.tsx
  • src/hooks/wallet/lastKnownSpendable.ts
  • src/hooks/wallet/spendPreflight.ts
  • src/hooks/wallet/useSpendBundle.ts
  • src/hooks/wallet/useWallet.ts
  • src/services/rain.ts
  • src/utils/__tests__/balance.utils.test.ts
  • src/utils/balance.utils.ts
  • src/utils/general.utils.ts

Comment thread src/hooks/wallet/useWallet.ts Outdated
Comment thread src/hooks/wallet/useWallet.ts Outdated
innolope-dev added a commit that referenced this pull request Jul 23, 2026
… fix

Rolls up for the next TestFlight + Play internal build:
  - main → mobile-release sync (push routing, app lock, step-up, CSP,
    Manteca per-rail hotfix, Rhino minimums, Sentry noise) — #2483
  - en / es-419 / pt-BR localization + 0 ESLint errors — #2447
  - $0-balance fix: last-known spendable, balanceUnavailable, no
    non-card /rain/cards poll — cherry-picked from #2482
stableSpendable and persistedSpendableRef survived an account switch,
so user A's settled total could paint for user B (and an equal total
suppressed B's cache write). Tag both holds with their owner and ignore
mismatches. Also stop refreshing the last-known-good cache from a
served-stale (balanceUnavailable) Rain value — display may use it, the
cache is reserved for live reads. Tests pin all three behaviors.

@kushagrasarathe kushagrasarathe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — SAFE (money-safe). Approving with one nit to tighten.

Correctly separates a real $0 balance from a failed read: the last-known spendable total is shown (fixing the $0 flash) while a bad read no longer masquerades as zero. Verified no stale number reaches an actual spend — the live on-chain gate in resolveSpendStrategy is intact; this cache is display-only.

Nit (non-blocking): the last-known cache isn't time-bounded — the at timestamp is written but never read, so a stale value can be shown indefinitely if reads keep failing. Either bound the display on at (e.g. show stale-flagged only within N minutes) or drop the unused field. Fine to ship as-is and follow up.

The persisted last-known total wrote an `at` timestamp that readers never
consulted, so a stale value could be painted indefinitely if /rain/cards
reads kept failing for a user. Expire it after LAST_KNOWN_MAX_AGE_MS (7d);
the value stays display-only and stale-flagged, and the affordability gate
is unaffected (it runs on live data). Entries written before the timestamp
existed are treated as fresh.
@innolope-dev

Copy link
Copy Markdown
Collaborator Author

Pushed fcf5854 addressing the review:

Time-bounding nit (your review): readLastKnownSpendable now expires entries on the at timestamp after LAST_KNOWN_MAX_AGE_MS (7d), so a stale value can no longer be painted indefinitely when /rain/cards reads keep failing. Still display-only and stale-flagged; the affordability gate remains on live data. Added a test.

Cross-user scoping (CodeRabbit's Major finding on useWallet.ts): already handled in f726aa46dstableSpendable, persistedSpendableRef and lastKnownSpendable are keyed by owner: userId, and stableForUser/lastKnownForUser only surface a value when owner === userId, so an account switch can't show or suppress with the previous user's balance. Covered by the two account-switch tests in the suite.

Spendable suite green (12 passing), typecheck clean on the touched files.

@kushagrasarathe

Copy link
Copy Markdown
Contributor

Re-reviewed fcf585425 — nit resolved, clean.

LAST_KNOWN_MAX_AGE_MS (7d) now expires the persisted total on its at timestamp in readLastKnownSpendable, with a test that advances Date.now() past the bound and asserts undefined. Backward-compat is handled (entries predating at are treated as fresh — a small self-healing population). The doc comment is honest that this is a display-only bound, not a correctness guard — the money path still uses the live gate. CI is green (ci-success passing; only the pre-existing repo-wide eslint red).

No remaining concerns — good to merge.

@kushagrasarathe

Copy link
Copy Markdown
Contributor

@innolope-dev merge conflicts

@innolope-dev

Copy link
Copy Markdown
Collaborator Author

Resolved the merge conflict with main — one conflicting file (balance.utils.test.ts, two independent test blocks added at the same spot). Kept both. All 938 tests pass, prettier clean. Pushed to this branch; GitHub now reports it as mergeable.

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