auto-commit for e33c1421-b92f-4df2-9ea6-cf98a1048634

This commit is contained in:
emergent-agent-e1
2025-11-08 07:17:53 +00:00
parent 83a7942124
commit b5b9545533
+413
View File
@@ -0,0 +1,413 @@
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { Platform, Alert } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';
import { getCurrentEndpoint, WALLET_ERRORS } from '../lib/wallet';
// Platform-aware Polkadot.js import
// Only import on native platforms (not web) to avoid import.meta issues
let ApiPromise: any = null;
let WsProvider: any = null;
let Keyring: any = null;
let web3Accounts: any = null;
let web3Enable: any = null;
let web3FromAddress: any = null;
let InjectedAccountWithMeta: any = null;
if (Platform.OS !== 'web') {
try {
const polkadotApi = require('@polkadot/api');
const polkadotKeyring = require('@polkadot/keyring');
ApiPromise = polkadotApi.ApiPromise;
WsProvider = polkadotApi.WsProvider;
Keyring = polkadotKeyring.Keyring;
console.log('✅ Polkadot.js loaded for native platform');
} catch (error) {
console.warn('⚠️ Polkadot.js not available:', error);
}
}
// ========================================
// TYPE DEFINITIONS
// ========================================
export interface MobileAccount {
address: string;
name?: string;
source?: 'local' | 'extension';
meta?: {
name?: string;
source?: string;
};
}
interface PolkadotContextType {
api: any | null;
isApiReady: boolean;
accounts: MobileAccount[];
selectedAccount: MobileAccount | null;
setSelectedAccount: (account: MobileAccount | null) => void;
connectWallet: () => Promise<void>;
disconnectWallet: () => void;
createWallet: (name: string, password: string) => Promise<{ success: boolean; error?: string; mnemonic?: string }>;
importWallet: (mnemonic: string, name: string, password: string) => Promise<{ success: boolean; error?: string }>;
error: string | null;
isLoading: boolean;
}
const PolkadotContext = createContext<PolkadotContextType | undefined>(undefined);
// ========================================
// PROVIDER COMPONENT
// ========================================
interface PolkadotProviderProps {
children: ReactNode;
endpoint?: string;
}
export const PolkadotProvider: React.FC<PolkadotProviderProps> = ({
children,
endpoint,
}) => {
const [api, setApi] = useState<any | null>(null);
const [isApiReady, setIsApiReady] = useState(false);
const [accounts, setAccounts] = useState<MobileAccount[]>([]);
const [selectedAccount, setSelectedAccount] = useState<MobileAccount | null>(null);
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const rpcEndpoint = endpoint || getCurrentEndpoint();
// ========================================
// API INITIALIZATION
// ========================================
useEffect(() => {
if (Platform.OS === 'web') {
console.log('📱 Web platform detected - using mock data');
setIsApiReady(true);
return;
}
const initApi = async () => {
try {
if (!ApiPromise || !WsProvider) {
console.warn('⚠️ Polkadot.js not available, using mock mode');
setIsApiReady(true);
return;
}
console.log('🔗 Connecting to PezkuwiChain:', rpcEndpoint);
const provider = new WsProvider(rpcEndpoint);
const apiInstance = await ApiPromise.create({ provider });
await apiInstance.isReady;
setApi(apiInstance);
setIsApiReady(true);
setError(null);
console.log('✅ Connected to PezkuwiChain');
// Get chain info
const [chain, nodeName, nodeVersion] = await Promise.all([
apiInstance.rpc.system.chain(),
apiInstance.rpc.system.name(),
apiInstance.rpc.system.version(),
]);
console.log(`📡 Chain: ${chain}`);
console.log(`🖥️ Node: ${nodeName} v${nodeVersion}`);
} catch (err: any) {
console.error('❌ Failed to connect to node:', err);
setError(`Failed to connect to node: ${rpcEndpoint}`);
setIsApiReady(false);
}
};
initApi();
return () => {
if (api) {
api.disconnect();
}
};
}, [rpcEndpoint]);
// ========================================
// LOAD SAVED ACCOUNTS
// ========================================
useEffect(() => {
loadAccounts();
}, []);
const loadAccounts = async () => {
try {
const savedAccounts = await AsyncStorage.getItem('pezkuwi_accounts');
if (savedAccounts) {
const parsedAccounts = JSON.parse(savedAccounts);
setAccounts(parsedAccounts);
// Auto-select first account if available
if (parsedAccounts.length > 0 && !selectedAccount) {
setSelectedAccount(parsedAccounts[0]);
}
}
} catch (error) {
console.error('Error loading accounts:', error);
}
};
const saveAccounts = async (accountsList: MobileAccount[]) => {
try {
await AsyncStorage.setItem('pezkuwi_accounts', JSON.stringify(accountsList));
} catch (error) {
console.error('Error saving accounts:', error);
}
};
// ========================================
// CREATE WALLET (NEW ACCOUNT)
// ========================================
const createWallet = async (
name: string,
password: string
): Promise<{ success: boolean; error?: string; mnemonic?: string }> => {
try {
if (Platform.OS === 'web' || !Keyring) {
return {
success: false,
error: 'Wallet creation not available on web platform',
};
}
setIsLoading(true);
// Generate mnemonic
const { mnemonicGenerate } = await import('@polkadot/util-crypto');
const mnemonic = mnemonicGenerate(12);
// Create keyring
const keyring = new Keyring({ type: 'sr25519', ss58Format: 42 });
const pair = keyring.addFromUri(mnemonic);
const newAccount: MobileAccount = {
address: pair.address,
name: name || 'My Wallet',
source: 'local',
meta: {
name: name || 'My Wallet',
source: 'mobile',
},
};
// Save encrypted mnemonic to secure store
await SecureStore.setItemAsync(
`pezkuwi_mnemonic_${pair.address}`,
mnemonic
);
// Save password hash (for future unlocking)
await SecureStore.setItemAsync(
`pezkuwi_password_${pair.address}`,
password // In production, hash this
);
// Add to accounts list
const updatedAccounts = [...accounts, newAccount];
setAccounts(updatedAccounts);
setSelectedAccount(newAccount);
await saveAccounts(updatedAccounts);
setIsLoading(false);
return {
success: true,
mnemonic, // Return mnemonic for user to backup
};
} catch (error: any) {
console.error('Error creating wallet:', error);
setIsLoading(false);
return {
success: false,
error: error.message || 'Failed to create wallet',
};
}
};
// ========================================
// IMPORT WALLET (FROM MNEMONIC)
// ========================================
const importWallet = async (
mnemonic: string,
name: string,
password: string
): Promise<{ success: boolean; error?: string }> => {
try {
if (Platform.OS === 'web' || !Keyring) {
return {
success: false,
error: 'Wallet import not available on web platform',
};
}
setIsLoading(true);
// Validate mnemonic
const { mnemonicValidate } = await import('@polkadot/util-crypto');
if (!mnemonicValidate(mnemonic)) {
setIsLoading(false);
return {
success: false,
error: 'Invalid mnemonic phrase',
};
}
// Create keyring and import account
const keyring = new Keyring({ type: 'sr25519', ss58Format: 42 });
const pair = keyring.addFromUri(mnemonic);
// Check if account already exists
const existingAccount = accounts.find(acc => acc.address === pair.address);
if (existingAccount) {
setIsLoading(false);
return {
success: false,
error: 'This wallet is already imported',
};
}
const newAccount: MobileAccount = {
address: pair.address,
name: name || 'Imported Wallet',
source: 'local',
meta: {
name: name || 'Imported Wallet',
source: 'mobile',
},
};
// Save encrypted mnemonic
await SecureStore.setItemAsync(
`pezkuwi_mnemonic_${pair.address}`,
mnemonic
);
// Save password
await SecureStore.setItemAsync(
`pezkuwi_password_${pair.address}`,
password
);
// Add to accounts
const updatedAccounts = [...accounts, newAccount];
setAccounts(updatedAccounts);
setSelectedAccount(newAccount);
await saveAccounts(updatedAccounts);
setIsLoading(false);
return { success: true };
} catch (error: any) {
console.error('Error importing wallet:', error);
setIsLoading(false);
return {
success: false,
error: error.message || 'Failed to import wallet',
};
}
};
// ========================================
// CONNECT WALLET
// ========================================
const connectWallet = async () => {
try {
setError(null);
if (Platform.OS === 'web') {
Alert.alert(
'Web Platform',
'Please use the mobile app to manage your wallet',
[{ text: 'OK' }]
);
return;
}
// Check if we have any accounts
if (accounts.length === 0) {
Alert.alert(
'No Wallet Found',
'Please create or import a wallet first',
[{ text: 'OK' }]
);
return;
}
// Auto-select first account if none selected
if (!selectedAccount && accounts.length > 0) {
setSelectedAccount(accounts[0]);
}
console.log('✅ Wallet connected');
} catch (err: any) {
console.error('❌ Wallet connection failed:', err);
setError(WALLET_ERRORS.CONNECTION_FAILED);
}
};
// ========================================
// DISCONNECT WALLET
// ========================================
const disconnectWallet = () => {
setSelectedAccount(null);
console.log('🔌 Wallet disconnected');
};
// ========================================
// CONTEXT VALUE
// ========================================
const value: PolkadotContextType = {
api,
isApiReady,
accounts,
selectedAccount,
setSelectedAccount,
connectWallet,
disconnectWallet,
createWallet,
importWallet,
error,
isLoading,
};
return (
<PolkadotContext.Provider value={value}>
{children}
</PolkadotContext.Provider>
);
};
// ========================================
// HOOK
// ========================================
export const usePolkadot = (): PolkadotContextType => {
const context = useContext(PolkadotContext);
if (!context) {
throw new Error('usePolkadot must be used within PolkadotProvider');
}
return context;
};