Skip to content

feat(rabe_connector): implement secure persistent caching for active addresses - #342

Merged
godamongstmen897 merged 232 commits into
Goldii-locks:mainfrom
emmyokolo2525-cyber:feat/rabe-connector-persistent-cache
Sep 1, 2026
Merged

feat(rabe_connector): implement secure persistent caching for active addresses#342
godamongstmen897 merged 232 commits into
Goldii-locks:mainfrom
emmyokolo2525-cyber:feat/rabe-connector-persistent-cache

Conversation

@emmyokolo2525-cyber

Copy link
Copy Markdown
Contributor

Summary

Implements secure persistent caching for active wallet addresses in rabe_connector, as described in issue #137. Active addresses are now serialized to localStorage and correctly restored across page reload cycles.

Changes

app/lib/rabe_connector.ts

New exports added:

Export Description
RABE_CACHE_KEY localStorage key for the cache entry
RabeActiveAddressCache Versioned interface: version, address, savedAt, network
validateRabeAddressCache Strict runtime validation — address format (56-char Base32 G…), timestamp > 0, known network
serializeRabeAddressCache JSON serialization
deserializeRabeAddressCache JSON parse + validate; returns null on corrupt/invalid data
saveRabeAddressCache Writes to localStorage; SSR-safe; logs warning via logRabeWarning on quota errors
loadRabeAddressCache Reads + validates from localStorage; SSR-safe; returns null when absent or corrupt
clearRabeAddressCache Removes entry on disconnect; no-op if absent; SSR-safe

__tests__/rabe_connector.test.ts

33 new test cases across three describe blocks:

  • validateRabeAddressCache — all valid/invalid shape permutations
  • serialization round-trip — JSON encode/decode, corrupt input, null, empty string
  • localStorage integration — save/load/clear, overwrite, corrupt data, setItem throwing, timestamp bounds

Verification

  • ✅ 376 / 376 tests pass (all existing tests continue to pass)
  • ✅ TypeScript: tsc --noEmit → 0 errors
  • ✅ Active session state parses correctly on reload (validated cache shape on every load call)

Closes #137

Great-O and others added 30 commits August 25, 2026 17:06
…inner_skeleton

- Replace hardcoded gray-* Tailwind classes with semantic design tokens:
  * bg-gray-900  -> bg-surface-card
  * bg-gray-800  -> bg-surface-field (content placeholders)
  * border-gray-800 -> border-border-strong
  * bg-gray-700  -> bg-border-subtle (nested placeholders / contrast layer)
- Add data-testid attributes to all skeleton sections for testability
- Add 19 comprehensive test cases covering:
  * Design token validation (no hardcoded gray-* classes remain)
  * Correct token-to-element mapping across container/header/stats/milestones
  * Component layout structure (grid, count, padding, animations)
  * Accessibility attributes (role=status, aria-live, sr-only, aria-hidden)

All 436 tests (33 files) pass, including the 19 new assertions.
- Add DEFAULT_SIGNING_TIMEOUT_MS = 60_000 configurable constant
- Add TxSignRequest interface, TxSignatureTimeoutError class,
  clearTxSensitiveMemory helper, and signTxWithTimeout function to
  app/lib/transactions.ts, matching the setTimeout + Promise.race
  pattern used by all other connector helpers in the codebase
- Wire signTxWithTimeout into submitContractTransaction via optional
  signingTimeoutMs param (defaults to the constant); on timeout the
  operation is aborted and any sensitive payload memory is zeroed
- Add __tests__/transactions_timeout.test.ts with 21 test cases
  covering: timeout fires, memory cleared, successful signing flow,
  pre-deadline no-early-fire, timer cleanup, error propagation,
  default constant, TxSignatureTimeoutError class, and
  clearTxSensitiveMemory helper
Adds app/components/TransactionSignerPanel.tsx — a wallet-agnostic
sign-transaction interface that unblocks issue Goldii-locks#222 (RTL tests).

- Parses supplied XDR on mount via useWalletMultiSigAssembly.parseStructure
  and displays a structure preview (source account, fee, operation count,
  existing signatures)
- States: idle -> signing -> success | error | parse-error
- Sign button delegates to hook.signTransaction(xdr, signTransaction) —
  no new XDR parsing or signing logic introduced
- Retry button returns to idle on signing failure
- Cancel/Dismiss calls onRejected() and resets state
- onSigned, onRejected, onError callbacks for parent integration
- Works for all four supported wallets (freighter, albedo, xbull, hana)
  through the unified useWalletMultiSigAssembly hook
