Inicio / Blockchain / Blockchain: De Cero a Desarrollador / Web3.js y Ethers.js

Web3.js y Ethers.js

Providers, signers, contratos, eventos y utilidades.

Avanzado
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Web3.js y Ethers.js

Las bibliotecas Web3.js y Ethers.js permiten a las aplicaciones JavaScript interactuar con la blockchain de Ethereum: leer datos, enviar transacciones, interactuar con smart contracts y escuchar eventos.

Ethers.js vs Web3.js

Característica      Ethers.js v6          Web3.js v4
────────────────────────────────────────────────────
Tamaño              ~120KB                ~590KB
API                 Más moderna           Más legacy
Licencia            MIT                   LGPL-3.0
Providers           Separados de Signers  Todo en web3 instance
Popularidad         Creciendo rápido      Establecido
Recomendación       ✓ Preferido           Válido

Nos centraremos en Ethers.js por ser el estándar moderno.

Conceptos Fundamentales

Provider

Un Provider es la conexión (solo lectura) a la blockchain:

const { ethers } = require("ethers");

// Conectar a red local (Hardhat)
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545");

// Conectar a Ethereum mainnet via Alchemy/Infura
const mainnetProvider = new ethers.JsonRpcProvider(
    "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"
);

// Provider desde MetaMask (en el navegador)
const browserProvider = new ethers.BrowserProvider(window.ethereum);

Signer

Un Signer puede firmar transacciones (lectura + escritura):

// Desde clave privada
const wallet = new ethers.Wallet(privateKey, provider);

// Desde MetaMask
const signer = await browserProvider.getSigner();

// Ver dirección
console.log("Dirección:", await signer.getAddress());

Operaciones Básicas

Consultar la blockchain

// Balance de una cuenta
const balance = await provider.getBalance("0x742d35Cc...");
console.log("Balance:", ethers.formatEther(balance), "ETH");

// Número de bloque actual
const blockNumber = await provider.getBlockNumber();

// Información de un bloque
const block = await provider.getBlock(blockNumber);
console.log("Timestamp:", block.timestamp);
console.log("Transacciones:", block.transactions.length);

// Detalles de una transacción
const tx = await provider.getTransaction("0xtxhash...");
console.log("De:", tx.from);
console.log("Para:", tx.to);
console.log("Valor:", ethers.formatEther(tx.value));

// Precio del gas
const feeData = await provider.getFeeData();
console.log("Gas Price:", ethers.formatUnits(feeData.gasPrice, "gwei"), "Gwei");

Enviar ETH

async function enviarETH() {
    const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

    const tx = await wallet.sendTransaction({
        to: "0xDestinatario...",
        value: ethers.parseEther("0.1") // 0.1 ETH
    });

    console.log("TX Hash:", tx.hash);

    // Esperar confirmación
    const receipt = await tx.wait();
    console.log("Confirmada en bloque:", receipt.blockNumber);
    console.log("Gas usado:", receipt.gasUsed.toString());
}

Interactuar con Smart Contracts

Crear instancia de contrato

// ABI del contrato (puede ser completo o parcial)
const abi = [
    "function name() view returns (string)",
    "function symbol() view returns (string)",
    "function totalSupply() view returns (uint256)",
    "function balanceOf(address) view returns (uint256)",
    "function transfer(address to, uint256 amount) returns (bool)",
    "function approve(address spender, uint256 amount) returns (bool)",
    "event Transfer(address indexed from, address indexed to, uint256 value)"
];

// Solo lectura (con provider)
const tokenRead = new ethers.Contract(TOKEN_ADDRESS, abi, provider);

// Lectura + escritura (con signer)
const tokenWrite = new ethers.Contract(TOKEN_ADDRESS, abi, signer);

Leer datos del contrato

// Llamadas view/pure (gratis, no gastan gas)
const name = await tokenRead.name();
const symbol = await tokenRead.symbol();
const totalSupply = await tokenRead.totalSupply();
const balance = await tokenRead.balanceOf("0xUsuario...");

