Saltar a contenido

Ejercicios resueltos — Capítulo 30

Volver al índice · Capítulo original

Ejercicio 1: Instalar MetaMask en testnet

Instala MetaMask y crea una wallet en testnet.

Solución

  1. Instala la extensión MetaMask en Chrome/Firefox/Brave.
  2. Crea una wallet nueva (guarda la frase semilla en un sitio seguro).
  3. Cambia la red a Sepolia (testnet):
  4. Red: Sepolia
  5. RPC URL: https://rpc.sepolia.org
  6. Chain ID: 11155111
  7. Símbolo: SepoliaETH
  8. Pide ETH de prueba gratis en Sepolia Faucet.

Ejercicio 2: Conectar la web a MetaMask

Conecta tu web a MetaMask con eth_requestAccounts.

Solución

<button id="conectar">Conectar wallet</button>
<p id="estado"></p>

<script>
  const btn = document.getElementById('conectar');
  const estado = document.getElementById('estado');

  btn.addEventListener('click', async () => {
    if (!window.ethereum) {
      estado.textContent = 'Instala MetaMask';
      return;
    }

    try {
      const cuentas = await window.ethereum.request({
        method: 'eth_requestAccounts',
      });
      estado.textContent = `Conectado: ${cuentas[0].slice(0, 6)}...${cuentas[0].slice(-4)}`;
    } catch (err) {
      estado.textContent = 'Rechazado por el usuario';
    }
  });
</script>

Ejercicio 3: Leer balance con ethers

Lee el balance de una dirección.

Solución

npm install ethers
import { BrowserProvider, formatEther } from 'ethers';

async function leerBalance(direccion) {
  const provider = new BrowserProvider(window.ethereum);
  const balance = await provider.getBalance(direccion);

  return formatEther(balance); // convierte de wei a ETH
}

const saldo = await leerBalance('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
console.log('Balance:', saldo, 'ETH');

Desde una wallet conectada:

const provider = new BrowserProvider(window.ethereum);
const cuentas = await provider.send('eth_requestAccounts', []);
const saldo = await provider.getBalance(cuentas[0]);
console.log('Tu balance:', formatEther(saldo), 'ETH');

Ejercicio 4: Contrato Contador en Sepolia

Despliega un contrato Contador en Sepolia y úsalo desde el frontend.

Solución

Contrato Contador.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Contador {
    uint256 public cuenta;

    function incrementar() public {
        cuenta += 1;
    }

    function reiniciar() public {
        cuenta = 0;
    }
}

Compilar y desplegar:

  1. Usa Remix IDE o Hardhat.
  2. Conecta MetaMask a Sepolia.
  3. Deploy → copia la dirección.

Frontend:

import { BrowserProvider, Contract } from 'ethers';

const ABI = [
  'function cuenta() view returns (uint256)',
  'function incrementar()',
  'function reiniciar()',
];

const DIRECCION = '0x...'; // dirección del contrato

async function incrementar() {
  const provider = new BrowserProvider(window.ethereum);
  const signer = await provider.getSigner();
  const contrato = new Contract(DIRECCION, ABI, signer);

  const tx = await contrato.incrementar();
  console.log('Transacción enviada:', tx.hash);
  await tx.wait();
  console.log('Confirmada');
}

async function leer() {
  const provider = new BrowserProvider(window.ethereum);
  const contrato = new Contract(DIRECCION, ABI, provider);
  const cuenta = await contrato.cuenta();
  console.log('Cuenta:', cuenta.toString());
}

Ejercicio 5: Información de un NFT

Muestra información de un NFT desde OpenSea API.

Solución

const API_KEY = 'tu-api-key-opensea';
const CONTRATO = '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D'; // BAYC
const TOKEN_ID = '1';

async function obtenerNFT() {
  const res = await fetch(
    `https://api.opensea.io/api/v2/chain/ethereum/contract/${CONTRATO}/nfts/${TOKEN_ID}`,
    {
      headers: {
        'X-API-KEY': API_KEY,
      },
    }
  );

  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const data = await res.json();
  return data.nft;
}

const nft = await obtenerNFT();
console.log('Nombre:', nft.name);
console.log('Imagen:', nft.image_url);

Ejercicio 6: Subir a IPFS

Sube un archivo a IPFS y accede vía gateway.

Solución

npm install ipfs-http-client
import { create } from 'ipfs-http-client';

const ipfs = create({ url: 'https://ipfs.infura.io:5001/api/v0' });

async function subir(texto) {
  const { path } = await ipfs.add(texto);
  return `https://ipfs.io/ipfs/${path}`;
}

const url = await subir('Hola desde IPFS');
console.log('Disponible en:', url);

Consideraciones: - IPFS público → el archivo se replica entre nodos. - Si nadie lo pide, puede desaparecer (no es almacenamiento permanente). - Para persistencia, usa Pinata, NFT.storage o web3.storage.


Ejercicio 7: Comparar Web2 vs Web3

Escribe el mismo formulario con Web2 y con Web3.

Web2: Login tradicional

<form id="login">
  <input type="email" placeholder="Email" required>
  <input type="password" placeholder="Contraseña" required>
  <button>Entrar</button>
</form>

<script>
  document.getElementById('login').addEventListener('submit', async (e) => {
    e.preventDefault();
    const form = new FormData(e.target);
    const res = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(Object.fromEntries(form)),
      headers: { 'Content-Type': 'application/json' },
    });
    const { token } = await res.json();
    localStorage.setItem('token', token);
  });
</script>

Ventajas: - UX familiar. - Recuperación de contraseña posible. - Sin gas fees.

Desventajas: - Servidor central puede caer. - Datos en el servidor.

Web3: Conectar wallet

<button id="conectar">Conectar wallet</button>

<script>
  document.getElementById('conectar').addEventListener('click', async () => {
    if (!window.ethereum) return alert('Instala MetaMask');

    const cuentas = await window.ethereum.request({
      method: 'eth_requestAccounts',
    });

    console.log('Conectado como:', cuentas[0]);
  });
</script>

Ventajas: - Sin contraseñas ni emails. - Identidad soberana. - Login con una sola acción.

Desventajas: - Si pierdes la clave, pierdes la cuenta. - Curva de aprendizaje alta. - Requiere instalar una extensión.

Conclusión

No todo debe ser Web3. Web2 sigue siendo la mejor opción para el 99% de casos. Usa Web3 solo cuando la descentralización sea un requisito real.


Volver al índice · Capítulo original