- Replace hardcoded empty message with EmptyStateCard component
- Display briefcase icon for job-related context
- Show descriptive title and explanation text
- Include role badges (Client, Freelancer, Arbiter) showing available participation options
- Add comprehensive test coverage with 16 test cases
- Ensure proper accessibility with region landmarks and aria-labels
- Validates placeholder display under empty data states
…, desktop

- Add responsive padding and spacing (px-3 sm:px-6) for mobile-first approach
- Implement responsive typography scaling (text-xl sm:text-2xl md:text-3xl)
- Stack layout vertically on mobile, horizontal on tablet/desktop
- Apply responsive grid layouts (grid-cols-1 sm:grid-cols-2 lg:grid-cols-3)
- Make search form full-width on mobile, inline on tablet+
- Add responsive gaps and margins throughout
- Implement horizontal overflow handling for pagination on mobile
- Reduce button padding and font sizes on mobile viewports
- Add comprehensive responsive design test suite with 20 test cases
- Validate layout at mobile (< 640px), tablet (640-1024px), desktop (> 1024px)
…yling

- Add focus-visible ring-2 styling on all interactive elements (indigo-500)
- Implement hover state transitions with smooth animations (transition-all duration-200)
- Add active state styling for button press feedback
- Style disabled buttons with opacity-50 and cursor-not-allowed
- Prevent hover effects on disabled buttons (disabled:hover:bg-gray-900)
- Apply ring-offset for focus states on dark background (ring-offset-gray-950)
- Use inset focus ring on job expand buttons for better UX
- Add focus-visible:outline-none to remove browser defaults
- Implement smooth transitions on all state changes
- Add comprehensive test suite with 25 test cases validating:
  - Search input focus and hover states
  - Search button focus, hover, and active states
  - Role filter button states and transitions
  - Job expand button interactive states
  - Pagination button states and disabled styling
  - Accessibility compliance and transitions
…onSignerPanel

- Move state declarations before closeModal callback (WalletSelectorModal)
- Remove eslint-disable and add missing hook dependency (TransactionSignerPanel)
…addresses

Add state serialization to rabe_connector so active wallet addresses
survive page reload cycles.

New exports:
- RABE_CACHE_KEY: localStorage key for the cache entry
- RabeActiveAddressCache: versioned interface (version, address, savedAt, network)
- validateRabeAddressCache: strict runtime validation (address format,
  timestamp, network)
- serializeRabeAddressCache / deserializeRabeAddressCache: JSON
  round-trip with validation on read-back; returns null on corrupt data
- saveRabeAddressCache: writes to localStorage; SSR-safe; logs warning
  on QuotaExceededError via existing logRabeWarning
- loadRabeAddressCache: reads + validates from localStorage; SSR-safe
- clearRabeAddressCache: removes entry on disconnect; no-op if absent

Tests: 33 new cases covering validation, serialization round-trips,
and all localStorage integration scenarios (save/load/clear, overwrite,
corrupt data, storage errors, timestamp bounds).

All 376 existing tests continue to pass. TypeScript: 0 errors.

Closes Goldii-locks#137
Great-O and others added 28 commits August 31, 2026 16:31
…ing-skeleton-stories

