--- name: solana-mobile description: Solana Mobile development with Mobile Wallet Adapter (MWA), Seeker device integration, SeedVault secure storage, and React Native dApp patterns for building mobile-first Solana applications. user-invocable: true --- # Solana Mobile Development Skill Expert knowledge for building mobile dApps on Solana with Mobile Wallet Adapter, Seeker device integration, and SeedVault support. --- ## Overview This skill provides comprehensive guidance for developing mobile applications on the Solana blockchain, with focus on: - **Mobile Wallet Adapter (MWA)** - Protocol for connecting mobile dApps to wallets - **Solana Mobile Seeker** - Detecting and optimizing for Seeker devices - **SeedVault** - Secure key storage on Solana Mobile devices - **React Native Integration** - Building cross-platform mobile dApps - **dApp Store Publishing** - Submitting apps to Solana dApp Store --- ## When to Use This Skill Activate this skill when: - Building React Native dApps for Solana - Implementing Mobile Wallet Adapter - Detecting Solana Mobile Seeker users - Integrating with SeedVault for secure key storage - Publishing to Solana dApp Store - Optimizing mobile user experience - Implementing Sign-In With Solana (SIWS) --- ## Core Concepts ### Mobile Wallet Adapter (MWA) MWA is a protocol that enables secure communication between mobile dApps and wallet apps. **Key Features:** - Secure WebSocket-based communication - Session-based authorization - Transaction signing - Message signing - Multi-wallet support **Protocol Flow:** 1. **Association** - Establish connection via URI scheme 2. **Session Establishment** - ECDH key exchange 3. **Authorization** - Request wallet access 4. **Privileged Methods** - Sign transactions/messages ### Solana Mobile Seeker Seeker is Solana's flagship mobile device with built-in crypto features. **Detection Methods:** 1. **Platform Constants** (Client-side, spoofable) 2. **Genesis Token Verification** (On-chain, guaranteed) **Seeker Features:** - SeedVault secure storage - Optimized Solana performance - Seeker Genesis Token (SGT) - Native dApp Store integration ### SeedVault Secure enclave for storing private keys on Solana Mobile devices. **Benefits:** - Hardware-backed security - Biometric authentication - Secure key derivation - Isolated from app memory --- ## Implementation Patterns ### 1. Mobile Wallet Adapter Setup #### Install Dependencies ```bash npm install @solana-mobile/mobile-wallet-adapter-protocol \ @solana-mobile/mobile-wallet-adapter-protocol-web3js \ @solana/web3.js \ react-native-quick-crypto ``` #### Configure Polyfills Create `polyfill.js`: ```javascript import { install } from 'react-native-quick-crypto'; install(); ``` Create `index.js`: ```javascript import './polyfill'; import 'expo-router/entry'; ``` Update `package.json`: ```json { "main": "./index.js" } ``` #### MWA Provider Setup ```typescript import { ConnectionProvider, RPC_ENDPOINT, } from '@solana/web3.js'; import { SolanaMobileWalletAdapter, } from '@solana-mobile/mobile-wallet-adapter-protocol-web3js'; export function MobileWalletProvider({ children }) { return ( {children} ); } ``` ### 2. Authorization Flow ```typescript import { transact } from '@solana-mobile/mobile-wallet-adapter-protocol-web3js'; async function authorizeWallet() { const result = await transact(async (wallet) => { const authResult = await wallet.authorize({ cluster: 'solana:mainnet', identity: { name: 'Obscura DEX', uri: 'https://obscura.finance', icon: 'icon.png', }, features: [ 'solana:signMessages', 'solana:signAndSendTransaction', ], }); return { accounts: authResult.accounts, authToken: authResult.auth_token, walletUriBase: authResult.wallet_uri_base, }; }); return result; } ``` ### 3. Sign and Send Transaction ```typescript import { Transaction } from '@solana/web3.js'; import { transact } from '@solana-mobile/mobile-wallet-adapter-protocol-web3js'; async function signAndSendTransaction(transaction: Transaction) { const result = await transact(async (wallet) => { // Authorize if needed const authResult = await wallet.authorize({ cluster: 'solana:mainnet', identity: APP_IDENTITY, }); // Get latest blockhash const { blockhash } = await connection.getLatestBlockhash(); transaction.recentBlockhash = blockhash; transaction.feePayer = authResult.accounts[0].publicKey; // Sign and send const signedTxs = await wallet.signAndSendTransactions({ transactions: [transaction], }); return signedTxs[0]; }); return result; } ``` ### 4. Sign Message ```typescript async function signMessage(message: string) { const result = await transact(async (wallet) => { const authResult = await wallet.authorize({ cluster: 'solana:mainnet', identity: APP_IDENTITY, }); const encodedMessage = new TextEncoder().encode(message); const signedMessages = await wallet.signMessages({ addresses: [authResult.accounts[0].address], payloads: [encodedMessage], }); return { signature: signedMessages.signed_payloads[0], address: authResult.accounts[0].address, }; }); return result; } ``` ### 5. Seeker Detection (Platform Constants) ```typescript import { Platform } from 'react-native'; function isSeekerDevice(): boolean { return Platform.constants?.Model === 'Seeker'; } function getSeekerInfo() { if (!isSeekerDevice()) { return null; } return { model: Platform.constants.Model, brand: Platform.constants.Brand, manufacturer: Platform.constants.Manufacturer, fingerprint: Platform.constants.Fingerprint, }; } // Usage if (isSeekerDevice()) { console.log('Running on Seeker device!'); // Show Seeker-specific UI } ``` ### 6. Seeker Genesis Token Verification #### Client-Side: Sign-In With Solana (SIWS) ```typescript async function signInWithSolana() { const result = await transact(async (wallet) => { const authResult = await wallet.authorize({ cluster: 'solana:mainnet', identity: APP_IDENTITY, sign_in_payload: { domain: 'obscura.finance', statement: 'Sign in to verify Seeker ownership', uri: 'https://obscura.finance', version: '1', chainId: 'solana:mainnet', nonce: generateNonce(), issuedAt: new Date().toISOString(), }, }); return { walletAddress: authResult.accounts[0].address, signInResult: authResult.sign_in_result, }; }); // Send to backend for verification const isVerified = await verifyOnBackend(result); return isVerified; } ``` #### Server-Side: Verify SIWS + Check SGT ```typescript import { verifySignIn } from '@solana/wallet-standard-util'; import { Connection, PublicKey } from '@solana/web3.js'; // Step 1: Verify SIWS signature async function verifySIWS(signInPayload, signInResult): Promise { const serializedOutput = { account: { publicKey: new Uint8Array(signInResult.account.publicKey), ...signInResult.account, }, signature: new Uint8Array(signInResult.signature), signedMessage: new Uint8Array(signInResult.signedMessage), }; return verifySignIn(signInPayload, serializedOutput); } // Step 2: Check for Seeker Genesis Token async function checkSeekerGenesisToken( walletAddress: string, heliusApiKey: string ): Promise { const response = await fetch( `https://api.helius.xyz/v0/addresses/${walletAddress}/balances?api-key=${heliusApiKey}` ); const data = await response.json(); // Check for Seeker Genesis Token // SGT Collection: https://solscan.io/collection/... const hasSGT = data.tokens?.some( (token) => token.mint === 'SEEKER_GENESIS_TOKEN_MINT' ); return hasSGT; } // Step 3: Combined verification async function verifySeekerUser(signInResult, heliusApiKey): Promise { // Verify SIWS signature const siwsVerified = await verifySIWS( signInResult.payload, signInResult.result ); if (!siwsVerified) { return false; } // Check SGT ownership const hasSGT = await checkSeekerGenesisToken( signInResult.walletAddress, heliusApiKey ); return hasSGT; } ``` ### 7. SeedVault Integration ```typescript // SeedVault is automatically used by MWA-compatible wallets // No additional code needed - keys are stored securely // To leverage SeedVault features: async function requestBiometricAuth() { // Wallet apps handle biometric auth automatically // when accessing SeedVault keys const result = await transact(async (wallet) => { // This will trigger biometric prompt if enabled const authResult = await wallet.authorize({ cluster: 'solana:mainnet', identity: APP_IDENTITY, }); return authResult; }); return result; } ``` ### 8. Reauthorization (Persistent Sessions) ```typescript // Store auth token securely import AsyncStorage from '@react-native-async-storage/async-storage'; async function saveAuthToken(authToken: string) { await AsyncStorage.setItem('mwa_auth_token', authToken); } async function getAuthToken(): Promise { return await AsyncStorage.getItem('mwa_auth_token'); } // Reauthorize with stored token async function reauthorize() { const authToken = await getAuthToken(); if (!authToken) { return authorizeWallet(); // First time auth } try { const result = await transact(async (wallet) => { const authResult = await wallet.authorize({ cluster: 'solana:mainnet', identity: APP_IDENTITY, auth_token: authToken, // Reuse token }); // Save new token if updated await saveAuthToken(authResult.auth_token); return authResult; }); return result; } catch (error) { // Token expired, request new auth await AsyncStorage.removeItem('mwa_auth_token'); return authorizeWallet(); } } ``` --- ## Best Practices ### Security 1. **Never Request Private Keys** - Always use MWA signing methods - Never ask users for seed phrases - Trust wallet apps for key management 2. **Verify Signatures** - Always verify SIWS signatures on backend - Use on-chain verification for critical features - Don't trust client-side checks alone 3. **Secure Token Storage** - Use AsyncStorage for auth tokens - Encrypt sensitive data - Clear tokens on logout ### User Experience 1. **Handle Wallet Not Installed** ```typescript try { await transact(async (wallet) => { // ... wallet operations }); } catch (error) { if (error.message.includes('No wallet found')) { // Show wallet installation prompt showInstallWalletDialog(); } } ``` 2. **Loading States** ```typescript const [isConnecting, setIsConnecting] = useState(false); async function connect() { setIsConnecting(true); try { await authorizeWallet(); } finally { setIsConnecting(false); } } ``` 3. **Error Handling** ```typescript try { await signAndSendTransaction(tx); } catch (error) { if (error.message.includes('User rejected')) { showToast('Transaction cancelled'); } else if (error.message.includes('Insufficient funds')) { showToast('Insufficient SOL balance'); } else { showToast('Transaction failed'); } } ``` ### Performance 1. **Reuse Connections** - Store auth tokens - Reauthorize instead of fresh auth - Use wallet_uri_base for faster connections 2. **Batch Transactions** ```typescript await wallet.signAndSendTransactions({ transactions: [tx1, tx2, tx3], // Batch multiple }); ``` 3. **Optimize for Seeker** ```typescript if (isSeekerDevice()) { // Use optimized settings for Seeker connection.commitment = 'confirmed'; // Faster } ``` --- ## Common Patterns ### Pattern 1: Connect Button ```typescript function ConnectWalletButton() { const [wallet, setWallet] = useState(null); const [loading, setLoading] = useState(false); async function handleConnect() { setLoading(true); try { const result = await reauthorize(); setWallet(result.accounts[0]); } catch (error) { console.error('Connection failed:', error); } finally { setLoading(false); } } if (wallet) { return ( Connected: {wallet.address.slice(0, 8)}... ); } return (