Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Integración y desarrollo11 min de lectura

Crea una aplicación blockchain: arquitectura, cadena de herramientas y una ruta de compilación reproducible

Una ruta práctica y reproducible desde la validación del caso de uso hasta una aplicación blockchain desplegada, incluyendo diseño de contratos, integración RPC, verificación y resolución de problemas.

TL;DR

Crear una aplicación blockchain significa construir software que lee y escribe en un libro mayor distribuido, generalmente mediante un contrato inteligente más un cliente fuera de cadena que se comunica con un nodo a través de RPC. Esta guía recorre las decisiones que importan antes de escribir código y luego ofrece un contrato ejecutable, un script de despliegue y una integración de cliente que puedes reproducir en cualquier red compatible con EVM. Separa el comportamiento documentado del protocolo de las recomendaciones de ingeniería y de las mediciones que debes ejecutar tú mismo, y muestra cómo verificar cada paso en lugar de asumir el éxito. Al final tendrás un contrato probado, un cliente RPC funcional y una lista de verificación para solucionar los fallos que más a menudo bloquean los primeros despliegues.

Qué implica realmente crear una aplicación blockchain

Una aplicación blockchain no es un único artefacto. Son al menos tres piezas que cooperan: un programa en cadena (un contrato inteligente o un módulo nativo) que aplica reglas y almacena estado, un cliente fuera de cadena que construye y firma transacciones, y un endpoint de nodo que acepta esas transacciones y sirve lecturas. El libro mayor proporciona orden y finalidad; tu aplicación proporciona la lógica de negocio y la experiencia de usuario a su alrededor.

Esta distinción importa porque la mayoría de los fallos de principiante no son errores del contrato. Son fallos de integración: un cliente apuntando al chain ID incorrecto, una transacción enviada antes de que avanzara el nonce, una lectura servida por un nodo retrasado o una dirección de contrato registrada de un despliegue fallido. Trata las mitades en cadena y fuera de cadena como sistemas separados con pruebas separadas.

Nota de alcance: este artículo se centra en redes compatibles con EVM porque las herramientas son uniformes y reproducibles. El mismo modelo mental se aplica a cadenas no EVM, pero el lenguaje de contrato, el ABI y las bibliotecas de cliente difieren. Para elecciones de endpoints específicos de red, usa el Asistente RPC en lugar de codificar un proveedor aquí.

  • En cadena: código del contrato, diseño de almacenamiento, eventos, control de acceso.
  • Fuera de cadena: firma, ciclo de vida de transacciones, reintentos, indexación, interfaz de usuario.
  • Transporte: JSON-RPC sobre HTTPS o WebSocket hacia un nodo en el que confías para corrección y disponibilidad.

Decidir si el caso de uso necesita una blockchain

Antes de elegir un protocolo, prueba el requisito contra propiedades que solo proporciona un libro mayor compartido: múltiples partes independientes, necesidad de un rastro de auditoría a prueba de manipulaciones y una regla que debería ejecutarse sin un único operador de confianza. Si una organización controla todos los escritores y lectores, una base de datos convencional con un registro de auditoría de solo adición suele ser más barata, rápida y fácil de operar.

Un filtro útil es preguntar quién debe poder verificar el historial sin confiar en ti. Si la respuesta es 'nadie fuera de nuestro equipo', la blockchain añade coste sin añadir confianza. Si la respuesta es 'contrapartes, auditores o usuarios que no confían en nuestros servidores', el libro mayor se gana su lugar. Escribe esta respuesta; impulsa cada decisión posterior sobre despliegue público versus permisionado y sobre cuántos datos pertenecen en cadena.

Mantén las cargas útiles grandes fuera de cadena. Almacena hashes, identificadores y transiciones de estado en cadena, y mantén documentos, imágenes y datos personales en almacenamiento convencional referenciado por hash. Esto reduce el coste de gas y evita poner datos regulados en un medio inmutable.

Elegir una red, un modelo de cuenta y una estrategia de endpoints

