Package Exports
- p-sdk-wallet
- p-sdk-wallet/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (p-sdk-wallet) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
p-sdk-wallet
A comprehensive, secure, and easy-to-use wallet SDK for React Native applications from PWC.
This SDK provides a high-level Vault API to manage multiple accounts (from mnemonic or private keys) under a single password, similar to MetaMask. It's built with high security in mind, using AES and PBKDF2 for strong encryption.
Features
- ๐ High Security: Uses AES and PBKDF2 for strong encryption of the entire vault.
- ๐๏ธ Vault Architecture: Manages multiple accounts under a single password.
- ๐ Multi-Account: Supports HD wallets (BIP-44) and imported private key accounts.
- ๐ Multi-Chain: Ready for any EVM-compatible chain.
- ๐จโ๐ป Dev-Friendly API: A simple, high-level API that abstracts away cryptographic complexity.
Installation
npm install p-sdk-wallet
# or
yarn add p-sdk-walletโ ๏ธ Important: Setup Required
To use this SDK, your React Native application needs some configuration. Please follow these steps carefully.
Step 1: Install All Required Packages
Install the SDK itself, its peer dependencies, and all necessary polyfill packages with one command.
yarn add p-sdk-wallet ethers react-native-keychain @react-native-async-storage/async-storage react-native-get-random-values buffer process text-encoding stream-browserify eventsStep 2: Configure Metro for Node.js Core Modules
Some libraries used by the SDK depend on Node.js core modules (stream, events) that don't exist in React Native. You need to tell Metro (the bundler) to use the browser-compatible versions you just installed.
Modify your metro.config.js at the root of your project:
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
/**
* Metro configuration
* https://reactnative.dev/docs/metro
*
* @type {import('@react-native/metro-config').MetroConfig}
*/
const config = {
resolver: {
extraNodeModules: {
// Polyfill for stream and events
stream: require.resolve('stream-browserify'),
events: require.resolve('events/'),
},
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);Note: After changing metro.config.js, you must restart your bundler with a cache reset: npx react-native start --reset-cache.
Step 3: Set up Global Polyfills
Finally, load the remaining polyfills into the global environment of your app.
In your main entry file (index.js or App.tsx), add the following code at the very top, before any other imports:
// --- Start of Polyfills ---
// Required for crypto operations
import 'react-native-get-random-values';
// Polyfill for Buffer
if (typeof global.Buffer === 'undefined') {
global.Buffer = require('buffer').Buffer;
}
// Polyfill for process (required by some dependencies)
global.process = require('process');
global.process.env.NODE_ENV = __DEV__ ? 'development' : 'production';
// Polyfill for TextEncoder/TextDecoder (required by some dependencies)
import { TextEncoder, TextDecoder } from 'text-encoding';
if (typeof global.TextEncoder === 'undefined') {
global.TextEncoder = TextEncoder;
}
if (typeof global.TextDecoder === 'undefined') {
global.TextDecoder = TextDecoder;
}
// --- End of Polyfills ---
// Your other imports and AppRegistry call...API Reference & Usage Examples
1. Vault Creation & Management
Create New Vault
import { Vault, type EncryptedData } from 'p-sdk-wallet';
// Create a new vault with a fresh mnemonic
const { vault, encryptedVault } = await Vault.createNew('your-secure-password');
// Save encryptedVault securely (iOS Keychain, AsyncStorage, etc.)
await saveToSecureStorage(encryptedVault);
console.log('Vault created with accounts:', vault.getAccounts());Load Existing Vault
// Load vault from encrypted data
const encryptedVault: EncryptedData = await loadFromSecureStorage();
const vault = await Vault.load('your-secure-password', encryptedVault);
console.log('Vault loaded successfully');Export Encrypted Vault
// Get the current encrypted vault data for backup
const encryptedVault = vault.exportEncryptedVault();
console.log('Encrypted vault data:', encryptedVault);2. Account Management
Add New HD Account
// Add a new HD account derived from the mnemonic
const newAccount = await vault.addNewHDAccount();
console.log('New HD Account:', {
address: newAccount.address,
name: newAccount.name,
type: newAccount.type
});Import Account from Private Key
// Import an existing account using private key
const privateKey = '0x1234567890abcdef...'; // 64-character hex string
const importedAccount = await vault.importAccount(privateKey);
console.log('Imported Account:', {
address: importedAccount.address,
name: importedAccount.name,
type: importedAccount.type
});Get All Accounts
// Get all accounts (HD + imported)
const allAccounts = vault.getAccounts();
console.log('All accounts:', allAccounts);
// Filter by account type
const hdAccounts = allAccounts.filter(acc => acc.type === 'HD');
const importedAccounts = allAccounts.filter(acc => acc.type === 'Simple');Remove Account
// Remove an imported account (HD accounts cannot be removed)
const accountToRemove = vault.getAccounts().find(acc => acc.type === 'Simple');
if (accountToRemove) {
vault.removeAccount(accountToRemove.address);
console.log('Account removed:', accountToRemove.address);
}3. Security Operations
Export Mnemonic (Recovery Phrase)
// Export the 24-word mnemonic phrase (requires password verification)
try {
const mnemonic = await vault.exportMnemonic('your-secure-password');
console.log('Recovery phrase:', mnemonic);
// IMPORTANT: Show this to user securely and never log it
// User should write it down and store it safely
} catch (error) {
console.error('Failed to export mnemonic:', error.message);
}Get Private Key for Account
// Get private key for a specific account (vault must be unlocked)
const accountAddress = '0x1234...';
const privateKey = vault.getPrivateKeyFor(accountAddress);
console.log('Private key:', privateKey);
// WARNING: Handle private key securely, never expose itCheck Vault Status
// Check if vault is unlocked
const isUnlocked = vault.isUnlocked();
console.log('Vault unlocked:', isUnlocked);
// Get vault information
const vaultInfo = vault.getVaultInfo();
console.log('Vault info:', vaultInfo);4. Balance & Token Operations
Get Native Token Balance
import { type ChainId } from 'p-sdk-wallet';
import { ethers } from 'ethers';
const accountAddress = '0x1234...';
const chainId: ChainId = '1'; // Ethereum mainnet
// Get native token balance (ETH, BNB, MATIC, etc.)
const balance = await vault.getNativeBalance(accountAddress, chainId);
console.log('Balance:', ethers.formatEther(balance), 'ETH');Get ERC-20 Token Balance
const tokenAddress = '0xA0b86a33E6441b8c4C8C8C8C8C8C8C8C8C8C8C8C'; // USDC
const tokenBalance = await vault.getTokenBalance(accountAddress, tokenAddress, chainId);
console.log('Token balance:', tokenBalance.toString());Get Token Information
// Get token metadata (name, symbol, decimals)
const tokenInfo = await vault.getTokenInfo(tokenAddress, chainId);
console.log('Token info:', {
name: tokenInfo.name,
symbol: tokenInfo.symbol,
decimals: tokenInfo.decimals,
totalSupply: tokenInfo.totalSupply.toString()
});5. Transaction Operations
Send Native Tokens
const senderAddress = '0x1234...';
const recipientAddress = '0x5678...';
const amount = '0.01'; // 0.01 ETH
const chainId: ChainId = '1';
try {
const txResponse = await vault.sendNativeToken(
senderAddress,
recipientAddress,
amount,
chainId
);
console.log('Transaction sent:', {
hash: txResponse.hash,
from: txResponse.from,
to: txResponse.to,
value: txResponse.value.toString()
});
// Wait for confirmation
const receipt = await txResponse.wait();
console.log('Transaction confirmed:', receipt.blockNumber);
} catch (error) {
console.error('Transaction failed:', error.message);
}Send ERC-20 Tokens
const tokenAddress = '0xA0b86a33E6441b8c4C8C8C8C8C8C8C8C8C8C8C8C'; // USDC
const amount = '100'; // 100 USDC
try {
const txResponse = await vault.sendToken(
senderAddress,
recipientAddress,
tokenAddress,
amount,
chainId
);
console.log('Token transaction sent:', txResponse.hash);
} catch (error) {
console.error('Token transaction failed:', error.message);
}Get Transaction History
// Get recent transactions for an account
const transactions = await vault.getTransactionHistory(accountAddress, chainId);
console.log('Transaction history:', transactions);6. Multi-Chain Support
Supported Networks
import { SUPPORTED_CHAINS } from 'p-sdk-wallet';
console.log('Supported chains:', SUPPORTED_CHAINS);
// Output:
// {
// '1': { name: 'Ethereum', symbol: 'ETH', ... },
// '56': { name: 'BNB Smart Chain', symbol: 'BNB', ... },
// '137': { name: 'Polygon', symbol: 'MATIC', ... },
// '42161': { name: 'Arbitrum One', symbol: 'ETH', ... },
// '10': { name: 'Optimism', symbol: 'ETH', ... },
// '8453': { name: 'Base', symbol: 'ETH', ... }
// }Cross-Chain Operations
// Work with multiple chains simultaneously
const ethereumBalance = await vault.getNativeBalance(accountAddress, '1');
const bscBalance = await vault.getNativeBalance(accountAddress, '56');
const polygonBalance = await vault.getNativeBalance(accountAddress, '137');
console.log('Multi-chain balances:', {
ethereum: ethers.formatEther(ethereumBalance),
bsc: ethers.formatEther(bscBalance),
polygon: ethers.formatEther(polygonBalance)
});7. Vanity Wallet Generation
Generate Vanity HD Wallet
// Generate a wallet where the first account address starts with '0xaaa' (default from config)
const { mnemonic, vault, encryptedVault, address, attempts, elapsedMs } = await Vault.generateVanityHDWallet({
password: 'password'
});
console.log('Vanity address:', address);
console.log('Attempts:', attempts);
console.log('Time taken:', elapsedMs, 'ms');
// Output: 0xaaa1234567890abcdef...Custom Vanity Prefix
// Generate with custom prefix
const { mnemonic, vault, encryptedVault, address } = await Vault.generateVanityHDWallet({
prefix: 'dead', // Custom prefix (without 0x)
password: 'password'
});
console.log('Custom vanity address:', address);
// Output: 0xdead1234567890abcdef...Advanced Vanity Configuration
// Full control over vanity generation
const { mnemonic, vault, encryptedVault, address } = await Vault.generateVanityHDWallet({
prefix: 'cool',
password: 'password',
maxAttempts: 50000,
caseSensitive: false,
onProgress: (attempts: number, elapsedMs: number) => {
console.log(`Attempt ${attempts}, elapsed: ${elapsedMs}ms`);
}
});8. Error Handling Examples
Comprehensive Error Handling
const handleWalletOperations = async () => {
try {
// Create or load vault
const vault = await Vault.load(password, encryptedVault);
// Add account
const account = await vault.addNewHDAccount();
// Get balance
const balance = await vault.getNativeBalance(account.address, '1');
// Send transaction
const tx = await vault.sendNativeToken(
account.address,
recipientAddress,
'0.01',
'1'
);
console.log('All operations successful');
} catch (error) {
if (error instanceof Error) {
switch (error.message) {
case 'Decryption failed':
console.error('Incorrect password');
break;
case 'Insufficient balance':
console.error('Not enough funds for transaction');
break;
case 'Invalid address':
console.error('Invalid recipient address');
break;
case 'Network error':
console.error('Network connection issue');
break;
default:
console.error('Unknown error:', error.message);
}
}
}
};Rate Limiting for Export Mnemonic
// The exportMnemonic function has built-in rate limiting
// It will throw an error if called too frequently
try {
const mnemonic = await vault.exportMnemonic(password);
// Success
} catch (error) {
if (error.message.includes('Rate limit exceeded')) {
console.error('Please wait before trying again');
}
}9. Advanced Usage Patterns
Batch Operations
// Perform multiple operations efficiently
const batchOperations = async (vault: Vault) => {
const accounts = vault.getAccounts();
const chainId: ChainId = '1';
// Get balances for all accounts
const balancePromises = accounts.map(account =>
vault.getNativeBalance(account.address, chainId)
);
const balances = await Promise.all(balancePromises);
accounts.forEach((account, index) => {
console.log(`${account.name}: ${ethers.formatEther(balances[index])} ETH`);
});
};Wallet Backup and Restore
// Complete backup process
const backupWallet = async (vault: Vault, password: string) => {
// Export encrypted vault
const encryptedVault = vault.exportEncryptedVault();
// Export mnemonic (for recovery)
const mnemonic = await vault.exportMnemonic(password);
// Save both securely
const backup = {
encryptedVault,
mnemonic,
timestamp: Date.now(),
version: '1.0.0'
};
return backup;
};
// Restore from backup
const restoreWallet = async (backup: any, password: string) => {
const vault = await Vault.load(password, backup.encryptedVault);
return vault;
};Security Best Practices
// Secure wallet initialization
const secureWalletInit = async () => {
// 1. Generate strong password
const password = generateStrongPassword();
// 2. Create vault with vanity address (uses default config)
const { mnemonic, vault, encryptedVault, address } = await Vault.generateVanityHDWallet({
password: password
});
// 3. Save encrypted vault securely
await saveToSecureStorage(encryptedVault);
// 4. Export and display mnemonic securely
const exportedMnemonic = await vault.exportMnemonic(password);
displayMnemonicSecurely(exportedMnemonic);
// 5. Clear sensitive data from memory
clearSensitiveData();
return vault;
};10. TypeScript Types
import {
Vault,
type Account,
type ChainId,
type EncryptedData,
type TokenInfo,
type TransactionResponse,
type VaultInfo
} from 'p-sdk-wallet';
// Use types for better development experience
const accounts: Account[] = vault.getAccounts();
const chainId: ChainId = '1';
const tokenInfo: TokenInfo = await vault.getTokenInfo(tokenAddress, chainId);This comprehensive API reference covers all the functions available in the PWC Wallet SDK. Each example includes proper error handling and follows security best practices.
Loading a Vault from iOS Device
The SDK uses iOS Keychain for secure storage of the encrypted vault. Here's how to properly implement vault loading on iOS:
Step 1: Install react-native-keychain
yarn add react-native-keychain
cd ios && pod installStep 2: iOS-specific Setup
Add the following to your ios/YourApp/Info.plist:
<key>NSFaceIDUsageDescription</key>
<string>This app uses Face ID to securely access your wallet</string>Step 3: Vault Storage and Loading Implementation
import { Vault, type EncryptedData } from 'p-sdk-wallet';
import * as Keychain from 'react-native-keychain';
class WalletManager {
private static readonly VAULT_KEY = 'pwc-wallet-vault';
private static readonly VAULT_SERVICE = 'com.pwc.wallet';
/**
* Save the encrypted vault to iOS Keychain
*/
static async saveVault(encryptedVault: EncryptedData): Promise<void> {
try {
const vaultData = JSON.stringify(encryptedVault);
await Keychain.setGenericPassword(
this.VAULT_KEY,
vaultData,
{
service: this.VAULT_SERVICE,
accessControl: Keychain.ACCESS_CONTROL.BIOMETRIC_ANY,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
authenticationType: Keychain.AUTHENTICATION_TYPE.BIOMETRICS,
}
);
console.log('Vault saved to Keychain successfully');
} catch (error) {
console.error('Failed to save vault to Keychain:', error);
throw new Error('Failed to save wallet securely');
}
}
/**
* Load the encrypted vault from iOS Keychain
*/
static async loadVault(password: string): Promise<Vault> {
try {
// Retrieve the encrypted vault from Keychain
const credentials = await Keychain.getGenericPassword({
service: this.VAULT_SERVICE,
authenticationPrompt: {
title: 'Unlock Wallet',
subtitle: 'Use Face ID or Touch ID to access your wallet',
description: 'Authenticate to unlock your wallet',
cancel: 'Cancel',
},
});
if (!credentials || !credentials.password) {
throw new Error('No wallet found. Please create a new wallet first.');
}
// Parse the encrypted vault data
const encryptedVault: EncryptedData = JSON.parse(credentials.password);
// Load the vault using the provided password
const vault = await Vault.load(password, encryptedVault);
console.log('Vault loaded successfully');
return vault;
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('Decryption failed')) {
throw new Error('Incorrect password. Please try again.');
}
if (error.message.includes('No wallet found')) {
throw new Error('No wallet found. Please create a new wallet first.');
}
}
console.error('Failed to load vault:', error);
throw new Error('Failed to load wallet. Please try again.');
}
}
/**
* Check if a wallet exists on the device
*/
static async hasExistingWallet(): Promise<boolean> {
try {
const credentials = await Keychain.getGenericPassword({
service: this.VAULT_SERVICE,
});
return !!credentials && !!credentials.password;
} catch (error) {
return false;
}
}
/**
* Delete the wallet from Keychain (for logout/reset)
*/
static async deleteVault(): Promise<void> {
try {
await Keychain.resetGenericPassword({
service: this.VAULT_SERVICE,
});
console.log('Vault deleted from Keychain');
} catch (error) {
console.error('Failed to delete vault:', error);
throw new Error('Failed to delete wallet');
}
}
}
// Usage Example:
const initializeWallet = async (password: string) => {
try {
// Check if wallet exists
const hasWallet = await WalletManager.hasExistingWallet();
if (hasWallet) {
// Load existing wallet
const vault = await WalletManager.loadVault(password);
console.log('Wallet loaded:', vault.getAccounts());
return vault;
} else {
// Create new wallet
const { vault: newVault, encryptedVault } = await Vault.createNew(password);
await WalletManager.saveVault(encryptedVault);
console.log('New wallet created:', vault.getAccounts());
return vault;
}
} catch (error) {
console.error('Wallet initialization failed:', error);
throw error;
}
};Step 4: React Native Component Example
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, TouchableOpacity, Alert } from 'react-native';
import { Vault } from 'p-sdk-wallet';
import { WalletManager } from './WalletManager';
const WalletScreen: React.FC = () => {
const [password, setPassword] = useState('');
const [vault, setVault] = useState<Vault | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
checkExistingWallet();
}, []);
const checkExistingWallet = async () => {
const hasWallet = await WalletManager.hasExistingWallet();
if (!hasWallet) {
Alert.alert(
'Welcome!',
'No wallet found. Please create a new wallet to get started.',
[{ text: 'OK' }]
);
}
};
const handleUnlockWallet = async () => {
if (!password.trim()) {
Alert.alert('Error', 'Please enter your password');
return;
}
setLoading(true);
try {
const loadedVault = await WalletManager.loadVault(password);
setVault(loadedVault);
setPassword('');
const accounts = loadedVault.getAccounts();
Alert.alert('Success', `Wallet unlocked! Found ${accounts.length} account(s)`);
} catch (error) {
Alert.alert('Error', error instanceof Error ? error.message : 'Failed to unlock wallet');
} finally {
setLoading(false);
}
};
const handleCreateWallet = async () => {
if (!password.trim()) {
Alert.alert('Error', 'Please enter a password');
return;
}
setLoading(true);
try {
const { vault: newVault, encryptedVault } = await Vault.createNew(password);
await WalletManager.saveVault(encryptedVault);
setVault(newVault);
setPassword('');
Alert.alert(
'Wallet Created!',
'Your wallet has been created successfully. Please save your recovery phrase in a secure location.',
[{ text: 'OK' }]
);
} catch (error) {
Alert.alert('Error', 'Failed to create wallet');
} finally {
setLoading(false);
}
};
return (
<View style={{ flex: 1, padding: 20 }}>
<Text style={{ fontSize: 24, marginBottom: 20 }}>PWC Wallet</Text>
<TextInput
style={{ borderWidth: 1, borderColor: '#ccc', padding: 10, marginBottom: 20 }}
placeholder="Enter password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity
style={{ backgroundColor: '#007AFF', padding: 15, marginBottom: 10 }}
onPress={handleUnlockWallet}
disabled={loading}
>
<Text style={{ color: 'white', textAlign: 'center' }}>
{loading ? 'Loading...' : 'Unlock Wallet'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ backgroundColor: '#34C759', padding: 15 }}
onPress={handleCreateWallet}
disabled={loading}
>
<Text style={{ color: 'white', textAlign: 'center' }}>
{loading ? 'Creating...' : 'Create New Wallet'}
</Text>
</TouchableOpacity>
{vault && (
<View style={{ marginTop: 20 }}>
<Text style={{ fontSize: 18, marginBottom: 10 }}>Your Accounts:</Text>
{vault.getAccounts().map((account, index) => (
<Text key={index} style={{ marginBottom: 5 }}>
{account.name}: {account.address}
</Text>
))}
</View>
)}
</View>
);
};
export default WalletScreen;Key iOS Security Features
- Biometric Authentication: Uses Face ID/Touch ID for additional security
- Device-Only Access: Vault is only accessible when the device is unlocked
- Secure Storage: Uses iOS Keychain, the most secure storage on iOS
- Access Control: Prevents unauthorized access even if the device is compromised
Error Handling Best Practices
- Always handle Keychain errors gracefully
- Provide user-friendly error messages
- Implement proper loading states
- Validate password strength for new wallets
- Handle biometric authentication failures
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
License
MIT
๐ New: Solana Support
The SDK now supports Solana blockchain with full HD wallet functionality, SPL token operations, and vanity wallet generation!
Solana Features:
- โ HD Wallet Support - Generate and manage multiple Solana accounts from a single mnemonic
- โ SPL Token Operations - Send and receive SPL tokens with associated token accounts
- โ Vanity Wallet Generation - Create Solana wallets with custom address prefixes
- โ Multi-Chain Support - Seamlessly switch between EVM and Solana chains
- โ React Native Compatible - Works perfectly in mobile applications
๐ Quick Start
Installation
npm install pwc-wallet-sdk
# or
yarn add pwc-wallet-sdkBasic Usage
import { Vault } from 'pwc-wallet-sdk';
// Create a new wallet (EVM by default)
const { vault, encryptedVault } = await Vault.createNew('your-password');
// Create a new Solana wallet
const { vault: solanaVault, encryptedVault: solanaEncryptedVault } =
await Vault.createNew('your-password', 'solana');
// Load existing wallet
const loadedVault = await Vault.load('your-password', encryptedVault);
// Get all accounts
const accounts = vault.getAccounts();
console.log('Accounts:', accounts);๐ API Reference
Vault Management
Creating Vaults
// Create new EVM vault
const { vault, encryptedVault } = await Vault.createNew('password');
// Create new Solana vault
const { vault, encryptedVault } = await Vault.createNew('password', 'solana');
// Create from existing mnemonic (EVM)
const { vault, encryptedVault } = await Vault.createFromMnemonic(
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
'password'
);
// Create from existing mnemonic (Solana)
const { vault, encryptedVault } = await Vault.createFromMnemonic(
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
'password',
'solana'
);
// Load encrypted vault
const vault = await Vault.load('password', encryptedVault);Account Management
// Add new HD account (EVM)
const newAccount = await vault.addNewHDAccount();
// Add new Solana account
const newSolanaAccount = await vault.addNewSolanaAccount();
// Import account from private key
const importedAccount = await vault.importAccount('0x123...');
// Get all accounts
const accounts = vault.getAccounts();
// Returns: [
// { address: '0x123...', type: 'HD', name: 'Account 1' },
// { address: 'ABC123...', type: 'Solana', name: 'Solana 1' },
// { address: '0x456...', type: 'Simple', name: 'Imported 1' }
// ]Blockchain Operations
Native Token Operations
// Get native balance (works for both EVM and Solana)
const ethBalance = await vault.getNativeBalance('0x123...', '1'); // Ethereum
const solBalance = await vault.getNativeBalance('ABC123...', 'solana'); // Solana
// Send native tokens
const tx = await vault.sendNativeToken(
'0x123...', // from address
'0x456...', // to address
'0.1', // amount
'1' // chain ID (Ethereum)
);
const solTx = await vault.sendNativeToken(
'ABC123...', // from address
'DEF456...', // to address
'0.5', // amount in SOL
'solana' // chain ID (Solana)
);Token Operations
// Get token info
const tokenInfo = await vault.getTokenInfo('0x123...', '1'); // ERC-20
const splTokenInfo = await vault.getTokenInfo('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'solana'); // SPL
// Get token balance
const balance = await vault.getTokenBalance('0x123...', '0x456...', '1');
const splBalance = await vault.getTokenBalance('ABC123...', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'solana');
// Send tokens
const tokenTx = await vault.sendToken(
'0x123...', // from address
'0x456...', // to address
'100', // amount
'0x789...', // token contract address
'1' // chain ID
);
const splTx = await vault.sendToken(
'ABC123...', // from address
'DEF456...', // to address
'50', // amount
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // SPL token address
'solana' // chain ID
);Vanity Wallet Generation
EVM Vanity Wallets
// Generate vanity wallet with prefix
const { vault, encryptedVault, attempts, foundAddress } = await Vault.generateVanityHDWallet(
'aaa', // prefix (without 0x)
'password',
'evm',
1000000, // max attempts
false, // case sensitive
(attempts, address) => {
console.log(`Attempt ${attempts}: ${address}`);
}
);
console.log(`Found address: ${foundAddress} after ${attempts} attempts`);Solana Vanity Wallets
// Generate Solana vanity wallet
const { vault, encryptedVault, attempts, foundAddress } = await Vault.generateVanityHDWallet(
'abc', // prefix
'password',
'solana',
1000000, // max attempts
false, // case sensitive
(attempts, address) => {
console.log(`Attempt ${attempts}: ${address}`);
}
);
console.log(`Found Solana address: ${foundAddress} after ${attempts} attempts`);Security Operations
Export Mnemonic (Password Protected)
// Export mnemonic with password verification
const mnemonic = await vault.exportMnemonic('password');
console.log('Mnemonic:', mnemonic);Get Private Key
// Get private key for specific address (vault must be unlocked)
const privateKey = await vault.getPrivateKeyFor('0x123...');
console.log('Private key:', privateKey);๐ Supported Chains
EVM Chains
- Ethereum (
1) - Mainnet - BNB Smart Chain (
56) - Mainnet - Polygon (
137) - Mainnet - Arbitrum One (
42161) - Mainnet - OP Mainnet (
10) - Mainnet - Base (
8453) - Mainnet
Solana Chains
- Solana (
solana) - Mainnet - Solana Devnet (
solana-devnet) - Devnet
๐ Security Features
Encryption & Storage
- PBKDF2 encryption with configurable iterations
- AES-256-GCM for secure data encryption
- Salt generation for each encryption operation
- Memory protection with secure key clearing
Rate Limiting & Protection
- Export mnemonic rate limiting (5 attempts per 30 seconds)
- Audit logging for security-sensitive operations
- Vault identification for tracking
- Memory protection against timing attacks
Best Practices
- Password verification for sensitive operations
- Private key isolation in memory
- Secure random generation for mnemonics
- Input validation and sanitization
๐ฑ React Native Integration
Secure Storage Setup
import * as Keychain from 'react-native-keychain';
import { Vault } from 'pwc-wallet-sdk';
class WalletManager {
static async saveVault(encryptedVault: any, password: string): Promise<void> {
try {
await Keychain.setInternetCredentials(
'pwc-wallet-vault',
'user',
JSON.stringify(encryptedVault)
);
} catch (error) {
console.error('Failed to save vault:', error);
throw error;
}
}
static async loadVault(password: string): Promise<Vault> {
try {
const credentials = await Keychain.getInternetCredentials('pwc-wallet-vault');
if (!credentials || !credentials.password) {
throw new Error('No vault found');
}
const encryptedVault = JSON.parse(credentials.password);
return await Vault.load(password, encryptedVault);
} catch (error) {
console.error('Failed to load vault:', error);
throw error;
}
}
static async deleteVault(): Promise<void> {
try {
await Keychain.resetInternetCredentials('pwc-wallet-vault');
} catch (error) {
console.error('Failed to delete vault:', error);
throw error;
}
}
}React Native Component Example
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import { Vault } from 'pwc-wallet-sdk';
import { WalletManager } from './WalletManager';
const WalletScreen: React.FC = () => {
const [vault, setVault] = useState<Vault | null>(null);
const [accounts, setAccounts] = useState<any[]>([]);
const createWallet = async () => {
try {
const { vault: newVault, encryptedVault } = await Vault.createNew('my-password');
await WalletManager.saveVault(encryptedVault, 'my-password');
setVault(newVault);
setAccounts(newVault.getAccounts());
} catch (error) {
Alert.alert('Error', 'Failed to create wallet');
}
};
const loadWallet = async () => {
try {
const loadedVault = await WalletManager.loadVault('my-password');
setVault(loadedVault);
setAccounts(loadedVault.getAccounts());
} catch (error) {
Alert.alert('Error', 'Failed to load wallet');
}
};
const addSolanaAccount = async () => {
if (!vault) return;
try {
const newAccount = await vault.addNewSolanaAccount();
setAccounts(vault.getAccounts());
} catch (error) {
Alert.alert('Error', 'Failed to add Solana account');
}
};
return (
<View>
<Text>Wallet Accounts:</Text>
{accounts.map((account, index) => (
<Text key={index}>
{account.name}: {account.address} ({account.type})
</Text>
))}
<Button title="Create Wallet" onPress={createWallet} />
<Button title="Load Wallet" onPress={loadWallet} />
<Button title="Add Solana Account" onPress={addSolanaAccount} />
</View>
);
};๐งช Testing
# Run all tests
npm test
# Run Solana-specific tests
npm test -- solana.test.ts
# Run with coverage
npm run test:coverage๐ฆ Build & Publish
# Build the package
npm run build
# Run tests
npm test
# Publish to npm
npm publish๐ง Configuration
Vanity Wallet Settings
import { VANITY_WALLET_CONFIG } from 'pwc-wallet-sdk';
// Default configuration
console.log(VANITY_WALLET_CONFIG);
// {
// DEFAULT_PREFIX: 'aaa',
// DEFAULT_MAX_ATTEMPTS: 1000000,
// DEFAULT_CASE_SENSITIVE: false,
// PROGRESS_UPDATE_INTERVAL: 1000,
// NON_BLOCKING_INTERVAL: 10000,
// MAX_PREFIX_LENGTH: 10,
// MAX_ATTEMPTS_LIMIT: 10000000
// }๐จ Error Handling
try {
const vault = await Vault.load('wrong-password', encryptedVault);
} catch (error) {
if (error.message.includes('Invalid password')) {
console.log('Wrong password provided');
} else if (error.message.includes('Corrupted vault')) {
console.log('Vault data is corrupted');
}
}
try {
const mnemonic = await vault.exportMnemonic('password');
} catch (error) {
if (error.message.includes('Rate limit exceeded')) {
console.log('Too many export attempts');
} else if (error.message.includes('Invalid password')) {
console.log('Wrong password');
}
}๐ License
MIT License - see LICENSE file for details.
๐ค Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
๐ Support
For support and questions:
- Create an issue on GitHub
- Check the documentation
- Review the test files for usage examples