Skip to content

feat: integrate Freighter wallet provider - #445

Open
jahrulezfrancis wants to merge 1 commit into
LightForgeHub:mainfrom
jahrulezfrancis:feat/freighter-wallet-integration
Open

feat: integrate Freighter wallet provider#445
jahrulezfrancis wants to merge 1 commit into
LightForgeHub:mainfrom
jahrulezfrancis:feat/freighter-wallet-integration

Conversation

@jahrulezfrancis

@jahrulezfrancis jahrulezfrancis commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Integrates the wallet provider with the official @stellar/freighter-api, replacing simulated wallet behavior with real Freighter connections, transaction signing, connection persistence, and network-change detection.

Changes

  • Connects to Freighter using isConnected() and requestAccess().
  • Retrieves the active public key using Freighter v6's getAddress() API.
  • Exposes signTransaction() through useWallet().
  • Signs transactions using the active address and network passphrase.
  • Prevents transaction signing when Freighter is not on Testnet.
  • Persists wallet connection details in local storage across reloads.
  • Revalidates cached connections against Freighter during initialization.
  • Uses WatchWalletChanges to detect account and network changes.
  • Refreshes the XLM balance when the account or network changes.
  • Replaces the simulated connection modal with the real Freighter prompt.
  • Displays the abbreviated public key in desktop and mobile navigation.
  • Displays an accessible warning when Freighter is connected to Mainnet or another unsupported network.
  • Updates wallet mocks to match the Freighter v6 response format.

Implementation Notes

getPublicKey() is not available in the installed @stellar/freighter-api v6 package. The implementation uses its current replacement, getAddress().

Connection persistence stores only public wallet information:

  • Public address
  • Selected network
  • Network passphrase

No private keys or transaction signatures are persisted.

Validation

  • Production build passes
  • Changed files pass ESLint with no errors
  • git diff --check passes
  • Freighter mock responses match the installed API version
  • Manually verify connection with the Freighter browser extension
  • Manually switch between Testnet and Mainnet and confirm the warning updates
  • Manually sign a Testnet transaction

Visual Evidence


Before Implementation

Screencast.from.2026-07-26.21-19-38.webm

After implementation

2026-07-28.10-05-51.mp4

Existing Repository Constraints

Repository-wide TypeScript and lint checks still report unrelated pre-existing issues, including missing Playwright dependencies and errors in unrelated pages/components.

Closes #436

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

@jahrulezfrancis is attempting to deploy a commit to the luluameh's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The wallet provider now integrates Freighter for connection, network monitoring, cached restoration, and transaction signing. Wallet controls display connection state and network errors across desktop, mobile, and modal interfaces, while test mocks match the updated Freighter API responses.

Changes

Freighter wallet integration

Layer / File(s) Summary
Connection state and Freighter lifecycle
src/providers/WalletProvider.tsx
Adds cached restoration, Freighter access requests, wallet-change watching, expanded network state, and wrong-network detection.
Transaction signing contract
src/providers/WalletProvider.tsx
Adds validated transaction signing through Freighter with signing state and signed XDR results.
Wallet connection controls and feedback
src/components/CreateWalletModal.tsx, src/components/home/layout/navbar.tsx, src/components/layout/Navbar.tsx
Updates connection, disconnect, address display, retry, loading, and wrong-network UI behavior.
Freighter mock API alignment
tests/mocks/wallet.ts
Updates mock network, access, and signing payloads for the provider’s new API usage.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CreateWalletModal
  participant WalletProvider
  participant Freighter
  participant Navbar
  User->>CreateWalletModal: Select Freighter
  CreateWalletModal->>WalletProvider: connect()
  WalletProvider->>Freighter: requestAccess()
  Freighter-->>WalletProvider: address and network
  WalletProvider-->>CreateWalletModal: connection result
  WalletProvider-->>Navbar: wallet state and address
  Navbar-->>User: Connected address or network alert
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements Freighter API integration, real address/signing, network-change handling, and connection persistence required by #436.
Out of Scope Changes check ✅ Passed The changed files all support Freighter wallet integration, UI state, or test mocks, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: integrating the Freighter wallet provider.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (8)
tests/mocks/wallet.ts (1)

59-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

setAllowed mock is now unexercised.

The provider moved to requestAccess(), so nothing drives this mock; drop it unless another test still relies on it.

🤖 Prompt for 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.

In `@tests/mocks/wallet.ts` around lines 59 - 62, Remove the unused setAllowed
mock from the wallet test mock. Confirm no remaining tests reference setAllowed,
while preserving the existing requestAccess-based mock behavior.
src/providers/WalletProvider.tsx (4)