La elección de red es un equilibrio entre tiempo de finalidad, mercado de comisiones, madurez de herramientas y quién puede leer tu estado. Las redes EVM públicas comparten herramientas, por lo que un contrato escrito para una generalmente se porta con cambios de configuración. Los despliegues permisionados cambian apertura por privacidad y membresía controlada. Decide cuál de estos exige tu filtro de caso de uso antes de escribir código.

Tu estrategia de endpoints es una decisión operativa, no una línea de código. Un único endpoint público está bien para un tutorial e es inaceptable para cualquier cosa de la que dependan los usuarios. Planifica al menos un endpoint primario y uno de respaldo, una comprobación de salud y una regla clara sobre qué lecturas toleran datos ligeramente obsoletos. La guía de endpoints RPC multicadena cubre patrones de conmutación por error, y los precios de RPC ayudan a dimensionar el volumen de solicitudes una vez que conoces tu ratio de lectura/escritura.

Para cargas de trabajo en producción que necesitan rendimiento predecible y aislamiento, un nodo dedicado elimina los efectos de vecino ruidoso que pueden exhibir los endpoints compartidos. Para una primera compilación, un endpoint gestionado es suficiente; lo importante es mantener la URL en configuración para poder cambiarla sin tocar el código de la aplicación.

  • Registra el chain ID, la moneda nativa y el explorador de bloques de cada red que toques.
  • Mantén las URL de RPC en variables de entorno, nunca en el código fuente comprometido.
  • Decide las necesidades de consistencia de lectura antes de elegir entre un solo nodo y un pool con balanceo de carga.

Requisitos previos y una configuración local reproducible

Necesitas Node.js 18 o superior, un gestor de paquetes y una cuenta de testnet con fondos. Los pasos siguientes usan Hardhat porque agrupa compilación, pruebas y scripting de despliegue en una sola cadena de herramientas. Instala en un directorio vacío para que las versiones queden fijadas por el archivo de bloqueo generado.

Nunca uses una clave privada de mainnet durante el desarrollo. Genera una clave nueva para testnets, guárdala en un archivo .env excluido del control de versiones y trata cualquier clave pegada en un chat o registro como comprometida.

mkdir chain-app && cd chain-app
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv
npx hardhat init
# choose: Create a JavaScript project
printf 'RPC_URL=\nPRIVATE_KEY=\n' > .env
echo '.env' >> .gitignore

Escribir un contrato mínimo con un modelo de estado explícito

Empieza con un contrato lo suficientemente pequeño como para razonar sobre él por completo. El ejemplo siguiente almacena un valor por remitente y emite un evento en cada escritura. El evento no es decoración: los sistemas fuera de cadena deberían reconstruir el estado a partir de eventos en lugar de consultar el almacenamiento, porque los eventos son más baratos de consultar y te dan un historial ordenado.

Fíjate en la regla de acceso. Cualquier dirección puede escribir su propia ranura, pero ninguna dirección puede escribir la de otra. Hacer explícita la autorización a este tamaño previene la clase más común de vulnerabilidad de contratos, que es una transición de estado sin protección.

// contracts/Registry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Registry {
    mapping(address => string) private values;

    event ValueSet(address indexed author, string value);

    function setValue(string calldata value) external {
        require(bytes(value).length > 0, "empty value");
        values[msg.sender] = value;
        emit ValueSet(msg.sender, value);
    }

    function getValue(address author) external view returns (string memory) {
        return values[author];
    }
}

Compilar, probar y desplegar con un paso de verificación

Escribe al menos una prueba que verifique tanto el camino feliz como el camino de rechazo. Una prueba que solo comprueba el camino feliz pasará mientras tu control de acceso está roto. Ejecuta la suite antes de cada despliegue; una prueba fallida es más barata que una transacción fallida.

El script de despliegue siguiente lee la configuración del entorno, despliega, espera el recibo e imprime la dirección. Esperar el recibo es el paso de verificación: una dirección impresa antes de la confirmación puede pertenecer a una transacción que nunca se confirma.

// scripts/deploy.js
const hre = require("hardhat");

