Integração Web3
Princípio Central: Mantenha Objetos Web3 Fora do Redux
Arquitetura Recomendada
Web3 Context (Fora do 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);
};
// Listen to account changes
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
Sincronizando Context com Redux
Ciclo de Vida da Transação
Enviando Transações
Uso em Componentes
Ouvindo Eventos On-Chain
RTK Query com Dados Web3
Estratégias de Invalidação de Cache
Ao Mudar de Rede
Ao Mudar de Conta
Após Confirmação da Transação
Boas Práticas
1. Separar Responsabilidades
2. Tratar Estados de Transação
3. Atualizações Otimistas com Rollback
4. Limpeza de Listeners de Evento
Próximos Passos
Atualizado