console.log(`${name} (${symbol})`);
console.log("Supply:", ethers.formatEther(totalSupply));
console.log("Balance:", ethers.formatEther(balance));

Escribir al contrato

// Transferir tokens (gasta gas)
async function transferirTokens() {
    const amount = ethers.parseEther("100"); // 100 tokens

    const tx = await tokenWrite.transfer("0xDestinatario...", amount);
    console.log("TX enviada:", tx.hash);

    const receipt = await tx.wait();
    console.log("Confirmada:", receipt.status === 1 ? "Éxito" : "Falló");
}

// Aprobar allowance
async function aprobar() {
    const amount = ethers.parseEther("1000");
    const tx = await tokenWrite.approve("0xSpender...", amount);
    await tx.wait();
    console.log("Aprobado");
}

Escuchar Eventos

// Eventos en tiempo real
tokenRead.on("Transfer", (from, to, amount, event) => {
    console.log(`Transfer: ${from} → ${to}`);
    console.log(`Cantidad: ${ethers.formatEther(amount)}`);
    console.log(`Block: ${event.log.blockNumber}`);
});

// Eventos históricos (filtrar por rango de bloques)
async function eventosHistoricos() {
    const filter = tokenRead.filters.Transfer();
    const events = await tokenRead.queryFilter(filter, -1000); // Últimos 1000 bloques

    events.forEach(event => {
        console.log(`${event.args.from} → ${event.args.to}: ${event.args.value}`);
    });
}

// Filtrar por dirección específica
const filterFrom = tokenRead.filters.Transfer("0xAlice...");
const filterTo = tokenRead.filters.Transfer(null, "0xBob...");

Utilidades de Ethers.js

// Conversiones de unidades
ethers.parseEther("1.5");           // "1500000000000000000" (Wei)
ethers.formatEther("1500000000000000000"); // "1.5"
ethers.parseUnits("20", "gwei");    // "20000000000"
ethers.formatUnits("20000000000", "gwei"); // "20.0"

// Hashing
ethers.keccak256(ethers.toUtf8Bytes("hello"));
ethers.id("Transfer(address,address,uint256)"); // Event topic

// ABI encoding
const coder = ethers.AbiCoder.defaultAbiCoder();
coder.encode(["address", "uint256"], ["0x...", 100]);
coder.decode(["address", "uint256"], encodedData);

// Generar wallet aleatorio
const randomWallet = ethers.Wallet.createRandom();
console.log("Address:", randomWallet.address);
console.log("Mnemonic:", randomWallet.mnemonic.phrase);

Patrón completo: DApp Backend

const { ethers } = require("ethers");
require("dotenv").config();

class BlockchainService {
    constructor() {
        this.provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
        this.wallet = new ethers.Wallet(process.env.PRIVATE_KEY, this.provider);
    }

    async getTokenInfo(tokenAddress) {
        const abi = [
            "function name() view returns (string)",
            "function symbol() view returns (string)",
            "function totalSupply() view returns (uint256)",
            "function decimals() view returns (uint8)"
        ];
        const token = new ethers.Contract(tokenAddress, abi, this.provider);

        return {
            name: await token.name(),
            symbol: await token.symbol(),
            totalSupply: ethers.formatEther(await token.totalSupply()),
            decimals: await token.decimals()
        };
    }

    async sendTokens(tokenAddress, to, amount) {
        const abi = ["function transfer(address,uint256) returns (bool)"];
        const token = new ethers.Contract(tokenAddress, abi, this.wallet);
        const tx = await token.transfer(to, ethers.parseEther(amount));
        return await tx.wait();
    }
}

Resumen

Ethers.js es la biblioteca estándar para interactuar con Ethereum desde JavaScript. Los Providers permiten lectura, los Signers lectura+escritura. Los contratos se instancian con ABI + dirección, y los eventos permiten reaccionar en tiempo real. Ethers.js proporciona utilidades para conversión de unidades, hashing, encoding y generación de wallets.

🔒

Ejercicio práctico disponible

Utilidades Web3 con JavaScript

Desbloquear ejercicios
// Utilidades Web3 con JavaScript
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes