Il tuo smart contract è deployato, il gas pagato, e ora? Ora arriva il problema vero: farlo usare alle persone. E qui la maggior parte dei progetti muore, perché si scopre che collegare un frontend a una blockchain è un campo minato di promesse non mantenute, stime di gas sbagliate e transazioni che spariscono nel nulla. Noi, di Meteora Web, lo vediamo ogni giorno: sviluppatori capaci che si perdono nei dettagli di Web3.js o Ethers.js e bruciano budget in errori evitabili. Questa guida non è un manuale accademico: è il percorso che seguiamo noi per portare una dApp dalla teoria alla produzione, con codice che funziona e scelte che hanno un perché economico.
Perché scegliere Ethers.js rispetto a Web3.js per la tua dApp?
La prima decisione che uccide i progetti è la scelta della libreria sbagliata. Web3.js è il veterano, il primo a popolare le dApp. Ma ha un peso specifico enorme, una gestione degli errori che lascia a desiderare e una curva di apprendimento che non perdona. Ethers.js, invece, è più leggero, modulare e con una API più pulita. Per noi la scelta è netta: Ethers.js vince quasi sempre, soprattutto se devi gestire più reti o vuoi un bundle più snello per il frontend.
Ma non è solo una questione di gusti. Web3.js ha una storia di problemi con le stime del gas e la gestione delle transazioni su reti congestionate. Ethers.js, con il suo parseEther e formatEther, ti obbliga a gestire i numeri come si deve, evitando errori di conversione che costano cari. Se parti da zero, non guardarti indietro: Ethers.js. Se devi mantenere un progetto legacy in Web3.js, impara a conviverci, ma pianifica la migrazione.
Come si confrontano in termini di performance e peso?
Un bundle Ethers.js minimo pesa circa 80KB gzip, contro i 150KB+ di Web3.js. In un mondo dove ogni kilobyte conta per il SEO e la velocità di caricamento, questa differenza si sente. E non parliamo solo di velocità: un bundle più leggero significa meno costi di hosting e una migliore esperienza utente, che si traduce in più conversioni. Noi ragioniamo sempre in termini di ritorno: un frontend più veloce è un frontend che vende di più.
Come funziona la connessione a una rete blockchain con Ethers.js?
Prima di scrivere codice, capisci il concetto: non ti connetti a "Internet" o a "Ethereum" come a un server. Ti connetti a un nodo, che è un computer che esegue il protocollo e ti risponde alle domande. Il tuo frontend parla con questo nodo tramite JSON-RPC, e la libreria (Ethers.js) traduce le tue chiamate in quel linguaggio. Scegliere il nodo giusto è una decisione di business: usare un provider pubblico come Cloudflare o un servizio a pagamento come Infura o Alchemy cambia affidabilità e costi.
Sponsored Protocol
Per un progetto serio, non usare mai un nodo pubblico gratuito per la produzione. Sono lenti, limitati e possono andare giù nei momenti peggiori. Noi partiamo sempre da un provider gestito, con un piano che scala con il traffico. Ecco come si configura una connessione di base:
import { ethers } from "ethers";
// Connessione a un nodo (es. Infura, Alchemy, o il tuo nodo)
const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID");
// Leggere il blocco corrente
const blockNumber = await provider.getBlockNumber();
console.log("Blocco corrente:", blockNumber);
// Leggere il saldo di un indirizzo
const address = "0x...";
const balance = await provider.getBalance(address);
console.log("Saldo in ETH:", ethers.formatEther(balance));
Questo codice funziona subito. Ma nota una cosa: getBalance restituisce un BigInt, non un numero. Gestire i numeri con i BigInt è la prima regola per non sbagliare i calcoli. Non fare mai operazioni aritmetiche con i float in JavaScript quando parli di wei o gwei.
Quale provider scegliere per la tua infrastruttura?
La scelta del provider è una scelta di affidabilità. Infura e Alchemy sono i più noti, ma ci sono anche opzioni come QuickNode o Drpc. Ognuno ha i suoi prezzi e i suoi limiti. Per un MVP, puoi iniziare con un piano gratuito, ma metti in conto di passare a un piano a pagamento appena il traffico cresce. Il costo di un provider gestito è irrisorio rispetto al costo di un downtime che ti fa perdere clienti e reputazione.
Come leggere dati da uno smart contract con Web3.js o Ethers.js?
Leggere dati da uno smart contract è l'operazione più comune e più semplice. Non serve firmare nulla, non si paga gas: stai solo facendo una domanda al nodo. Ma serve l'ABI, l'Interfaccia Binaria dell'Applicazione, che descrive le funzioni del contratto. Senza ABI, la libreria non sa come interpretare i dati. L'ABI è un JSON che si genera in fase di compilazione con Hardhat o Foundry.
Ecco come si legge il saldo di un token ERC-20:
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID");
// ABI minimale per la funzione balanceOf
const abi = [
"function balanceOf(address owner) view returns (uint256)",
"function symbol() view returns (string)"
];
const tokenAddress = "0x..."; // Indirizzo del token
const contract = new ethers.Contract(tokenAddress, abi, provider);
const holderAddress = "0x...";
const balance = await contract.balanceOf(holderAddress);
console.log("Saldo token:", balance.toString());
const symbol = await contract.symbol();
console.log("Simbolo:", symbol);
Nota come l'ABI definisce la funzione come view: questo dice alla libreria che la chiamata non modifica lo stato e non richiede gas. Sbagliare la definizione dell'ABI porta a errori di decoding che ti fanno perdere ore. Usa sempre l'ABI generata dal compilatore, non quella scritta a mano.
Sponsored Protocol
Come gestire gli errori di decoding e i dati mancanti?
Uno degli errori più comuni è il missing revert data o il call revert exception. Questo succede quando il contratto lancia un'eccezione, magari perché l'indirizzo non è valido o la funzione non esiste. La libreria ti restituisce un errore, ma il messaggio non è sempre chiaro. Il nostro consiglio: avvolgi ogni chiamata in un try/catch e logga l'errore completo, non solo il messaggio. Spesso la risposta è nei dettagli dell'oggetto errore.
Come inviare transazioni che modificano lo stato della blockchain?
Qui le cose si fanno serie. Inviare una transazione significa pagare gas, e il gas si paga in ETH o nel token della rete. Prima di inviare, devi avere un wallet (es. MetaMask) collegato e l'utente deve firmare la transazione. La firma non è un dettaglio: è il consenso esplicito dell'utente a spendere i suoi fondi. Il tuo frontend non deve mai gestire le chiavi private direttamente. Mai. Usa sempre un wallet provider come MetaMask o WalletConnect.
Ecco come si invia una transazione per trasferire ETH:
import { ethers } from "ethers";
// Provider dal wallet del browser (MetaMask)
const provider = new ethers.BrowserProvider(window.ethereum);
// Richiedi accesso al wallet
await provider.send("eth_requestAccounts", []);
// Il signer è l'utente che firma
const signer = await provider.getSigner();
// Invia 0.1 ETH a un indirizzo
const tx = await signer.sendTransaction({
to: "0xdestinatario",
value: ethers.parseEther("0.1")
});
// Aspetta la conferma della transazione
const receipt = await tx.wait();
console.log("Transazione confermata:", receipt.hash);
Questo è il flusso base. Ma nella pratica, devi gestire la stima del gas, il prezzo del gas e i possibili errori di rete. Se non stai attento, puoi incorrere in out of gas o in transazioni che restano in pending per ore. Il gas è il costo di fare business su blockchain: imparare a stimarlo bene ti fa risparmiare soldi e frustrazione.
Sponsored Protocol
Come stimare il gas prima di inviare una transazione?
Usa la funzione estimateGas della libreria. Ti restituisce una stima del gas necessario, ma attenzione: è una stima, non una garanzia. Su reti congestionate, il prezzo del gas può cambiare in pochi secondi. Ecco come si fa:
const gasEstimate = await signer.estimateGas({
to: "0xdestinatario",
value: ethers.parseEther("0.1")
});
console.log("Gas stimato:", gasEstimate.toString());
// Imposta un limite di gas con un margine di sicurezza (es. 20% in più)
const gasLimit = gasEstimate * 120n / 100n;
const tx = await signer.sendTransaction({
to: "0xdestinatario",
value: ethers.parseEther("0.1"),
gasLimit
});
Questo codice ti salva da brutte sorprese. Ma ricorda: il gas limit non è il prezzo del gas. Il prezzo del gas è il maxFeePerGas e maxPriorityFeePerGas su reti EIP-1559. Lascia che la libreria li calcoli da sola, ma se vuoi ottimizzare, studia come funziona la fee market.
Quali errori di sicurezza evitare quando interagisci con la blockchain?
La sicurezza non è un'opzione, è una necessità. Noi, di Meteora Web, abbiamo visto progetti con chiavi private hardcoded nel frontend, endpoint di nodi esposti e contratti con funzioni di amministrazione senza controlli. Il frontend non è il posto per i segreti. Tutto ciò che è sensibile deve stare in un backend, e il frontend deve solo firmare transazioni. Usa sempre wallet come MetaMask, che gestiscono le chiavi in modo sicuro.
Un altro errore comune è non validare gli input dell'utente. Se il tuo form accetta un indirizzo, verifica che sia un indirizzo valido con ethers.isAddress(). Se accetti un importo, assicurati che sia un numero positivo. La blockchain non perdona: una volta che la transazione è inviata, non puoi annullarla. La prevenzione è l'unica strategia.
Come proteggere il tuo frontend da attacchi comuni?
Oltre alla validazione degli input, fai attenzione a:
- Phishing: verifica sempre l'URL del tuo sito e usa HTTPS. Un attaccante può clonare il tuo frontend e rubare le chiavi.
- Reentrancy: se il tuo contratto ha funzioni che chiamano altri contratti, assicurati che sia protetto. Ma questo è lato smart contract, non frontend.
- Intercettazione: non inviare mai dati sensibili su HTTP. Usa sempre HTTPS e, se possibile, WebSocket criptati per le connessioni in tempo reale.
E non dimenticare i backup. Se il tuo backend si rompe, devi poter ripristinare tutto. Noi lo sappiamo bene: la sicurezza nelle PMI italiane è sistematicamente sottovalutata, e nel mondo blockchain è ancora peggio.
Sponsored Protocol
Come gestire le connessioni in tempo reale con WebSocket?
Le dApp non sono solo richieste singole: spesso devi ascoltare eventi, come un cambio di saldo o una nuova transazione. Per questo serve una connessione WebSocket, che mantiene un canale aperto con il nodo. Ethers.js supporta WebSocketProvider, ma attenzione: le connessioni WebSocket sono più fragili delle HTTP. Se la connessione cade, devi riconnetterti e gestire i messaggi persi.
Ecco un esempio di come ascoltare eventi di un contratto:
import { ethers } from "ethers";
const provider = new ethers.WebSocketProvider("wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID");
const abi = [
"event Transfer(address indexed from, address indexed to, uint256 value)"
];
const contractAddress = "0x...";
const contract = new ethers.Contract(contractAddress, abi, provider);
// Ascolta gli eventi Transfer
contract.on("Transfer", (from, to, value, event) => {
console.log("Transfer:", from, to, value.toString());
});
// Chiudi la connessione quando non serve più
// provider.destroy();
Questo codice ti tiene aggiornato in tempo reale. Ma gestisci la riconnessione: se il WebSocket cade, devi riconnetterti e ricominciare ad ascoltare. Un pattern comune è usare un intervallo di riconnessione con backoff esponenziale.
Quali sono i limiti dei WebSocket e come superarli?
I WebSocket hanno un limite di connessioni per IP e possono essere bloccati da alcuni firewall. Inoltre, se il tuo frontend è statico, devi assicurarti che il server WebSocket sia configurato correttamente. Per applicazioni ad alta frequenza, considera l'uso di servizi come Pusher o Socket.io, ma per la blockchain, Ethers.js è sufficiente. La semplicità batte la complessità, finché il volume di dati non richiede altro.
Come testare le tue interazioni con la blockchain prima del deploy?
Testare è l'unico modo per non bruciare soldi veri. Usa una rete di test come Sepolia o Goerli, dove gli ETH sono finti. Ma non basta: devi scrivere test automatici. Noi usiamo Hardhat, che integra un ambiente di test locale e ti permette di simulare transazioni senza costi. Ecco un esempio di test con Hardhat:
Sponsored Protocol
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Token", function () {
it("Should return the correct balance", async function () {
const [owner, addr1] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MyToken");
const token = await Token.deploy("MyToken", "MTK", 18);
await token.waitForDeployment();
const balance = await token.balanceOf(owner.address);
expect(balance).to.equal(ethers.parseEther("1000000"));
});
});
Questo test verifica che il contratto assegni il saldo iniziale al deployer. Sembra banale, ma ti protegge da errori di logica che costerebbero caro. Scrivi test per ogni funzione del contratto, non solo per i casi felici. Testa anche i casi di errore, come un trasferimento a un indirizzo non valido.
Come usare i fork di rete per testare scenari realistici?
Hardhat ti permette di fare il fork di una rete reale, come Ethereum mainnet, e testare il tuo codice su uno stato identico a quello di produzione. Questo è prezioso per verificare l'interazione con contratti esistenti, come Uniswap o Aave. Con un fork, puoi simulare transazioni con saldi reali, ma senza spendere un centesimo. Il fork è il tuo migliore amico per testare integrazioni complesse.
Cosa fare adesso per portare la tua dApp in produzione
Hai le basi, ora devi agire. Ecco i passi concreti:
- Scegli Ethers.js per i nuovi progetti, e pianifica la migrazione da Web3.js se hai legacy.
- Configura un provider gestito (Infura, Alchemy) con un piano che scala. Non usare nodi gratuiti in produzione.
- Scrivi test automatici con Hardhat e testa su Sepolia prima di toccare la mainnet.
- Implementa la sicurezza: niente chiavi nel frontend, validazione degli input, HTTPS obbligatorio.
- Monitora le transazioni e gestisci gli errori con try/catch e log dettagliati.
Se vuoi approfondire l'intero ecosistema, leggi la nostra guida Blockchain e Web3 per Sviluppatori. E ricorda: una dApp che non è testata è una dApp che perde soldi. Noi, di Meteora Web, costruiamo piattaforme che reggono il traffico e i controlli. Se serve, siamo qui.