Paraโs Wallet Pregeneration feature allows you to create wallets for users before they authenticate, giving you control over when and how users claim ownership of their wallets. This is particularly powerful in mobile applications, where you can leverage device-specific storage capabilities for enhanced user experiences.
Mobile-Specific Benefits
While pregeneration works the same across all Para SDKs, React Native and Expo applications offer unique advantages:
Pregeneration is especially valuable for devices that may not have full WebAuthn support for passkeys. It allows you to create Para wallets for users on any device while managing the security of the wallet yourself.
Creating a Pregenerated Wallet
Check if a wallet exists
import { para } from '../your-para-client';
async function checkPregenWallet() {
const hasWallet = await para.hasPregenWallet({
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
return hasWallet;
}
Create a pregenerated wallet
import { para } from '../your-para-client';
import { WalletType } from '@getpara/react-native-wallet';
async function createPregenWallet() {
const pregenWallet = await para.createPregenWallet({
type: WalletType.EVM,
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
console.log("Pregenerated Wallet ID:", pregenWallet.id);
return pregenWallet;
}
Retrieve the user share
import { para } from '../your-para-client';
async function getUserShare() {
const userShare = await para.getUserShare();
// Store this share securely
return userShare;
}
Mobile Storage Options
In mobile applications, you have several options for securely storing the user share:
import * as Keychain from 'react-native-keychain';
// Store the user share
async function storeUserShare(userShare) {
try {
await Keychain.setGenericPassword(
'para_user_share',
userShare,
{
service: 'com.yourapp.wallet',
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY
}
);
} catch (error) {
console.error("Error storing user share:", error);
}
}
// Retrieve the user share
async function retrieveUserShare() {
try {
const credentials = await Keychain.getGenericPassword({
service: 'com.yourapp.wallet'
});
if (credentials) {
return credentials.password;
}
return null;
} catch (error) {
console.error("Error retrieving user share:", error);
return null;
}
}
import * as Keychain from 'react-native-keychain';
// Store the user share
async function storeUserShare(userShare) {
try {
await Keychain.setGenericPassword(
'para_user_share',
userShare,
{
service: 'com.yourapp.wallet',
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY
}
);
} catch (error) {
console.error("Error storing user share:", error);
}
}
// Retrieve the user share
async function retrieveUserShare() {
try {
const credentials = await Keychain.getGenericPassword({
service: 'com.yourapp.wallet'
});
if (credentials) {
return credentials.password;
}
return null;
} catch (error) {
console.error("Error retrieving user share:", error);
return null;
}
}
import { MMKV } from 'react-native-mmkv';
// Initialize with encryption for better security
const storage = new MMKV({
id: 'wallet-storage',
encryptionKey: 'your-secure-encryption-key'
});
// Store the user share
function storeUserShare(userShare) {
storage.set('user_share', userShare);
}
// Retrieve the user share
function retrieveUserShare() {
return storage.getString('user_share');
}
Whichever storage method you choose, ensure you implement proper security measures. The user share is critical for wallet access, and if lost, the wallet becomes permanently inaccessible.
Using Pregenerated Wallets in Mobile Apps
Once you have created a pregenerated wallet and stored the user share, you can use it for signing operations:
import { para } from '../your-para-client';
async function usePregenWallet() {
// Retrieve the user share from your secure storage
const userShare = await retrieveUserShare();
if (!userShare) {
console.error("User share not found");
return;
}
// Load the user share into the Para client
await para.setUserShare(userShare);
// Now you can perform signing operations
const messageBase64 = btoa("Hello, World!");
const signature = await para.signMessage({
walletId: "your-wallet-id",
messageBase64,
});
return signature;
}
Mobile-Specific Use Cases
Claiming Pregenerated Wallets
When a user is ready to take ownership of their pregenerated wallet, they can claim it once theyโve authenticated with Para:
import { para } from '../your-para-client';
async function claimWallet() {
// Ensure user is authenticated
if (!(await para.isFullyLoggedIn())) {
console.error("User must be authenticated to claim wallets");
return;
}
// Retrieve and load the user share
const userShare = await retrieveUserShare();
await para.setUserShare(userShare);
// Claim the wallet
const recoverySecret = await para.claimPregenWallets();
// Optionally, clear the locally stored user share after claiming
// since Para now manages it through the user's authentication
await clearUserShare();
return recoverySecret;
}
After claiming, Para will manage the user share through the userโs authentication methods. You can safely remove the user share from your local storage if you no longer need to access the wallet directly.
Best Practices for Mobile
-
Utilize Device Security: Leverage biometric authentication (TouchID/FaceID) to protect access to locally stored user shares.
-
Implement Device Sync: For users with multiple devices, consider implementing your own synchronization mechanism for user shares across devices.
-
Handle Offline States: Mobile applications often work offline. Design your pregenerated wallet system to function properly even when connectivity is limited.
-
Backup Strategies: Provide users with options to back up their wallet data, especially for device-specific wallets that might not be associated with their Para account.
-
Clear Security Boundaries: Clearly communicate to users when theyโre using an app-managed wallet versus a personally-owned wallet.