| 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:
- Association - Establish connection via URI scheme
- Session Establishment - ECDH key exchange
- Authorization - Request wallet access
- Privileged Methods - Sign transactions/messages
Solana Mobile Seeker
Seeker is Solana's flagship mobile device with built-in crypto features.
Detection Methods:
- Platform Constants (Client-side, spoofable)
- 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
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:
import { install } from 'react-native-quick-crypto';
install();
Create index.js:
import './polyfill';
import 'expo-router/entry';
Update package.json:
{
"main": "./index.js"
}
MWA Provider Setup
import {
ConnectionProvider,
RPC_ENDPOINT,
} from '@solana/web3.js';
import {
SolanaMobileWalletAdapter,
} from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';
export function MobileWalletProvider({ children }) {
return (
<ConnectionProvider endpoint={RPC_ENDPOINT}>
<SolanaMobileWalletAdapter
cluster="mainnet-beta"
appIdentity={{
name: 'Your dApp',
uri: 'https://yourdapp.com',
icon: 'favicon.ico',
}}
>
{children}
</SolanaMobileWalletAdapter>
</ConnectionProvider>
);
}
2. Authorization Flow
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
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) => {
const authResult = await wallet.authorize({
cluster: 'solana:mainnet',
identity: APP_IDENTITY,
});
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = authResult.accounts[0].publicKey;
const signedTxs = await wallet.signAndSendTransactions({
transactions: [transaction],
});
return signedTxs[0];
});
return result;
}
4. Sign Message
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)
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,
};
}
if (isSeekerDevice()) {
console.log('Running on Seeker device!');
}
6. Seeker Genesis Token Verification
Client-Side: Sign-In With Solana (SIWS)
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,
};
});
const isVerified = await verifyOnBackend(result);
return isVerified;
}
Server-Side: Verify SIWS + Check SGT
import { verifySignIn } from '@solana/wallet-standard-util';
import { Connection, PublicKey } from '@solana/web3.js';
async function verifySIWS(signInPayload, signInResult): Promise<boolean> {
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);
}
async function checkSeekerGenesisToken(
walletAddress: string,
heliusApiKey: string
): Promise<boolean> {
const response = await fetch(
`https://api.helius.xyz/v0/addresses/${walletAddress}/balances?api-key=${heliusApiKey}`
);
const data = await response.json();
const hasSGT = data.tokens?.some(
(token) => token.mint === 'SEEKER_GENESIS_TOKEN_MINT'
);
return hasSGT;
}
async function verifySeekerUser(signInResult, heliusApiKey): Promise<boolean> {
const siwsVerified = await verifySIWS(
signInResult.payload,
signInResult.result
);
if (!siwsVerified) {
return false;
}
const hasSGT = await checkSeekerGenesisToken(
signInResult.walletAddress,
heliusApiKey
);
return hasSGT;
}
7. SeedVault Integration
async function requestBiometricAuth() {
const result = await transact(async (wallet) => {
const authResult = await wallet.authorize({
cluster: 'solana:mainnet',
identity: APP_IDENTITY,
});
return authResult;
});
return result;
}
8. Reauthorization (Persistent Sessions)
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<string | null> {
return await AsyncStorage.getItem('mwa_auth_token');
}
async function reauthorize() {
const authToken = await getAuthToken();
if (!authToken) {
return authorizeWallet();
}
try {
const result = await transact(async (wallet) => {
const authResult = await wallet.authorize({
cluster: 'solana:mainnet',
identity: APP_IDENTITY,
auth_token: authToken,
});
await saveAuthToken(authResult.auth_token);
return authResult;
});
return result;
} catch (error) {
await AsyncStorage.removeItem('mwa_auth_token');
return authorizeWallet();
}
}
Best Practices
Security
-
Never Request Private Keys
- Always use MWA signing methods
- Never ask users for seed phrases
- Trust wallet apps for key management
-
Verify Signatures
- Always verify SIWS signatures on backend
- Use on-chain verification for critical features
- Don't trust client-side checks alone
-
Secure Token Storage
- Use AsyncStorage for auth tokens
- Encrypt sensitive data
- Clear tokens on logout
User Experience
-
Handle Wallet Not Installed
try {
await transact(async (wallet) => {
});
} catch (error) {
if (error.message.includes('No wallet found')) {
showInstallWalletDialog();
}
}
-
Loading States
const [isConnecting, setIsConnecting] = useState(false);
async function connect() {
setIsConnecting(true);
try {
await authorizeWallet();
} finally {
setIsConnecting(false);
}
}
-
Error Handling
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
-
Reuse Connections
- Store auth tokens
- Reauthorize instead of fresh auth
- Use wallet_uri_base for faster connections
-
Batch Transactions
await wallet.signAndSendTransactions({
transactions: [tx1, tx2, tx3],
});
-
Optimize for Seeker
if (isSeekerDevice()) {
connection.commitment = 'confirmed';
}
Common Patterns
Pattern 1: Connect Button
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 (
<View>
<Text>Connected: {wallet.address.slice(0, 8)}...</Text>
</View>
);
}
return (
<Button
title={loading ? 'Connecting...' : 'Connect Wallet'}
onPress={handleConnect}
disabled={loading}
/>
);
}
Pattern 2: Seeker-Specific UI
function SeekerBanner() {
const isSeeker = isSeekerDevice();
const [hasGenesisToken, setHasGenesisToken] = useState(false);
useEffect(() => {
if (isSeeker) {
verifyGenesisToken().then(setHasGenesisToken);
}
}, [isSeeker]);
if (!isSeeker) return null;
return (
<View style={styles.seekerBanner}>
<Text>🎉 Seeker Device Detected!</Text>
{hasGenesisToken && (
<Text>✅ Genesis Token Verified</Text>
)}
</View>
);
}
Pattern 3: Transaction with Confirmation
async function sendTransactionWithConfirmation(transaction: Transaction) {
const signature = await signAndSendTransaction(transaction);
const confirmation = await connection.confirmTransaction(
signature,
'confirmed'
);
if (confirmation.value.err) {
throw new Error('Transaction failed');
}
return signature;
}
Publishing to dApp Store
1. Build APK
eas build --platform android --profile production
cd android
./gradlew assembleRelease
2. Sign APK
keytool -genkey -v -keystore my-release-key.keystore \
-alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \
-keystore my-release-key.keystore \
app-release-unsigned.apk my-key-alias
3. Submit to dApp Store
npm install -g @solana-mobile/dapp-store-cli
dapp-store login
dapp-store create
dapp-store publish \
--apk ./app-release.apk \
--requestor-is-authorized \
--complies-with-solana-dapp-store-policies
Troubleshooting
Issue: Wallet Not Connecting
Solutions:
- Check if wallet app is installed
- Verify network (devnet/mainnet)
- Clear app cache
- Reinstall wallet app
Issue: Transaction Fails
Solutions:
- Check SOL balance for fees
- Verify recent blockhash
- Check transaction size
- Ensure correct cluster
Issue: Seeker Not Detected
Solutions:
- Verify Platform.constants.Model
- Check device fingerprint
- Use Genesis Token verification
- Test on actual Seeker device
Issue: Auth Token Expired
Solutions:
- Clear stored token
- Request fresh authorization
- Implement token refresh logic
- Handle ERROR_AUTHORIZATION_FAILED
Resources
Documentation
Tools
Example Apps
Integration with Obscura DEX
Mobile-Specific Features
-
Seeker Rewards
- Detect Seeker users
- Offer reduced trading fees
- Exclusive features for SGT holders
-
Mobile-Optimized UI
- Touch-friendly controls
- Simplified trading interface
- Biometric authentication
-
Push Notifications
- Price alerts
- Order fills
- Market updates
-
Offline Capabilities
- Cache market data
- Queue transactions
- Sync when online
Last Updated: February 18, 2026