# Savings Feature Usage Examples

This document provides practical examples for implementing the savings feature in different types of wallet applications.

## Table of Contents

1. [Browser Extension Example](#browser-extension-example)
2. [React Native Mobile App Example](#react-native-mobile-app-example)
3. [Common Operations](#common-operations)
4. [Error Handling](#error-handling)
5. [Testing](#testing)

## Browser Extension Example

Complete implementation for a Chrome extension using stateless operations.

### Background Script (background.js)

```typescript
import { SavingsOperations } from '@wallet/utils/savings/savings-operations';
import { JsonRpcProvider } from 'ethers';

// Stateless operations - no mnemonic stored
const operations = new SavingsOperations();

// Session management
class ExtensionWalletSession {
  private password: string | null = null;
  private lockTimeout?: NodeJS.Timeout;
  private readonly AUTO_LOCK_DELAY = 15 * 60 * 1000; // 15 minutes

  async unlock(password: string): Promise<boolean> {
    try {
      // Verify password by attempting to decrypt
      await this.decryptMnemonic(password);

      // Store password for session
      this.password = password;

      // Set auto-lock timer
      this.resetLockTimer();

      return true;
    } catch (error) {
      return false;
    }
  }

  lock(): void {
    this.password = null;
    if (this.lockTimeout) {
      clearTimeout(this.lockTimeout);
    }
  }

  isUnlocked(): boolean {
    return this.password !== null;
  }

  private resetLockTimer(): void {
    if (this.lockTimeout) {
      clearTimeout(this.lockTimeout);
    }

    this.lockTimeout = setTimeout(() => {
      this.lock();
      // Notify popup that wallet is locked
      chrome.runtime.sendMessage({ type: 'WALLET_LOCKED' });
    }, this.AUTO_LOCK_DELAY);
  }

  private async decryptMnemonic(password: string): Promise<string> {
    const { encryptedMnemonic } = await chrome.storage.local.get('encryptedMnemonic');

    if (!encryptedMnemonic) {
      throw new Error('No wallet found');
    }

    // Decrypt using Web Crypto API
    const key = await this.deriveKey(password);
    const decrypted = await this.decrypt(encryptedMnemonic, key);

    return decrypted;
  }

  private async deriveKey(password: string): Promise<CryptoKey> {
    const encoder = new TextEncoder();
    const passwordBuffer = encoder.encode(password);

    // Import password as key material
    const keyMaterial = await crypto.subtle.importKey(
      'raw',
      passwordBuffer,
      'PBKDF2',
      false,
      ['deriveKey']
    );

    // Get salt from storage (generated during wallet creation)
    const { salt } = await chrome.storage.local.get('salt');

    // Derive encryption key using PBKDF2
    return await crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: new Uint8Array(salt),
        iterations: 100000,
        hash: 'SHA-256'
      },
      keyMaterial,
      { name: 'AES-GCM', length: 256 },
      false,
      ['decrypt']
    );
  }

  private async decrypt(encryptedData: ArrayBuffer, key: CryptoKey): Promise<string> {
    const decoder = new TextDecoder();
    const decrypted = await crypto.subtle.decrypt(
      { name: 'AES-GCM', iv: new Uint8Array(encryptedData.slice(0, 12)) },
      key,
      encryptedData.slice(12)
    );

    return decoder.decode(decrypted);
  }

  async executeOperation<T>(
    operation: (mnemonic: string) => Promise<T>
  ): Promise<T> {
    if (!this.password) {
      throw new Error('Wallet locked');
    }

    // Reset timeout on each operation
    this.resetLockTimer();

    // Get mnemonic for this operation only
    const mnemonic = await this.decryptMnemonic(this.password);

    try {
      return await operation(mnemonic);
    } finally {
      // Mnemonic goes out of scope and will be garbage collected
    }
  }
}

const session = new ExtensionWalletSession();

// Message handlers
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  (async () => {
    try {
      switch (message.type) {
        case 'UNLOCK_WALLET':
          const unlocked = await session.unlock(message.password);
          sendResponse({ success: unlocked });
          break;

        case 'LOCK_WALLET':
          session.lock();
          sendResponse({ success: true });
          break;

        case 'GET_POCKET_BALANCE':
          const balance = await session.executeOperation(async (mnemonic) => {
            const provider = new JsonRpcProvider(message.rpcUrl);
            return await operations.getPocketBalance(
              mnemonic,
              {
                accountIndex: message.pocketIndex,
                walletIndex: 0
              },
              provider
            );
          });
          sendResponse({ success: true, balance });
          break;

        case 'TRANSFER_FROM_POCKET':
          const result = await session.executeOperation(async (mnemonic) => {
            const provider = new JsonRpcProvider(message.rpcUrl);
            return await operations.transferFromPocket(
              mnemonic,
              {
                accountIndex: message.pocketIndex,
                walletIndex: 0,
                to: message.to,
                amount: BigInt(message.amount)
              },
              provider,
              message.chain
            );
          });
          sendResponse({ success: true, result });
          break;

        case 'GET_POCKET_ADDRESS':
          const address = await session.executeOperation(async (mnemonic) => {
            return operations.getPocketAddress(mnemonic, message.pocketIndex, 0);
          });
          sendResponse({ success: true, address });
          break;

        default:
          sendResponse({ success: false, error: 'Unknown message type' });
      }
    } catch (error) {
      sendResponse({ success: false, error: error.message });
    }
  })();

  return true; // Keep channel open for async response
});
```

### Popup Script (popup.tsx)

```typescript
import React, { useState, useEffect } from 'react';
import { formatUnits } from 'ethers';

function PocketView() {
  const [pockets, setPockets] = useState<Array<{ index: number; balance: string }>>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    loadPockets();
  }, []);

  async function loadPockets() {
    setLoading(true);
    try {
      // Load balances for pockets 0-4
      const promises = [0, 1, 2, 3, 4].map(async (index) => {
        const response = await chrome.runtime.sendMessage({
          type: 'GET_POCKET_BALANCE',
          pocketIndex: index,
          rpcUrl: 'https://eth.llamarpc.com'
        });

        if (response.success) {
          return {
            index,
            balance: formatUnits(response.balance, 18)
          };
        }
        return null;
      });

      const results = await Promise.all(promises);
      setPockets(results.filter(p => p !== null && parseFloat(p.balance) > 0));
    } catch (error) {
      console.error('Failed to load pockets:', error);
    } finally {
      setLoading(false);
    }
  }

  async function transferFromPocket(pocketIndex: number, to: string, amount: string) {
    setLoading(true);
    try {
      const response = await chrome.runtime.sendMessage({
        type: 'TRANSFER_FROM_POCKET',
        pocketIndex,
        to,
        amount: parseUnits(amount, 18).toString(),
        rpcUrl: 'https://eth.llamarpc.com',
        chain: {
          chainId: 1,
          name: 'Ethereum',
          rpcUrl: 'https://eth.llamarpc.com'
        }
      });

      if (response.success) {
        alert(`Transaction sent: ${response.result.hash}`);
        loadPockets(); // Reload balances
      } else {
        alert(`Error: ${response.error}`);
      }
    } catch (error) {
      alert(`Error: ${error.message}`);
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="pocket-view">
      <h2>Savings Pockets</h2>

      {loading && <p>Loading...</p>}

      <div className="pockets-list">
        {pockets.map(pocket => (
          <div key={pocket.index} className="pocket-item">
            <h3>Pocket {pocket.index}</h3>
            <p>Balance: {pocket.balance} ETH</p>
            <button onClick={() => {
              const to = prompt('Recipient address:');
              const amount = prompt('Amount (ETH):');
              if (to && amount) {
                transferFromPocket(pocket.index, to, amount);
              }
            }}>
              Withdraw
            </button>
          </div>
        ))}
      </div>
    </div>
  );
}

export default PocketView;
```

## React Native Mobile App Example

Complete implementation for a mobile app using stateful manager.

### Wallet Service

```typescript
import { SavingsManager } from '@wallet/utils/savings/saving-manager';
import { createWalletClient, createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
import * as Keychain from 'react-native-keychain';
import TouchID from 'react-native-touch-id';
import { AppState, AppStateStatus } from 'react-native';

class WalletService {
  private manager: SavingsManager | null = null;
  private lockTimer?: NodeJS.Timeout;
  private readonly AUTO_LOCK_DELAY = 5 * 60 * 1000; // 5 minutes

  constructor() {
    // Listen for app state changes
    AppState.addEventListener('change', this.handleAppStateChange);
  }

  /**
   * Initialize wallet with biometric authentication
   */
  async initialize(): Promise<boolean> {
    try {
      // Require biometric authentication
      await TouchID.authenticate('Unlock wallet', {
        title: 'Authentication Required',
        fallbackLabel: 'Use Passcode'
      });

      // Get mnemonic from secure storage
      const credentials = await Keychain.getGenericPassword({
        service: 'com.myapp.wallet'
      });

      if (!credentials) {
        throw new Error('Wallet not found');
      }

      // Create manager
      this.manager = new SavingsManager(
        credentials.password,
        {
          chainId: 1,
          name: 'Ethereum',
          rpcUrl: 'https://eth.llamarpc.com'
        },
        0 // wallet index
      );

      // Set auto-lock timer
      this.resetLockTimer();

      return true;
    } catch (error) {
      console.error('Failed to initialize wallet:', error);
      return false;
    }
  }

  /**
   * Lock wallet and clear sensitive data
   */
  lock(): void {
    if (this.manager) {
      this.manager.dispose();
      this.manager = null;
    }

    if (this.lockTimer) {
      clearTimeout(this.lockTimer);
    }
  }

  isUnlocked(): boolean {
    return this.manager !== null;
  }

  private resetLockTimer(): void {
    if (this.lockTimer) {
      clearTimeout(this.lockTimer);
    }

    this.lockTimer = setTimeout(() => {
      this.lock();
    }, this.AUTO_LOCK_DELAY);
  }

  private handleAppStateChange = (nextAppState: AppStateStatus) => {
    if (nextAppState === 'background' || nextAppState === 'inactive') {
      // Lock immediately when app backgrounds
      this.lock();
    }
  };

  /**
   * Get manager instance (throws if locked)
   */
  getManager(): SavingsManager {
    if (!this.manager) {
      throw new Error('Wallet locked - please authenticate');
    }

    // Reset auto-lock timer on each access
    this.resetLockTimer();

    return this.manager;
  }

  /**
   * Create a new savings pocket
   */
  async createPocket(pocketIndex: number): Promise<string> {
    const manager = this.getManager();
    const pocket = manager.getPocket(pocketIndex);
    return pocket.address;
  }

  /**
   * Get pocket balance
   */
  async getPocketBalance(pocketIndex: number, tokens: string[]): Promise<any> {
    const manager = this.getManager();
    return await manager.getPocketTokenBalance(tokens, pocketIndex);
  }

  /**
   * Transfer to pocket
   */
  async transferToPocket(
    walletClient: any,
    pocketIndex: number,
    amount: string
  ): Promise<any> {
    const manager = this.getManager();
    return await manager.transferToPocket(walletClient, pocketIndex, amount);
  }

  /**
   * Transfer from pocket back to main wallet
   */
  async transferFromPocket(
    pocketIndex: number,
    amount: number,
    token: string = 'native'
  ): Promise<any> {
    const manager = this.getManager();
    return await manager.sendToMainWallet(pocketIndex, amount, token as any);
  }
}

export const walletService = new WalletService();
```

### React Component

```typescript
import React, { useState, useEffect } from 'react';
import { View, Text, Button, FlatList, Alert } from 'react-native';
import { walletService } from './WalletService';

function PocketsScreen() {
  const [pockets, setPockets] = useState<Array<{ index: number; address: string; balance: any }>>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    loadPockets();
  }, []);

  async function loadPockets() {
    setLoading(true);
    try {
      // Check if wallet is unlocked
      if (!walletService.isUnlocked()) {
        const unlocked = await walletService.initialize();
        if (!unlocked) {
          Alert.alert('Error', 'Failed to unlock wallet');
          return;
        }
      }

      // Load pockets 0-4
      const pocketData = await Promise.all(
        [0, 1, 2, 3, 4].map(async (index) => {
          try {
            const address = await walletService.createPocket(index);
            const balance = await walletService.getPocketBalance(index, []);

            return { index, address, balance };
          } catch (error) {
            console.error(`Failed to load pocket ${index}:`, error);
            return null;
          }
        })
      );

      setPockets(pocketData.filter(p => p !== null));
    } catch (error) {
      Alert.alert('Error', error.message);
    } finally {
      setLoading(false);
    }
  }

  async function handleWithdraw(pocketIndex: number) {
    Alert.prompt(
      'Withdraw from Pocket',
      'Enter amount (ETH):',
      async (amount) => {
        try {
          setLoading(true);

          const result = await walletService.transferFromPocket(
            pocketIndex,
            parseFloat(amount) * 1e18, // Convert to wei
            'native'
          );

          Alert.alert('Success', `Transaction: ${result.hash}`);
          loadPockets(); // Reload
        } catch (error) {
          Alert.alert('Error', error.message);
        } finally {
          setLoading(false);
        }
      }
    );
  }

  return (
    <View style={{ padding: 20 }}>
      <Text style={{ fontSize: 24, fontWeight: 'bold' }}>Savings Pockets</Text>

      {loading && <Text>Loading...</Text>}

      <FlatList
        data={pockets}
        keyExtractor={(item) => item.index.toString()}
        renderItem={({ item }) => (
          <View style={{ padding: 15, borderWidth: 1, marginVertical: 5 }}>
            <Text style={{ fontWeight: 'bold' }}>Pocket {item.index}</Text>
            <Text>Address: {item.address.slice(0, 10)}...</Text>
            <Text>
              Balance: {item.balance[0]?.balance.formatted || '0'} ETH
            </Text>
            <Button title="Withdraw" onPress={() => handleWithdraw(item.index)} />
          </View>
        )}
      />

      <Button title="Refresh" onPress={loadPockets} />
    </View>
  );
}

export default PocketsScreen;
```

## Common Operations

### Creating a Wallet

```typescript
import { SavingsManager, SavingsOperations } from '@wallet/utils/savings';
import { generateMnemonic } from 'bip39';

// Generate new mnemonic
const mnemonic = generateMnemonic();

// Encrypt and store securely (implementation depends on platform)
await secureStorage.storeMnemonic(mnemonic, password);

// Option 1: Stateful manager
const manager = new SavingsManager(mnemonic, chainConfig);

// Option 2: Stateless operations
const operations = new SavingsOperations();
```

### Getting Pocket Address

```typescript
// Stateless
const address = operations.getPocketAddress(mnemonic, 1, 0);
console.log(`Pocket 1 address: ${address}`);

// Stateful
const pocket = manager.getPocket(1);
console.log(`Pocket 1 address: ${pocket.address}`);
```

### Checking Pocket Balance

```typescript
import { formatUnits } from 'ethers';

// Stateless
const balance = await operations.getPocketBalance(
  mnemonic,
  { accountIndex: 1, walletIndex: 0 },
  provider
);
console.log(`Balance: ${formatUnits(balance, 18)} ETH`);

// Stateful
const balances = await manager.getPocketTokenBalance(
  ['0xTokenAddress'], // Token addresses to check
  1 // Pocket index
);
console.log(`Native: ${balances[0].balance.formatted}`);
```

### Transferring to Pocket

```typescript
import { parseUnits } from 'ethers';
import { createWalletClient, http } from 'viem';
import { mainnet } from 'viem/chains';

// Create wallet client for main wallet
const walletClient = createWalletClient({
  account: mainAccount,
  chain: mainnet,
  transport: http()
});

// Transfer to pocket (stateful only)
const result = await manager.transferToPocket(
  walletClient,
  1, // Pocket index
  '0.1' // Amount as string
);

console.log(`Transaction: ${result.hash}`);
```

### Transferring from Pocket

```typescript
// Stateless
const result = await operations.transferFromPocket(
  mnemonic,
  {
    accountIndex: 1,
    walletIndex: 0,
    to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
    amount: parseUnits('0.1', 18)
  },
  provider,
  chain
);

// Stateful
const result = await manager.sendToMainWallet(
  1, // Pocket index
  100000000000000000, // Amount in wei
  'native' // or token address
);
```

### Transferring Tokens from Pocket

```typescript
// Stateless
const result = await operations.transferTokenFromPocket(
  mnemonic,
  {
    accountIndex: 1,
    walletIndex: 0,
    tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
    to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
    amount: parseUnits('100', 6) // 100 USDC (6 decimals)
  },
  provider,
  chain
);

// Stateful
const result = await manager.sendToMainWallet(
  1, // Pocket index
  100000000, // 100 USDC in base units
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // Token address
);
```

## Error Handling

### Input Validation Errors

```typescript
import { SavingsValidation } from '@wallet/utils/savings/validation';

try {
  // This will throw if invalid
  SavingsValidation.validateAddress('invalid', 'Recipient');
  SavingsValidation.validateAmount(0n, 'Transfer amount');
} catch (error) {
  // Handle validation error
  console.error('Validation failed:', error.message);
  // Show user-friendly message
  alert(`Invalid input: ${error.message}`);
}
```

### Transaction Errors

```typescript
try {
  const result = await operations.transferFromPocket(
    mnemonic,
    options,
    provider,
    chain
  );

  if (!result.status) {
    throw new Error('Transaction failed');
  }

  console.log('Success:', result.hash);
} catch (error) {
  if (error.message.includes('insufficient funds')) {
    alert('Insufficient balance in pocket');
  } else if (error.message.includes('user rejected')) {
    alert('Transaction cancelled');
  } else {
    alert(`Transaction failed: ${error.message}`);
  }
}
```

### Authentication Errors

```typescript
try {
  await walletService.initialize();
} catch (error) {
  if (error.message.includes('authentication failed')) {
    alert('Biometric authentication failed. Please try again.');
  } else if (error.message.includes('not found')) {
    // No wallet exists - show onboarding
    navigation.navigate('CreateWallet');
  } else {
    alert(`Error: ${error.message}`);
  }
}
```

## Testing

### Unit Tests

```typescript
import { SavingsOperations } from '@wallet/utils/savings/savings-operations';
import { SavingsValidation } from '@wallet/utils/savings/validation';

describe('Savings Operations', () => {
  const mnemonic = 'test mnemonic phrase...';
  const operations = new SavingsOperations();

  it('should derive consistent addresses', () => {
    const address1 = operations.getPocketAddress(mnemonic, 0, 0);
    const address2 = operations.getPocketAddress(mnemonic, 0, 0);

    expect(address1).toBe(address2);
    expect(address1).toMatch(/^0x[a-fA-F0-9]{40}$/);
  });

  it('should validate addresses', () => {
    expect(() => {
      SavingsValidation.validateAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
    }).not.toThrow();

    expect(() => {
      SavingsValidation.validateAddress('invalid');
    }).toThrow();
  });

  it('should validate amounts', () => {
    expect(() => {
      SavingsValidation.validateAmount(1000n);
    }).not.toThrow();

    expect(() => {
      SavingsValidation.validateAmount(0n);
    }).toThrow('must be positive');
  });
});
```

### Integration Tests

```typescript
import { SavingsManager } from '@wallet/utils/savings/saving-manager';
import { JsonRpcProvider } from 'ethers';

describe('Savings Manager Integration', () => {
  let manager: SavingsManager;
  const testMnemonic = 'test test test test test test test test test test test junk';

  beforeEach(() => {
    manager = new SavingsManager(
      testMnemonic,
      {
        chainId: 1,
        name: 'Ethereum',
        rpcUrl: 'https://eth.llamarpc.com'
      },
      0
    );
  });

  afterEach(() => {
    manager.dispose();
  });

  it('should create pockets with unique addresses', () => {
    const pocket0 = manager.getPocket(0);
    const pocket1 = manager.getPocket(1);

    expect(pocket0.address).not.toBe(pocket1.address);
    expect(pocket0.derivationPath).not.toBe(pocket1.derivationPath);
  });

  it('should verify pocket addresses', () => {
    const pocket = manager.getPocket(0);
    const isValid = manager.verifyPocketAddress(0, pocket.address);

    expect(isValid).toBe(true);
  });

  it('should clear sensitive data on dispose', () => {
    manager.dispose();

    // Should throw after disposal
    expect(() => manager.getPocket(0)).toThrow();
  });
});
```

## Best Practices Summary

1. **Use Stateless for Extensions**: Browser extensions benefit from stateless operations due to background script lifecycle.

2. **Use Stateful for Mobile**: Mobile apps can safely use stateful managers with proper lifecycle management.

3. **Always Authenticate**: Require biometric/password authentication before sensitive operations.

4. **Handle App Lifecycle**: Clear sensitive data when app backgrounds or closes.

5. **Validate Inputs**: Use provided validation utilities before operations.

6. **Handle Errors Gracefully**: Provide user-friendly error messages.

7. **Test Thoroughly**: Write unit and integration tests for security features.

8. **Educate Users**: Provide clear instructions and warnings about security.

For more information, see [SECURITY.md](./SECURITY.md) for complete security guidance.
