βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β React Components β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β β WalletConnectionModal (UI) β β
β β BookingPage (Integration) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Custom Hooks β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β β useWallet() - State Management β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Services Layer β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β β wallet.service.ts - Transaction Signing β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Utilities β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β β freighter-utils.ts - Low-level API β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Freighter Wallet (Browser) β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β β @stellar/freighter-api β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User Click "Connect"
β
WalletConnectionModal.handleFreighterConnect()
β
connectFreighterWallet()
β
isFreighterInstalled() + getFreighterPublicKey()
β
Freighter API Response
β
Validate Address (isValidStellarAddress)
β
Save to localStorage
β
Update State β UI Update
User Submits Booking
β
Check Wallet Connection
β
Build Transaction (Stellar SDK)
β
signWithFreighter(xdr, networkPassphrase)
β
Freighter Modal (User Signs)
β
Return Signed XDR
β
Send to Backend
β
Broadcast Transaction
Created custom FreighterError class with specific error codes for better error handling:
throw new FreighterError('User rejected', 'USER_REJECTED')This allows UI to display context-specific messages without string matching.
Uses regex pattern for Stellar addresses:
- Starts with 'G' (public key indicator)
- 56 characters total
- Base32 encoding:
[A-Z2-7]
/^G[A-Z2-7]{55}$/Only stores public key (non-sensitive):
- Key:
walletAddress - Validates on retrieval:
isValidStellarAddress() - Clears on disconnect
Centralized in getNetworkPassphrase() function:
const passphrase = getNetworkPassphrase('testnet')
// Returns: 'Test SDF Network ; September 2015'Three levels of state handling:
-
Component Level (WalletConnectionModal):
- Connection state, error, address
- Simple, isolated state
-
Hook Level (useWallet):
- Full wallet lifecycle
- Reusable across components
- localStorage integration
-
Module Level (freighter-utils):
- Low-level Freighter API wrapper
- No state, pure functions
- Mock Freighter API responses
- Mock Stellar SDK operations
- Test all error paths
- Test state transitions
- User interactions
- UI state changes
- Error message display
- Modal controls
- Real Freighter wallet
- Real Stellar Testnet
- End-to-end flows
const network = 'testnet'
const passphrase = 'Test SDF Network ; September 2015'
const horizonUrl = 'https://horizon-testnet.stellar.org'const network = 'mainnet'
const passphrase = 'Public Global Stellar Network ; September 2015'
const horizonUrl = 'https://horizon.stellar.org'Switch at runtime:
await signUSDCPaymentTransaction(
sender,
recipient,
amount,
process.env.NEXT_PUBLIC_STELLAR_NETWORK || 'testnet'
)- Lazy Loading: Hook calls
getWalletStatus()on mount - Caching: Address cached in localStorage
- Timeout: Transactions have 30-second timeout
- Error Recovery: Retry mechanism prevents user frustration
- Public key only in storage (not private key)
- User controls signing via Freighter modal
- Network passphrase validated
- Address format validated
- Errors don't expose sensitive info
- Add rate limiting on connection attempts
- Monitor for failed signing attempts
- Log successful transactions (not details)
- Add CORS headers for API calls
- Consider implementing wallet whitelist
Cause: Freighter not installed
Solution: Check isFreighterInstalled() and show install link
Cause: Freighter open but wallet not selected Solution: User must open Freighter and select account
Cause: User clicks "Reject" in Freighter modal Solution: Show "You rejected" message, allow retry
Cause: localStorage corruption
Solution: Validate with isValidStellarAddress() on retrieval
-
Multi-Wallet Support
- Add MetaMask/Stellar Lab support
- Abstract wallet selection
-
Transaction History
- Track signed transactions
- Show confirmation status
-
Batch Signing
- Sign multiple transactions
- Transaction queue
-
Wallet Switching
- Allow switching between Freighter accounts
- Preserve session state
-
Advanced Features
- Transaction simulation
- Gas estimation
- Custom network support
// In freighter-utils.ts
console.log('Wallet status:', result)isValidStellarAddress('INVALID') // false
isValidStellarAddress('GTEST...') // truevi.mocked(FreighterApi.isConnected).mockResolvedValue({
isConnected: true
})console.log(localStorage.getItem('walletAddress'))- Update environment variables
- Configure Stellar network (testnet/mainnet)
- Test with real Freighter wallet
- Review error messages for production
- Set up monitoring/logging
- Run security audit
- Load test with multiple users
- Update API endpoints
- Test on mobile devices
- Verify accessibility
- Install Freighter from https://www.freighter.app
- Create or import Stellar account
- Select account in Freighter
- Click "Connect Wallet" button
- Approve connection in Freighter modal
- Check console for error messages
- Review test files for examples
- Check Stellar documentation
- Verify network configuration
- Test with Stellar Lab: https://stellar.expert/
- Freighter API Docs: https://github.com/StellarCN/freighter-api
- Stellar SDK Docs: https://developers.stellar.org/docs/tools/js-stellar-sdk
- Stellar DevNet: https://developers.stellar.org/docs/learn/fundamentals