149-152: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a timeout to the Horizon request.

This call is triggered on every connect and on every watcher-detected account/network change with no deadline; a stalled Horizon leaves balance pending indefinitely.

♻️ Suggested change
   try {
-    const response = await fetch(`${baseUrl}/accounts/${address}`);
+    const response = await fetch(`${baseUrl}/accounts/${address}`, {
+      signal: AbortSignal.timeout(10_000),
+    });
     if (!response.ok) return null;
🤖 Prompt for 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.

In `@src/providers/WalletProvider.tsx` around lines 149 - 152, Add an abort
timeout to the Horizon fetch in the account-loading flow, using an
AbortController and passing its signal to the request; ensure the timeout is
cleared after completion and existing null/error handling remains intact.

480-487: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Memoize the context values.

Both objects are rebuilt on every provider render (watcher ticks, balance resolution, signing flags), forcing all useWallet() consumers to re-render. Wrapping in useMemo keyed on state and the stable callbacks avoids that.

🤖 Prompt for 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.

In `@src/providers/WalletProvider.tsx` around lines 480 - 487, Memoize the
WalletContextValue object in the provider using useMemo, with dependencies on
state and the stable connect, disconnect, and signTransaction callbacks.
Preserve the existing isWrongNetwork calculation and pass the memoized value to
the context provider so watcher and state updates do not rebuild it
unnecessarily.

25-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the expected network configurable.

EXPECTED_NETWORK is compile-time pinned to TESTNET, and it gates both isWrongNetwork and signing. Reading it from process.env.NEXT_PUBLIC_STELLAR_NETWORK (defaulting to TESTNET) avoids a code change for mainnet.

♻️ Suggested change
-const EXPECTED_NETWORK = "TESTNET";
+const EXPECTED_NETWORK =
+  process.env.NEXT_PUBLIC_STELLAR_NETWORK ?? "TESTNET";
🤖 Prompt for 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.

In `@src/providers/WalletProvider.tsx` at line 25, Update EXPECTED_NETWORK in
WalletProvider to read from process.env.NEXT_PUBLIC_STELLAR_NETWORK, falling
back to "TESTNET" when unset, so the existing isWrongNetwork and signing logic
uses the configured network without further code changes.

222-233: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the real Testnet passphrase in mock mode.

networkPassphrase: "" (also in setMockProfile at Line 472) is a valid-looking but empty value that any consumer reading it outside the signing short-circuit will silently misuse. tests/mocks/wallet.ts already exports MOCK_NETWORK_PASSPHRASE with the correct Testnet value.

🤖 Prompt for 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.

In `@src/providers/WalletProvider.tsx` around lines 222 - 233, Update the mock
wallet state in the initialization flow to use the existing
MOCK_NETWORK_PASSPHRASE instead of an empty networkPassphrase, and make the same
change in setMockProfile. Reuse the exported Testnet passphrase constant from
tests/mocks/wallet.ts without changing other mock state behavior.
src/components/home/layout/navbar.tsx (2)

11-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate shortenAddress helper.

src/components/layout/Navbar.tsx (Line 13) carries its own shortenAddress. Extract one shared helper so the abbreviation format cannot drift between the two navbars.

🤖 Prompt for 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.

In `@src/components/home/layout/navbar.tsx` around lines 11 - 13, Extract the
duplicate shortenAddress logic from the home and layout navbar components into
one shared helper, then update both callers to import and use it. Preserve the
existing four-character prefix, ellipsis, and four-character suffix format.

109-134: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Connected wallet control is an interactive button that does nothing. Both breakpoints duplicate the same control, and in both the connected state keeps cursor-pointer, stays enabled, and remains keyboard-focusable while onClick is a no-op (if (!address) setWalletOpen(true)). Extract one shared control and make the connected state non-interactive (or give it a real action, e.g. copy address / open a wallet menu).

  • src/components/home/layout/navbar.tsx#L109-L134: render a non-button element (or disabled + non-pointer cursor) when address is set, and extract the shared control.
  • src/components/home/layout/navbar.tsx#L200-L224: replace this duplicated block with the extracted control so the mobile menu gets the same behavior.
🤖 Prompt for 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.

In `@src/components/home/layout/navbar.tsx` around lines 109 - 134, The duplicated
wallet controls at src/components/home/layout/navbar.tsx lines 109-134 and
200-224 should be replaced with one shared control. Update the control so the
connected state is non-interactive, not keyboard-focusable, and uses a
non-pointer cursor, while preserving the connect action, loading state, address
display, and disconnect behavior; apply the extracted control consistently at
both sites.
src/components/CreateWalletModal.tsx (1)

41-45: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Connected state ignores isWrongNetwork.

connect() resolves true on a Mainnet-selected wallet too, so the modal shows "Freighter is now connected." while the navbars render the wrong-network alert. Consider reading isWrongNetwork/network from useWallet() and adding a switch-to-Testnet hint in the connected panel.

🤖 Prompt for 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.

In `@src/components/CreateWalletModal.tsx` around lines 41 - 45, Update
handleConnect and the connected-panel rendering to account for
isWrongNetwork/network from useWallet: only present the normal connected state
when the wallet is on Testnet, and show a switch-to-Testnet hint when a
Mainnet-selected wallet connects successfully. Preserve the existing error state
for failed connections.
🤖 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/providers/WalletProvider.tsx`:
- Line 22: Replace the invalid MOCK_ADDRESS in WalletProvider with a valid
56-character Stellar public key, preferably by reusing the existing
MOCK_WALLET_ADDRESS from the test mock instead of defining a duplicate. Ensure
mock mode passes StrKey/Keypair.fromPublicKey validation and the navbar displays
the valid address.
- Around line 285-295: Update the cached-connection handling around
readCachedConnection so it sets only isLoading (and clears any prior error)
without spreading cached address or network values into state. Keep refresh()
responsible for populating verified connection data through setRealConnection,
while preserving the early return when no cache exists.

---

Nitpick comments:
In `@src/components/CreateWalletModal.tsx`:
- Around line 41-45: Update handleConnect and the connected-panel rendering to
account for isWrongNetwork/network from useWallet: only present the normal
connected state when the wallet is on Testnet, and show a switch-to-Testnet hint
when a Mainnet-selected wallet connects successfully. Preserve the existing
error state for failed connections.

In `@src/components/home/layout/navbar.tsx`:
- Around line 11-13: Extract the duplicate shortenAddress logic from the home
and layout navbar components into one shared helper, then update both callers to
import and use it. Preserve the existing four-character prefix, ellipsis, and
four-character suffix format.
- Around line 109-134: The duplicated wallet controls at
src/components/home/layout/navbar.tsx lines 109-134 and 200-224 should be
replaced with one shared control. Update the control so the connected state is
non-interactive, not keyboard-focusable, and uses a non-pointer cursor, while
preserving the connect action, loading state, address display, and disconnect
behavior; apply the extracted control consistently at both sites.

In `@src/providers/WalletProvider.tsx`:
- Around line 149-152: Add an abort timeout to the Horizon fetch in the
account-loading flow, using an AbortController and passing its signal to the
request; ensure the timeout is cleared after completion and existing null/error
handling remains intact.
- Around line 480-487: Memoize the WalletContextValue object in the provider
using useMemo, with dependencies on state and the stable connect, disconnect,
and signTransaction callbacks. Preserve the existing isWrongNetwork calculation
and pass the memoized value to the context provider so watcher and state updates
do not rebuild it unnecessarily.
- Line 25: Update EXPECTED_NETWORK in WalletProvider to read from
process.env.NEXT_PUBLIC_STELLAR_NETWORK, falling back to "TESTNET" when unset,
so the existing isWrongNetwork and signing logic uses the configured network
without further code changes.
- Around line 222-233: Update the mock wallet state in the initialization flow
to use the existing MOCK_NETWORK_PASSPHRASE instead of an empty
networkPassphrase, and make the same change in setMockProfile. Reuse the
exported Testnet passphrase constant from tests/mocks/wallet.ts without changing
other mock state behavior.

In `@tests/mocks/wallet.ts`:
- Around line 59-62: Remove the unused setAllowed mock from the wallet test
mock. Confirm no remaining tests reference setAllowed, while preserving the
existing requestAccess-based mock behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a94df337-07f6-49c0-90b6-45d9be40e1c3

📥 Commits

Reviewing files that changed from the base of the PR and between 3f9e758 and d62410e.

📒 Files selected for processing (5)
  • src/components/CreateWalletModal.tsx
  • src/components/home/layout/navbar.tsx
  • src/components/layout/Navbar.tsx
  • src/providers/WalletProvider.tsx
  • tests/mocks/wallet.ts

Comment thread src/providers/WalletProvider.tsx
Comment thread src/providers/WalletProvider.tsx
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.

Connect Freighter Wallet to WalletProvider

1 participant