async function main() {
  const [deployer] = await hre.ethers.getSigners();
  console.log("deployer:", deployer.address);
  const Registry = await hre.ethers.getContractFactory("Registry");
  const registry = await Registry.deploy();
  await registry.waitForDeployment();
  const address = await registry.getAddress();
  console.log("Registry deployed at:", address);
}

main().catch((e) => { console.error(e); process.exit(1); });

// hardhat.config.js excerpt
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
module.exports = {
  solidity: "0.8.24",
  networks: {
    target: {
      url: process.env.RPC_URL,
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
    }
  }
};

Conectar un cliente fuera de cadena mediante JSON-RPC

El cliente es donde surgen la mayoría de los errores de integración. El script siguiente lee el número de bloque actual, llama a una función de vista y envía una transacción que cambia el estado, comprobando el estado del recibo. Ejecútalo contra tu dirección desplegada y confirma que cada valor impreso coincide con lo que muestra el explorador.

El campo de estado del recibo es la señal autoritativa de éxito. Que exista un hash de transacción no significa que la transacción haya tenido éxito; las transacciones revertidas también se minan y también cuestan gas. Siempre ramifica según el estado antes de actualizar tu propia base de datos.

// scripts/client.js
const hre = require("hardhat");

async function main() {
  const address = process.env.CONTRACT_ADDRESS;
  const registry = await hre.ethers.getContractAt("Registry", address);

  const block = await hre.ethers.provider.getBlockNumber();
  console.log("block:", block);

  const tx = await registry.setValue("hello-ledger");
  const receipt = await tx.wait();
  console.log("status:", receipt.status, "gasUsed:", receipt.gasUsed.toString());

  const value = await registry.getValue(await tx.getSigner().getAddress());
  console.log("stored:", value);
}

main().catch((e) => { console.error(e); process.exit(1); });

Un procedimiento de medición que ejecutas tú mismo

No confíes en cifras publicadas de latencia o tasa de éxito para tu carga de trabajo; mídela. El procedimiento siguiente produce una tabla de resultados que puedes comparar entre endpoints y a lo largo del día. Ejecuta cada endpoint al menos 100 veces y registra la distribución, no solo el promedio, porque la latencia de cola es lo que los usuarios notan.

Registra las siguientes columnas para cada endpoint y repite la ejecución tres veces: número de solicitudes, número de éxitos, latencia p50, latencia p95 y cualquier código de error observado. Rellena la tabla con tus propias mediciones; las celdas siguientes están intencionalmente en blanco porque números fabricados engañarían tu planificación de capacidad.

  • | Endpoint | Solicitudes | Éxitos | p50 (ms) | p95 (ms) | Errores |
  • | primario | | | | | |
  • | respaldo | | | | | |
// scripts/measure.js — run with: node scripts/measure.js
const url = process.env.RPC_URL;
const N = 100;

async function once() {
  const t0 = performance.now();
  const res = await fetch(url, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_blockNumber", params: [] })
  });
  const body = await res.json();
  return { ms: performance.now() - t0, ok: res.ok && !body.error };
}

(async () => {
  const samples = [];
  for (let i = 0; i < N; i++) samples.push(await once());
  const ok = samples.filter(s => s.ok).length;
  const sorted = samples.map(s => s.ms).sort((a, b) => a - b);
  const p = q => sorted[Math.floor(q * (sorted.length - 1))].toFixed(1);
  console.log({ requests: N, successes: ok, p50: p(0.5), p95: p(0.95) });
})();

Resultados de verificación esperados y qué prueba cada uno

Una ejecución exitosa prueba más que 'funcionó'. Comprueba cada señal por separado para que un fallo apunte a la capa correcta. Si el número de bloque se imprime pero la llamada al contrato se revierte, tu transporte está bien y el problema es a nivel de contrato. Si el despliegue imprime una dirección pero el cliente no encuentra código allí, probablemente estás en la red incorrecta o el despliegue se revirtió.