feat: add Storybook stories for LoadingSkeleton (closes Goldii-locks#281)
…e_raise_modal

Implement overlay wrappers on dispute_raise_modal to fit constraints on mobile device screen heights.

Changes:
- Added overlayWrapper class with flex layout and max-h-[92vh] for mobile height constraints
- Added scrollableContent class to allow middle content to scroll independently
- Updated panel structure to use overlay wrapper with header, scrollable content, and actions
- Added flex-shrink-0 to header and actions to prevent shrinking
- Added comprehensive test cases for mobile viewport validation
No textual conflict against current main -- GitHub's cached mergeability
had gone stale (it reported DIRTY where a local three-way merge is
clean), the same as on Goldii-locks#366. Merging main in here refreshes the head so
CI runs against it.

lint 0 errors / tsc 0 errors / 2035 tests passing / build OK
…e-modal-stories

Feat: Implemented Storybook Interface for Dispute Raise ModaL
…he shared badge

This branch predates the LoadingSkeleton rewrite (Goldii-locks#349/Goldii-locks#351/Goldii-locks#352/Goldii-locks#354)
and the WalletBadge consolidation, and git merged it without conflicts
into something that does not parse -- the classic silent damage:

- LoadingSkeleton.tsx ended up as this branch's pre-token markup
  concatenated with main's current component, leaving an unterminated
  JSX block followed by `interface LoadingSkeletonProps`.
- WalletBadge.tsx ended up with two complete implementations: two
  `formatAddress` declarations and two `export default`s.
- Navbar.tsx and wallet_badge.test.tsx each got duplicate import lines.

LoadingSkeleton and SignatureTimeoutAlert are incidental to this PR --
its subject is the wallet badge -- and the branch's own copies were
already broken before the merge (its SignatureTimeoutAlert interleaves a
useEffect into the existing useMemo, leaving two catch blocks and an
undefined setParseMessage). Both were taken from main.

The badge work itself is real and entirely additive: 16 of its 33 cases
already passed against main's component, and the other 17 cover features
main did not have. Those were added to main's derived-status rendering
rather than replacing it:

  isValidStellarAddress   exported; G + 55 base32 chars, format only
  error / fieldError      wallet-field-error + wallet-error-text,
                          aria-invalid, red status dot
  validateAddress         flags a malformed address the same way
  alert                   wallet-alert-badge, amber dot; error wins
  empty placeholders      wallet-badge-placeholder, -custom and
                          wallet-empty-list-placeholder for empty
                          accounts/wallets/items, plus data-empty-state

One distinction worth noting: a present-but-blank address (`""`) is an
empty state, while a missing one (`null`) stays the ordinary
disconnected badge unless the caller passes placeholder copy. The
branch's own tests require both readings.

All four wallet-badge suites now pass together.

lint 0 errors / tsc 0 errors / 2052 tests passing / build OK
The branch's lockfile is not valid JSON -- it fails to parse at offset
101100 in the branch itself, before any merge -- which is why CI died at
"Install dependencies" with npm error code EUSAGE rather than at a
lint or test step.

The branch does not modify package.json at all, so its 5002-line
lockfile change had nothing to express; main's lockfile is the correct
one for these dependencies. Verified with a clean npm ci against the
merged package.json.

lint 0 errors / tsc 0 errors / 2052 tests passing / build OK
…ole-errors-and-transaction-tracking-in-network-sync-checker

# Conflicts:
#	__tests__/freighter_connector.component.test.tsx
#	__tests__/freighter_multisig_hook.test.ts
#	__tests__/signature_timeout_alert.test.tsx
#	__tests__/signature_timeout_alert_timeout.test.ts
#	app/components/SignatureTimeoutAlert.tsx
#	vitest.setup.ts
…pty-placeholder

feat(wallet): render descriptive placeholders for empty data states in wallet_badge
…lready carries

This branch and its three siblings (Goldii-locks#359/Goldii-locks#360/Goldii-locks#361) share a base of
commits that rework the same four test files and SignatureTimeoutAlert,
so most of the conflict here is the same work expressed twice:

- signature_timeout_alert_timeout.test.ts: `onRejected` vs main's
  `settled` for the same catch-before-advance handle.
- freighter_multisig_hook.test.ts: an equivalent signed-envelope builder
  to main's `buildSignedEnvelopeXdr`.
- signature_timeout_alert.test.tsx: the same act() calls, reformatted.
- freighter_connector.component.test.tsx: a different mocking strategy
  for the same cases.

None of those four adds a test case -- the it() counts are identical to
this branch's own merge base -- so main's versions were kept and this
branch's rewrites dropped. SignatureTimeoutAlert took the branch's
explanatory comment, which describes what main already does.

vitest.setup.ts combines both: the branch's guard is the better one (it
also catches the experimental Node localStorage global that reads as
undefined and shadows jsdom's), while main's defined the property on
both globalThis and window. The merged version does both.

The branch's actual deliverable is untouched: NetworkSyncChecker.tsx and
its 300-line component suite.

lint 0 errors / tsc 0 errors / 2061 tests passing / build OK
…ct-testing-library-assertions-for-network-sync-checker

[162] Write React Testing Library assertions for network_sync_checker
…eractive-states-for-notification-bell

# Conflicts:
#	__tests__/freighter_connector.component.test.tsx
#	__tests__/freighter_multisig_hook.test.ts
#	__tests__/signature_timeout_alert.test.tsx
#	__tests__/signature_timeout_alert_timeout.test.ts
#	app/components/SignatureTimeoutAlert.tsx
#	app/components/notification_bell.tsx
#	vitest.setup.ts
Only vitest.setup.ts conflicted, and main already carries the combined
version resolved on Goldii-locks#358 -- which includes this branch's own guard for
the experimental Node localStorage global. Took main's.

The branch's deliverable lands unchanged: the network_sync_checker
logging rework and its 197-line suite.

lint 0 errors / tsc 0 errors / 2072 tests passing / build OK
…y-list-views-for-notification-bell

# Conflicts:
#	__tests__/freighter_connector.component.test.tsx
#	__tests__/freighter_multisig_hook.test.ts
#	__tests__/signature_timeout_alert.test.tsx
#	__tests__/signature_timeout_alert_timeout.test.ts
#	app/components/notification_bell.tsx
#	vitest.setup.ts
…nsole-errors-and-transaction-tracking-in-network-sync-checker

[161] Format console errors and transaction tracking in network_sync_checker
…nteractive-states-for-notification-bell

[321] Add premium interactive states to notification_bell
…pty-list-views-for-notification-bell

[323] Design empty list display views for notification_bell
…e modal

The branch predates Goldii-locks#375's rewrite of DisputeRaiseModal, and its own
edit to that file is broken independently of the merge: it deletes the
counterparty <dd>'s closing tags and splices a stray label/textarea
fragment in their place, so the branch does not parse. The merge
inherited that.

The lib half is sound and is the real contribution, so it was kept and
the component rebuilt around it: the panel becomes a plain flex column,
DISPUTE_MODAL_CLASSES.overlayWrapper takes the padding and the height
cap, and a scrollableContent region wraps everything between the header
and the actions -- so on a short viewport the middle scrolls while the
title and buttons stay pinned.

Two assertions in the responsive suite described the old arrangement and
now follow it: the p-4/sm:p-6/lg:p-8 checks moved from the panel to the
wrapper, and the panel's overflow-y-auto check became a query for the
scrollable region inside it.

The branch's own "keeps action buttons clickable" case also assumed the
confirm button is live with an empty reason, which Goldii-locks#375 changed -- it now
fills the field first, which is what a user reaching those buttons would
have done anyway.

lint 0 errors / tsc 0 errors / 2098 tests passing / build OK
…spute-modal-mobile-overlay-wrappers

Goldii-locks#336 Handle mobile viewports navigation styling in dispute_raise_modal
Two Navbar conflicts: this branch swaps the inline provider select and
Connect button for a <WalletSelectorModal>. That is incidental to the
PR -- its subject is TransactionSignerPanel (Goldii-locks#222) and none of its tests
touch the Navbar -- and main's navbar-wallet-kit suite queries the
select by label and the button by name, so main's Navbar was kept.

TransactionSignerPanel then failed lint: it parsed the XDR in a mount
effect and wrote the result to three pieces of state, which
react-hooks/set-state-in-effect flags as cascading renders.

Parsing is a pure function of the XDR and the wallet/network in play, so
it now derives during render through useMemo. The signing lifecycle is
still real state (idle -> signing -> success/error), so it stays in
useState as `signingState`, with a parse failure outranking it:

  const state = parseErrorMessage ? "parse-error" : signingState;

Rendering is unchanged -- `state`, `structure` and `parseErrorMessage`
mean exactly what they did before.

lint 0 errors / tsc 0 errors / 2140 tests passing / build OK
…on-signer-panel

Feat(wallet): build TransactionSignerPanel component (Goldii-locks#222)
The rabe_connector conflict was structural, not semantic: this branch's
addition is a single append-only hunk at the end of the file (156 lines,
zero deletions), but main has grown past that point, so git aligned the
two additions and split an existing function in half. Rebuilt as main's
file plus the branch's block appended -- the persistent address cache
lands whole.

Running the suite then exposed a real defect in
dashboard-accessibility.test.tsx (from Goldii-locks#306): five waitFor calls that
are never awaited.

Three are nested inside an outer waitFor, so the click replays on every
retry and the inner assertions never gate the test; one of them leaked a
rejection that made vitest exit 1 while reporting all tests passed. Two
more sit in synchronous tests, where nothing inside them ever ran.

With the assertions actually running, two failed for a second reason:
the dashboard auto-expands the first job once the fetch resolves, so an
unconditional click closed the very panel they were looking for. They
now click only while the row is still collapsed, and go through
fireEvent so React flushes the update inside act().

lint 0 errors / tsc 0 errors / 2173 tests passing / build OK, exit 0
@drips-wave

drips-wave Bot commented Sep 1, 2026

Copy link
Copy Markdown

@emmyokolo2525-cyber 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

@godamongstmen897
godamongstmen897 merged commit 4291512 into Goldii-locks:main Sep 1, 2026
1 check 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.

Implement secure persistent caching for active keys in rabe_connector