Integración Web3
Principio central: Mantener los objetos Web3 fuera de Redux
Arquitectura recomendada
Web3 Context (fuera de Redux)
// src/contexts/Web3Context.tsx
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { ethers } from 'ethers';
interface Web3ContextValue {
provider: ethers.providers.Web3Provider | null;
signer: ethers.Signer | null;
account: string | null;
chainId: number | null;
connect: () => Promise<void>;
disconnect: () => void;
}
const Web3Context = createContext<Web3ContextValue | undefined>(undefined);
export function Web3Provider({ children }: { children: ReactNode }) {
const [provider, setProvider] = useState<ethers.providers.Web3Provider | null>(null);
const [signer, setSigner] = useState<ethers.Signer | null>(null);
const [account, setAccount] = useState<string | null>(null);
const [chainId, setChainId] = useState<number | null>(null);
const connect = async () => {
if (!window.ethereum) {
throw new Error('MetaMask not installed');
}
const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const accounts = await web3Provider.send('eth_requestAccounts', []);
const network = await web3Provider.getNetwork();
const signer = web3Provider.getSigner();
setProvider(web3Provider);
setSigner(signer);
setAccount(accounts[0]);
setChainId(network.chainId);
};
const disconnect = () => {
setProvider(null);
setSigner(null);
setAccount(null);
setChainId(null);
};
// Escuchar cambios de cuenta
useEffect(() => {
if (!window.ethereum) return;
const handleAccountsChanged = (accounts: string[]) => {
if (accounts.length === 0) {
disconnect();
} else {
setAccount(accounts[0]);
}
};
const handleChainChanged = (chainIdHex: string) => {
const newChainId = parseInt(chainIdHex, 16);
setChainId(newChainId);
};
window.ethereum.on('accountsChanged', handleAccountsChanged);
window.ethereum.on('chainChanged', handleChainChanged);
return () => {
window.ethereum?.removeListener('accountsChanged', handleAccountsChanged);
window.ethereum?.removeListener('chainChanged', handleChainChanged);
};
}, []);
return (
<Web3Context.Provider
value={{ provider, signer, account, chainId, connect, disconnect }}
>
{children}
</Web3Context.Provider>
);
}
export function useWeb3() {
const context = useContext(Web3Context);
if (!context) {
throw new Error('useWeb3 must be used within Web3Provider');
}
return context;
}Redux Slice for Web3 State
Sincronizar Context con Redux
Ciclo de vida de la transacción
Envío de transacciones
Uso en componentes
Escuchar eventos on-chain
RTK Query con datos Web3
Estrategias de invalidación de caché
Al cambiar de red
Al cambiar de cuenta
Después de la confirmación de la transacción
Buenas prácticas
1. Separar responsabilidades
2. Manejar estados de transacción
3. Actualizaciones optimistas con rollback
4. Limpieza de listeners de eventos
Siguientes pasos
Última actualización