Complete reference for all hooks, services, and utilities in PassPay.
A hook that abstracts LazorKit transaction signing with loading states, error handling, and gasless configuration.
import { useLazorkitTransaction } from "@/hooks";interface UseLazorkitTransactionOptions {
gasless?: boolean;
paymasterUrl?: string;
onSuccess?: (signature: string) => void;
onError?: (error: Error) => void;
}
interface UseLazorkitTransactionReturn {
execute: (params: ExecuteParams) => Promise<string | null>;
loading: boolean;
error: string | null;
clearError: () => void;
}
interface ExecuteParams {
instructions: TransactionInstruction[];
lookupTables?: AddressLookupTableAccount[];
}| Parameter | Type | Default | Description |
|---|---|---|---|
gasless |
boolean |
true |
Enable fee sponsorship via paymaster |
paymasterUrl |
string |
kora.devnet.lazorkit.com |
Paymaster service URL |
onSuccess |
(sig: string) => void |
- | Callback when transaction succeeds |
onError |
(error: Error) => void |
- | Callback when transaction fails |
| Property | Type | Description |
|---|---|---|
execute |
function |
Execute the transaction |
loading |
boolean |
Transaction in progress |
error |
string | null |
Error message if failed |
clearError |
function |
Reset error state |
const { execute, loading, error } = useLazorkitTransaction({
gasless: true,
onSuccess: (signature) => {
console.log("Transaction confirmed:", signature);
Alert.alert("Success!", `Signature: ${signature.slice(0, 8)}...`);
},
onError: (error) => {
console.error("Transaction failed:", error);
},
});
// Execute a transaction
const handleTransfer = async () => {
const instruction = SystemProgram.transfer({
fromPubkey: wallet,
toPubkey: recipient,
lamports: LAMPORTS_PER_SOL * 0.01,
});
const signature = await execute({ instructions: [instruction] });
if (signature) {
// Transaction succeeded
}
};A hook that provides wallet connection state and a pre-built "Not Connected" UI component.
import { useWalletGuard } from "@/hooks";interface UseWalletGuardReturn {
isConnected: boolean;
publicKey: PublicKey | null;
truncatedAddress: string | null;
NotConnectedView: React.FC;
}| Property | Type | Description |
|---|---|---|
isConnected |
boolean |
Whether wallet is connected |
publicKey |
PublicKey | null |
Connected wallet address |
truncatedAddress |
string | null |
Shortened address for display |
NotConnectedView |
React.FC |
Component to show when not connected |
export default function TransferScreen() {
const { isConnected, publicKey, truncatedAddress, NotConnectedView } =
useWalletGuard();
// Show connect prompt if not connected
if (!isConnected) {
return <NotConnectedView />;
}
// Main screen content
return (
<View>
<Text>Connected: {truncatedAddress}</Text>
{/* ... */}
</View>
);
}A hook that fetches and manages SOL balance with automatic refresh on screen focus.
import { useSolBalance } from "@/hooks";interface UseSolBalanceOptions {
publicKey: PublicKey | null;
refreshOnFocus?: boolean;
}
interface UseSolBalanceReturn {
balance: number | null;
loading: boolean;
error: string | null;
refresh: () => Promise<void>;
}| Parameter | Type | Default | Description |
|---|---|---|---|
publicKey |
PublicKey | null |
- | Wallet address to check |
refreshOnFocus |
boolean |
true |
Auto-refresh when screen gains focus |
| Property | Type | Description |
|---|---|---|
balance |
number | null |
Balance in SOL |
loading |
boolean |
Fetching in progress |
error |
string | null |
Error message if failed |
refresh |
function |
Manually trigger refresh |
function WalletScreen() {
const { smartWalletPubkey } = useWallet();
const { balance, loading, refresh } = useSolBalance({
publicKey: smartWalletPubkey,
});
return (
<View>
{loading ? <ActivityIndicator /> : <Text>{balance?.toFixed(4)} SOL</Text>}
<Button title="Refresh" onPress={refresh} />
</View>
);
}A hook for managing local transaction history with AsyncStorage persistence.
import { useTransactionHistory } from "@/hooks";interface Transaction {
id: string;
type: "transfer" | "stake" | "memo";
signature: string;
amount?: number;
recipient?: string;
timestamp: number;
}
interface UseTransactionHistoryReturn {
history: Transaction[];
loading: boolean;
addTransaction: (tx: Omit<Transaction, "id" | "timestamp">) => Promise<void>;
clearHistory: () => Promise<void>;
getExplorerUrl: (signature: string) => string;
}| Property | Type | Description |
|---|---|---|
history |
Transaction[] |
Array of past transactions |
loading |
boolean |
Loading history from storage |
addTransaction |
function |
Add new transaction to history |
clearHistory |
function |
Clear all history |
getExplorerUrl |
function |
Get Solana Explorer URL |
function HistoryScreen() {
const { history, addTransaction, getExplorerUrl } = useTransactionHistory();
// Add a transaction after success
await addTransaction({
type: "transfer",
signature: "5xY9...",
amount: 0.01,
recipient: "7abc...",
});
// Display history
return (
<FlatList
data={history}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => Linking.openURL(getExplorerUrl(item.signature))}
>
<Text>
{item.type}: {item.amount} SOL
</Text>
<Text>{new Date(item.timestamp).toLocaleString()}</Text>
</TouchableOpacity>
)}
/>
);
}A hook for clipboard operations with haptic feedback.
import { useClipboard } from "@/hooks";interface UseClipboardReturn {
copy: (text: string) => Promise<void>;
paste: () => Promise<string | null>;
copied: boolean;
}| Property | Type | Description |
|---|---|---|
copy |
function |
Copy text to clipboard |
paste |
function |
Get text from clipboard |
copied |
boolean |
True briefly after copying |
function AddressDisplay({ address }: { address: string }) {
const { copy, copied } = useClipboard();
return (
<TouchableOpacity onPress={() => copy(address)}>
<Text>{truncateAddress(address)}</Text>
<Text>{copied ? "Copied!" : "Tap to copy"}</Text>
</TouchableOpacity>
);
}A hook for managing user session persistence with AsyncStorage.
import { useSession } from "@/hooks";interface UseSessionOptions {
autoRestore?: boolean; // Auto-restore session on mount (default: true)
autoSync?: boolean; // Auto-sync with wallet connection (default: true)
sessionExpiryMs?: number; // Session expiry in ms (default: 24 hours)
expiryWarningMs?: number; // Warning threshold in ms (default: 5 minutes)
trackAppState?: boolean; // Update activity on foreground (default: true)
}
interface UseSessionReturn {
session: SessionData | null;
preferences: UserPreferences;
isRestoring: boolean;
isValid: boolean;
isExpiringSoon: boolean;
timeRemaining: number;
createNewSession: (walletAddress?: string) => Promise<SessionData | null>;
endSession: (keepPreferences?: boolean) => Promise<void>;
extendCurrentSession: (additionalMs?: number) => Promise<SessionData | null>;
updatePreferences: (prefs: Partial<UserPreferences>) => Promise<boolean>;
refresh: () => Promise<void>;
}| Parameter | Type | Default | Description |
|---|---|---|---|
autoRestore |
boolean |
true |
Restore session on mount |
autoSync |
boolean |
true |
Sync with wallet connection |
sessionExpiryMs |
number |
86400000 (24h) |
Session duration in milliseconds |
expiryWarningMs |
number |
300000 (5min) |
Warning threshold before expiry |
trackAppState |
boolean |
true |
Track app foreground/background |
| Property | Type | Description |
|---|---|---|
session |
SessionData | null |
Current session data |
preferences |
UserPreferences |
User preferences |
isRestoring |
boolean |
Session restoration in progress |
isValid |
boolean |
Whether session is valid |
isExpiringSoon |
boolean |
Session expires within threshold |
timeRemaining |
number |
Milliseconds until expiry |
createNewSession |
function |
Create a new session |
endSession |
function |
End the current session |
extendCurrentSession |
function |
Extend session duration |
updatePreferences |
function |
Update user preferences |
refresh |
function |
Refresh session state from storage |
export default function HomeScreen() {
const {
session,
isRestoring,
isValid,
isExpiringSoon,
preferences,
extendCurrentSession,
endSession,
} = useSession();
if (isRestoring) {
return <ActivityIndicator />;
}
if (!isValid) {
return <WelcomeScreen />;
}
return (
<View>
<Text>Welcome, {session?.walletAddress.slice(0, 8)}...</Text>
{isExpiringSoon && (
<Button title="Extend Session" onPress={() => extendCurrentSession()} />
)}
<Button title="Logout" onPress={() => endSession()} />
</View>
);
}Singleton connection to Solana RPC with caching utilities.
services/rpc.ts
// Get or create connection singleton
function getConnection(): Connection;
// Get connection with custom endpoint
function getConnection(endpoint?: string): Connection;
// Devnet RPC endpoint constant
const DEVNET_RPC: string;import { getConnection, DEVNET_RPC } from "@/services/rpc";
const connection = getConnection();
const balance = await connection.getBalance(publicKey);Utilities for SOL transfer operations.
features/transfer/services/transfer.service.ts
// Direct import (recommended)
import { createTransferInstruction } from "@/features/transfer/services";
// Backward compatible import
import { createTransferInstruction } from "@/services/transfer";/**
* Create a SOL transfer instruction
*/
function createTransferInstruction(
fromPubkey: PublicKey,
toPubkey: PublicKey,
lamports: number
): TransactionInstruction;
/**
* Validate a Solana address
*/
function isValidSolanaAddress(address: string): boolean;
/**
* Convert SOL to lamports
*/
function solToLamports(sol: number): number;
/**
* Convert lamports to SOL
*/
function lamportsToSol(lamports: number): number;import {
createTransferInstruction,
isValidSolanaAddress,
solToLamports,
} from "@/services/transfer";
// Validate address
if (!isValidSolanaAddress(recipientAddress)) {
throw new Error("Invalid address");
}
// Create instruction
const ix = createTransferInstruction(
wallet,
new PublicKey(recipientAddress),
solToLamports(0.01)
);Utilities for native SOL staking.
features/staking/services/staking.service.ts
// Direct import (recommended)
import { createStakeAccountInstructions } from "@/features/staking/services";
// Backward compatible import
import { createStakeAccountInstructions } from "@/services/staking";/**
* Create stake account and delegation instructions
* Uses createAccountWithSeed to avoid extra signers
*/
async function createStakeAccountInstructions(params: {
walletPubkey: PublicKey;
lamports: number;
validatorVoteAccount: PublicKey;
}): Promise<{
instructions: TransactionInstruction[];
stakeAccountPubkey: PublicKey;
}>;
/**
* Get current stake account rent exemption
*/
async function getStakeRentExemption(): Promise<number>;
/**
* Fetch active validators
*/
async function getValidators(): Promise<ValidatorInfo[]>;import { createStakeAccountInstructions } from "@/services/staking";
const { instructions, stakeAccountPubkey } =
await createStakeAccountInstructions({
walletPubkey: wallet,
lamports: LAMPORTS_PER_SOL * 1, // 1 SOL
validatorVoteAccount: validatorPubkey,
});
// Execute via LazorKit
await execute({ instructions });Utilities for on-chain memo operations.
features/memo/services/memo.service.ts
// Direct import (recommended)
import { createMemoInstruction } from "@/features/memo/services";
// Backward compatible import
import { createMemoInstruction } from "@/services/memo";/**
* Create a memo instruction
*/
function createMemoInstruction(
message: string,
signer: PublicKey
): TransactionInstruction;
/**
* Validate memo length
*/
function isValidMemoLength(message: string): boolean;
/**
* Maximum memo length in bytes
*/
const MAX_MEMO_LENGTH: number; // 566 bytesimport { createMemoInstruction, isValidMemoLength } from "@/services/memo";
if (!isValidMemoLength(message)) {
throw new Error("Memo too long");
}
const memoIx = createMemoInstruction(message, wallet);
await execute({ instructions: [memoIx] });Utilities for managing user session persistence with AsyncStorage.
features/session/services/session.service.ts
// Direct import (recommended)
import {
createSession,
getSession,
clearSession,
hasValidSession,
} from "@/features/session/services";
// Via hooks index
import { useSession } from "@/hooks";interface SessionData {
walletAddress: string;
createdAt: number;
expiresAt: number;
lastActivity: number;
isAuthenticated: boolean;
}
interface UserPreferences {
theme?: "light" | "dark" | "system";
showBalance?: boolean;
notifications?: boolean;
hapticFeedback?: boolean;
}/**
* Create a new session
*/
async function createSession(
walletAddress: string,
config?: { expiryMs?: number }
): Promise<SessionData | null>;
/**
* Get the current session
*/
async function getSession(): Promise<SessionData | null>;
/**
* Check if there's a valid session
*/
async function hasValidSession(): Promise<boolean>;
/**
* Clear the current session
*/
async function clearSession(): Promise<boolean>;
/**
* Update last activity timestamp
*/
async function updateLastActivity(): Promise<boolean>;
/**
* Extend session expiry
*/
async function extendSession(
additionalMs?: number
): Promise<SessionData | null>;
/**
* Get session time remaining
*/
async function getSessionTimeRemaining(): Promise<number>;
/**
* Check if session is about to expire
*/
async function isSessionExpiringSoon(thresholdMs?: number): Promise<boolean>;
/**
* Save user preferences
*/
async function saveUserPreferences(
preferences: Partial<UserPreferences>
): Promise<boolean>;
/**
* Get user preferences
*/
async function getUserPreferences(): Promise<UserPreferences>;
/**
* Clear all session data
*/
async function clearAllSessionData(keepPreferences?: boolean): Promise<boolean>;import {
createSession,
getSession,
hasValidSession,
clearSession,
} from "@/features/session/services";
// Create session after wallet connection
const session = await createSession(walletAddress);
// Check validity
if (await hasValidSession()) {
console.log("User is authenticated");
}
// Get session data
const currentSession = await getSession();
console.log(`Wallet: ${currentSession?.walletAddress}`);
// Logout
await clearSession();Common helper functions used throughout the app.
utils/helpers.ts
/**
* Truncate address for display
* "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
* → "7xKX...sAsU"
*/
function truncateAddress(address: string, chars?: number): string;
/**
* Format SOL amount for display
* 1.234567890 → "1.2346"
*/
function formatSol(lamports: number, decimals?: number): string;
/**
* Get Solana Explorer URL for transaction
*/
function getExplorerUrl(
signature: string,
cluster?: "mainnet" | "devnet" | "testnet"
): string;
/**
* Sleep for specified milliseconds
*/
function sleep(ms: number): Promise<void>;
/**
* Check if string is valid base58
*/
function isBase58(str: string): boolean;import { truncateAddress, formatSol, getExplorerUrl } from "@/utils/helpers";
// Display
<Text>{truncateAddress(address)}</Text>
<Text>{formatSol(lamports)} SOL</Text>
// Link
const url = getExplorerUrl(signature, "devnet");Build deep link URLs for authentication redirects.
utils/redirect-url.ts
/**
* Get appropriate redirect URL based on environment
* Expo Go: exp://192.168.1.x:8081
* Standalone: passpaymobile://
*/
function getRedirectUrl(path?: string): string;
/**
* Parse incoming redirect URL
*/
function parseRedirectUrl(url: string): {
path: string;
params: Record<string, string>;
};import { getRedirectUrl } from "@/utils/redirect-url";
// In LazorKitProvider config
const redirectUrl = getRedirectUrl();
// Connect with redirect
await connect({
redirectUri: getRedirectUrl("/auth-callback"),
});// Transaction type for history
type TransactionType = "transfer" | "stake" | "memo" | "other";
// Network cluster
type Cluster = "mainnet-beta" | "devnet" | "testnet";
// Validator information
interface ValidatorInfo {
votePubkey: PublicKey;
nodePubkey: PublicKey;
commission: number;
activatedStake: number;
}
// Balance info
interface BalanceInfo {
lamports: number;
sol: number;
}
// Transaction status
type TransactionStatus = "pending" | "confirmed" | "failed";// Wallet hook return type
interface UseWalletReturn {
smartWalletPubkey: PublicKey | null;
isConnecting: boolean;
connect: (options: ConnectOptions) => Promise<void>;
disconnect: () => Promise<void>;
signAndSendTransaction: (options: SignOptions) => Promise<string>;
signMessage: (message: Uint8Array) => Promise<Uint8Array>;
}
// Connection options
interface ConnectOptions {
redirectUri?: string;
}
// Signing options
interface SignOptions {
instructions: TransactionInstruction[];
lookupTables?: AddressLookupTableAccount[];
gasConfig?: {
type: "paymaster";
paymasterUrl: string;
};
redirectUrl?: string;
}// constants/theme.ts
export const Colors = {
primary: "#512DA8",
secondary: "#7C4DFF",
success: "#4CAF50",
error: "#F44336",
warning: "#FF9800",
background: {
light: "#FFFFFF",
dark: "#121212",
},
text: {
light: "#000000",
dark: "#FFFFFF",
},
};// Defined in services
export const DEVNET_RPC = "https://api.devnet.solana.com";
export const MAINNET_RPC = "https://api.mainnet-beta.solana.com";
export const PORTAL_URL = "https://portal.lazor.sh";
export const PAYMASTER_DEVNET = "https://kora.devnet.lazorkit.com";