Confirma que la dirección desplegada tiene bytecode consultando eth_getCode; un resultado vacío significa que no se desplegó nada en esa dirección en esa cadena. Confirma que el estado del recibo de la transacción es 1. Confirma que el valor almacenado leído de vuelta coincide con lo que escribiste. Confirma que el explorador muestra el mismo bloque y transacción que viste localmente.

  • El número de bloque avanza entre dos llamadas: el transporte y el chain ID son correctos.
  • eth_getCode devuelve algo no vacío: existe un contrato en la dirección en esta cadena.
  • El estado del recibo es 1: la transición de estado se ejecutó sin reversión.
  • El valor leído de vuelta coincide con la escritura: el almacenamiento y la codificación ABI concuerdan.
  • El explorador concuerda con la salida local: no estás en una bifurcación o en un nodo obsoleto.

Solución de problemas que bloquean los primeros despliegues

Los errores de nonce y reemplazo generalmente significan que una transacción anterior sigue pendiente. Consulta el nonce pendiente y espera o reemplaza la transacción con una comisión más alta. No reenvíes a ciegas, porque envíos duplicados con el mismo nonce pueden dejar varadas transacciones posteriores.

Los errores de fondos insuficientes en testnets suelen ser un problema de faucet o de coincidencia de chain ID más que un problema real de saldo. Verifica que el chain ID que reporta tu cliente coincida con la red que financiaste. Si las lecturas devuelven datos obsoletos, puede que estés golpeando un pool con balanceo de carga donde un backend se retrasa; fija las lecturas a un solo nodo o añade una comprobación de frescura del número de bloque.

Los fallos de estimación de gas frecuentemente provienen de una reversión que ocurriría durante la ejecución. Llama primero a la función con eth_call para exponer el motivo de la reversión antes de enviar una transacción. Para un diagnóstico más profundo a nivel de endpoint, consulta la guía de endpoints RPC y la guía de alojamiento de nodos blockchain.

  • nonce too low: una transacción anterior ya lo consumió; actualízalo desde el nodo.
  • replacement underpriced: eleva el incremento de comisión por encima del mínimo del nodo.
  • execution reverted: reproduce con eth_call para leer la cadena de motivo.
  • lecturas obsoletas: compara el último bloque del nodo que sirve con un segundo endpoint.

Limitaciones, compensaciones y cuándo dejar de construir

La ejecución en cadena es costosa y pública. Cada escritura de estado se replica en toda la red, por lo que los costes escalan con el almacenamiento y la computación de formas que una base de datos no. Diseña para un estado en cadena mínimo y empuja la agregación fuera de cadena cuando sea posible.

La inmutabilidad corta en ambos sentidos. Un error en un contrato desplegado puede ser imposible de corregir sin una migración, así que planifica rutas de actualización o contratos versionados antes del lanzamiento. Las redes públicas también exponen tu lógica a adversarios que pueden leerla y simularla; asume que tu contrato será sondeado.

Finalmente, una aplicación blockchain hereda la fiabilidad de su dependencia más débil. Si tu endpoint está caído, tu aplicación está caída aunque la cadena esté sana. Presupuesta endpoints redundantes y monitorízalos de la misma manera que monitorizas tus propios servidores.

Próximos pasos del prototipo a la producción

Una vez verificado el flujo local, refuérzalo: añade pruebas de integración contra una red bifurcada, añade un endpoint de respaldo con comprobaciones de salud e instrumenta tu cliente para registrar números de bloque junto con IDs de solicitud para que los incidentes sean diagnosticables. Revisa el directorio de redes para confirmar los parámetros de la cadena antes de apuntar a un nuevo entorno.

Para la selección de endpoints y comparaciones de proveedores, usa el Asistente RPC en lugar de incrustar una elección de proveedor en tu base de código. La descripción general del servicio API describe cómo encaja el acceso gestionado en una aplicación, y la guía de RPC para proyectos Web3 cubre criterios de evaluación. Entrega el contrato más pequeño que satisfaga tu filtro de caso de uso, mídalo y amplía solo cuando las mediciones lo justifiquen.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar