Inicio / Blockchain / Blockchain: De Cero a Desarrollador / Deploy y Verificación en Producción

Deploy y Verificación en Producción

Testnet, Etherscan, mainnet, proxies, gas y monitoreo.

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

Deploy y Verificación en Producción

Desplegar un smart contract a mainnet es un proceso que requiere precaución extrema. Una vez desplegado, el código es inmutable y cualquier error puede resultar en pérdida de fondos. En esta lección cubriremos todo el proceso de deploy profesional.

Checklist Pre-Deploy

ANTES de desplegar a mainnet:

☐ Código auditado por empresa externa
☐ Coverage de tests > 95%
☐ Tests de integración pasando
☐ Gas optimizado (gas reporter)
☐ Contrato verificado en testnet
☐ Frontend testeado con testnet
☐ Multisig para funciones admin
☐ Bug bounty program activo
☐ Documentación completa
☐ Plan de emergencia (pausability)
☐ Timelock en funciones críticas

Deploy a Testnet

// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
    solidity: "0.8.20",
    networks: {
        sepolia: {
            url: process.env.SEPOLIA_RPC_URL,
            accounts: [process.env.DEPLOYER_PRIVATE_KEY],
            gasPrice: "auto"
        }
    },
    etherscan: {
        apiKey: process.env.ETHERSCAN_API_KEY
    }
};
# .env (NUNCA commitear al repo)
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
DEPLOYER_PRIVATE_KEY=0xTU_CLAVE_PRIVADA
ETHERSCAN_API_KEY=TU_API_KEY
# Deploy a Sepolia
npx hardhat run scripts/deploy.js --network sepolia

# Output:
# Token desplegado en: 0x1234...

Verificación en Etherscan

# Verificar automáticamente con Hardhat
npx hardhat verify --network sepolia CONTRACT_ADDRESS "Arg1" "Arg2"

# Ejemplo para un token ERC-20:
npx hardhat verify --network sepolia \
    0x1234567890abcdef... \
    "Mi Token" "MTK" 1000000

Verificación programática

// scripts/deploy-and-verify.js
async function main() {
    const [deployer] = await ethers.getSigners();

    console.log("Desplegando...");
    const Token = await ethers.getContractFactory("MiToken");
    const token = await Token.deploy("Mi Token", "MTK", 1000000);
    await token.waitForDeployment();

    const address = await token.getAddress();
    console.log("Desplegado en:", address);

    // Esperar 6 confirmaciones para Etherscan
    console.log("Esperando confirmaciones...");
    await token.deploymentTransaction().wait(6);

    // Verificar
    console.log("Verificando en Etherscan...");
    try {
        await hre.run("verify:verify", {
            address: address,
            constructorArguments: ["Mi Token", "MTK", 1000000],
        });
        console.log("Verificado exitosamente");
    } catch (error) {
        if (error.message.includes("Already Verified")) {
            console.log("Ya estaba verificado");
        } else {
            throw error;
        }
    }
}

Deploy a Mainnet

// scripts/deploy-mainnet.js
async function main() {
    const [deployer] = await ethers.getSigners();
    const balance = await ethers.provider.getBalance(deployer.address);

    console.log("=== DEPLOY A MAINNET ===");
    console.log("Deployer:", deployer.address);
    console.log("Balance:", ethers.formatEther(balance), "ETH");

    // Estimar costos
    const Token = await ethers.getContractFactory("MiToken");
    const deployTx = await Token.getDeployTransaction("Mi Token", "MTK", 1000000);
    const estimatedGas = await ethers.provider.estimateGas(deployTx);
    const feeData = await ethers.provider.getFeeData();
    const estimatedCost = estimatedGas * feeData.maxFeePerGas;

    console.log("Gas estimado:", estimatedGas.toString());
    console.log("Costo estimado:", ethers.formatEther(estimatedCost), "ETH");

    // Confirmación manual
    console.log("\n¿Continuar? (CTRL+C para cancelar, espera 10 segundos)");
    await new Promise(r => setTimeout(r, 10000));

    // Deploy
    const token = await Token.deploy("Mi Token", "MTK", 1000000);
    await token.waitForDeployment();

    console.log("CONTRATO DESPLEGADO:", await token.getAddress());
}

Proxies: Contratos Actualizables

Para contratos que necesitan ser actualizables:

npm install @openzeppelin/hardhat-upgrades
// scripts/deploy-proxy.js
const { ethers, upgrades } = require("hardhat");

async function main() {
    // Deploy con proxy (UUPS o Transparent)
    const Token = await ethers.getContractFactory("MiTokenV1");
    const proxy = await upgrades.deployProxy(Token, ["Mi Token", "MTK"], {
        initializer: "initialize",
        kind: "uups"
    });
    await proxy.waitForDeployment();

    console.log("Proxy desplegado en:", await proxy.getAddress());
}
// scripts/upgrade.js
async function main() {
    const proxyAddress = "0x... dirección del proxy ...";

    const TokenV2 = await ethers.getContractFactory("MiTokenV2");
    const upgraded = await upgrades.upgradeProxy(proxyAddress, TokenV2);
    await upgraded.waitForDeployment();

    console.log("Proxy actualizado a V2");
}

Gas Optimization

Técnicas para reducir costos de deploy y ejecución:

// ❌ Costoso: strings largos en require
require(balance > 0, "El balance del usuario debe ser mayor que cero");

// ✅ Eficiente: custom errors
error InsufficientBalance();
if (balance == 0) revert InsufficientBalance();

// ❌ Costoso: múltiples SSTOREs
contract Costoso {
    uint public a;
    uint public b;
    function set(uint _a, uint _b) public {
        a = _a; // SSTORE 1
        b = _b; // SSTORE 2
    }
}

// ✅ Eficiente: packing de variables
contract Eficiente {
    uint128 public a;  // Se empaquetan en
    uint128 public b;  // un solo slot de 32 bytes

    // Usar unchecked cuando es seguro
    function incrementar(uint x) public pure returns (uint) {
        unchecked { return x + 1; } // Ahorra ~100 gas
    }
}

Monitoreo Post-Deploy

Herramientas de monitoreo:

Tenderly     → Simulación de transacciones, alertas
OpenZeppelin Defender → Monitoreo automatizado, relayers
Etherscan    → Logs, eventos, transacciones
TheGraph     → Indexación de eventos para queries
Dune Analytics → Dashboards y métricas on-chain

Configurar alertas para:
☐ Transacciones con alto valor
☐ Llamadas a funciones admin
☐ Cambios en ownership
☐ Pausas/unpauses del contrato
☐ Deploys de nuevas implementaciones (proxy)

Seguridad en Producción

Gestión de claves:
- NUNCA guardar claves privadas en código
- Usar hardware wallet para deploy a mainnet
- Implementar multisig (Gnosis Safe) para admin
- Separar wallet de deploy de wallet de admin

Timelock:
- Esperar 24-48h antes de ejecutar cambios críticos
- DAOs usan governance con timelock
- Permite a usuarios reaccionar antes del cambio

Pausability:
- Implementar función pause() para emergencias
- Solo admin/multisig puede pausar
- Diseñar qué funciones se pausan y cuáles no

Resumen

El deploy a producción requiere un proceso riguroso: auditoría, testing exhaustivo, deploy en testnet, verificación en Etherscan y monitoreo post-deploy. Para contratos actualizables, usar proxies de OpenZeppelin. La optimización de gas reduce costos, y el monitoreo continuo con alertas protege contra incidentes. Nunca apresures un deploy a mainnet: los fondos de los usuarios dependen de un código sin errores.

🔒

Ejercicio práctico disponible

Checklist y herramientas de deploy

Desbloquear ejercicios
// Checklist y herramientas de deploy
// 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