Una hot wallet y una cold wallet no son tanto dos categorías de productos como dos puntos en un espectro de exposición de claves privadas: una hot wallet mantiene el material de firma en una máquina conectada a internet, mientras que una cold wallet lo mantiene en un dispositivo o medio que está offline cuando se usan las claves. La diferencia de seguridad proviene de la superficie de ataque alrededor de la clave, no de la etiqueta en la caja, y una hardware wallet que está permanentemente conectada a un portátil en red no se comporta como una cold wallet. Este artículo construye un modelo mental de custodia de claves, recorre un procedimiento de firma offline y verificación reproducible que puedes ejecutar contra cualquier endpoint RPC compatible con EVM, y explica dónde impactan realmente las compensaciones en producción.
Respuesta directa y alcance
Una hot wallet almacena y usa claves privadas en un dispositivo conectado a internet, por lo que la firma ocurre en el mismo entorno que navega, descarga y ejecuta otro software. Una cold wallet mantiene las claves privadas en un medio que está offline en el momento de la firma, y la transacción se traslada a ese medio para su aprobación. La diferencia práctica es el tamaño de la superficie de ataque alrededor de la clave: las hot wallets optimizan para velocidad y programabilidad, las cold wallets optimizan para aislar la clave del compromiso remoto.
Este artículo trata sobre el mecanismo y las compensaciones operativas, no sobre recomendar una marca específica. Asume que ya entiendes que una wallet contiene claves en lugar de monedas, y que los saldos on-chain viven en cuentas controladas por esas claves. También asume que tienes acceso a un endpoint RPC compatible con EVM, que puedes obtener a través del servicio API de OnFinality o comparar entre redes soportadas.
- Hot wallet: el material de la clave está online al firmar; conveniente para transacciones frecuentes, pequeñas o automatizadas.
- Cold wallet: el material de la clave está offline al firmar; conveniente para tenencias grandes, poco frecuentes y de largo horizonte.
- La etiqueta importa menos que la ruta de firma real: ¿dónde vive la clave y qué puede alcanzarla?
Supuestos y requisitos previos
El procedimiento a continuación utiliza una cadena compatible con EVM y un endpoint JSON-RPC. Necesitas Node.js 18 o posterior, la librería ethers y una cuenta de prueba con fondos en una testnet. Nunca ejecutes estos ejemplos con una clave que controle fondos reales. El objetivo es observar el límite de firma, no mover valor.
También necesitas una forma de inspeccionar lo que tu wallet está haciendo realmente. Un explorador de bloques para la cadena objetivo, junto con la capacidad de leer eth_chainId y eth_getTransactionCount, es suficiente para verificar que una transacción firmada se construyó contra la red y el nonce correctos antes de ser transmitida.
npm init -y
npm install ethers
# create a throwaway test key, never reuse it for real funds
node -e "const {Wallet}=require('ethers');const w=Wallet.createRandom();console.log(w.address);console.log(w.privateKey)"El modelo mental: exposición de la clave, no tipo de dispositivo
Cada wallet es un sistema de gestión de claves con tres tareas: generar una clave, almacenarla y usarla para firmar. Hot y cold describen la segunda y tercera tareas. En una hot wallet, el almacenamiento y la firma ocurren en una máquina de propósito general conectada a la red. En una cold wallet, el almacenamiento y la firma ocurren en un dispositivo dedicado o una máquina aislada de la red (air-gapped), y solo el payload sin firmar cruza el límite.
Por eso es útil la distinción comunitaria entre una hardware wallet y una cold wallet. Una hardware wallet es un dispositivo; una cold wallet es una propiedad de cómo se usa ese dispositivo. Una hardware wallet conectada por USB a un portátil que simultáneamente ejecuta un navegador y una extensión de wallet sigue aislando la clave del host, pero el host puede influir en lo que se firma. Un dispositivo air-gapped que intercambia códigos QR o tarjetas SD elimina incluso ese canal, a costa de la conveniencia.
El modelo de amenaza se deriva directamente. Las hot wallets están expuestas a malware, extensiones de navegador maliciosas, secuestro del portapapeles, sitios de phishing que solicitan firmas y compromiso remoto del host. Las cold wallets reducen esos riesgos pero introducen riesgo de cadena de suministro, riesgo de firmware, riesgo de acceso físico y el riesgo de que un usuario firme una transacción que no puede leer completamente en una pantalla pequeña.
- Atacante remoto con acceso al host: la clave de la hot wallet es alcanzable; la clave de la cold wallet no.
- dApp maliciosa que solicita una firma: ambas pueden ser engañadas, pero una cold wallet ofrece una segunda superficie de revisión.
- Dispositivo perdido o destruido: ambas dependen de una copia de seguridad offline correcta de la frase de recuperación.
- Coacción o robo físico: el almacenamiento en frío desplaza el riesgo hacia la capa física y de respaldo.
Cómo difiere la firma entre los dos modelos
En una hot wallet, el flujo es corto: construir la transacción, firmarla localmente con la clave que ya está en memoria y transmitir la transacción en bruto. En una cold wallet, el flujo se divide: una máquina online construye la transacción sin firmar, el payload sin firmar se transfiere al firmante offline, el firmante devuelve una firma y la máquina online transmite la transacción firmada. La máquina online nunca ve la clave privada.
Esa división es todo el argumento de seguridad, y también es la fuente de la mayor fricción operativa. Cada paso en el que el payload cruza el límite es un lugar donde ambos lados pueden discrepar sobre el ID de cadena, el nonce, el destinatario, el valor o los parámetros de gas. Por lo tanto, un flujo de firma en frío robusto incluye un paso de verificación que decodifica la transacción firmada y la compara con la intención antes de la transmisión.
Procedimiento ejecutable: construir, firmar offline y verificar
El siguiente script demuestra el límite. El primer bloque se ejecuta en la máquina online y produce una transacción sin firmar. El segundo bloque representa al firmante offline. El tercer bloque se ejecuta de nuevo en la máquina online y verifica la firma antes de transmitir. Reemplaza RPC_URL con tu endpoint y PRIVATE_KEY con una clave de prueba desechable.
El paso de verificación es la parte que la mayoría de los tutoriales omite. Decodifica la transacción firmada y verifica que el ID de cadena, el destinatario y el valor coincidan con lo previsto. Si alguno de ellos difiere, la transacción no se transmite.
// online machine: build unsigned transaction
const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('RPC_URL');
async function buildUnsigned() {
const chainId = (await provider.getNetwork()).chainId;
const nonce = await provider.getTransactionCount('0xYourFromAddress');
const unsigned = {
to: '0xRecipientAddress',
value: ethers.parseEther('0.001'),
nonce,
chainId,
gasLimit: 21000n,
maxFeePerGas: ethers.parseUnits('20', 'gwei'),
maxPriorityFeePerGas: ethers.parseUnits('1', 'gwei'),
type: 2
};
console.log(JSON.stringify(unsigned, (k, v) => typeof v === 'bigint' ? v.toString() : v));
}
buildUnsigned();Firma offline y verificación on-chain
En el firmante offline, deserializa el payload sin firmar, fírmalo y devuelve solo la transacción firmada serializada. La clave privada nunca sale de este entorno. En la máquina online, analiza la transacción firmada y verifica sus campos antes de enviarla.
Una ejecución exitosa produce un hash de transacción, y el explorador muestra la transferencia con el destinatario y el valor esperados. Una verificación fallida se detiene antes de la transmisión e imprime el campo que no coincide, que es exactamente el comportamiento que deseas cuando un payload ha sido manipulado en tránsito.
// offline signer: sign the payload, return raw tx only
const { ethers } = require('ethers');
const unsigned = JSON.parse(process.argv[2]);
const wallet = new ethers.Wallet('PRIVATE_KEY');
const signed = await wallet.signTransaction({
...unsigned,
nonce: Number(unsigned.nonce),
chainId: Number(unsigned.chainId),
gasLimit: BigInt(unsigned.gasLimit),
maxFeePerGas: BigInt(unsigned.maxFeePerGas),
maxPriorityFeePerGas: BigInt(unsigned.maxPriorityFeePerGas),
value: BigInt(unsigned.value)
});
console.log(signed);
// online machine: verify before broadcast
const parsed = ethers.Transaction.from(signedRaw);
if (parsed.chainId !== expectedChainId) throw new Error('chainId mismatch');
if (parsed.to.toLowerCase() !== expectedTo.toLowerCase()) throw new Error('recipient mismatch');
if (parsed.value !== expectedValue) throw new Error('value mismatch');
const tx = await provider.broadcastTransaction(signedRaw);
console.log(tx.hash);Tabla de resultados y qué registrar
Ejecuta el procedimiento en una testnet y registra los siguientes campos para cada intento. La tabla es una plantilla; complétala con tus propios valores observados en lugar de asumir un resultado particular. El objetivo es mostrar que la transacción firmada coincide con la intención y que la transmisión solo tiene éxito después de la verificación.
| Campo | Esperado | Observado | Aprobado/Fallido |
|---|---|---|---|
| chainId | coincide con la red objetivo | ||
| nonce | siguiente nonce no usado para el remitente | ||
| to | destinatario previsto | ||
| value | cantidad prevista en wei | ||
| maxFeePerGas | dentro de tu política de comisiones | ||
| hash de transmisión | visible en el explorador |
Si alguna fila falla, no transmitas. Investiga el paso de construcción o el canal de transferencia antes de reintentar. Esta tabla también sirve como registro de auditoría para operaciones de firma en frío, lo cual es útil cuando más de una persona participa en el movimiento de fondos.
Solución de problemas comunes
La mayoría de los fallos en un flujo hot-versus-cold no son criptográficos. Son errores de coordinación entre el lado online y el offline. El más común es un nonce obsoleto: la máquina online construye una transacción, el firmante offline tarda en aprobarla y otra transacción de la misma cuenta se confirma primero. La transacción firmada entonces se revierte o se descarta. Reconstruye con un nonce nuevo en lugar de reutilizar el payload antiguo.
Un segundo fallo común es una discrepancia en el ID de cadena, que ocurre cuando la máquina online apunta a un endpoint de testnet mientras el firmante asume mainnet, o cuando una wallet multicadena usa por defecto la red incorrecta. Siempre lee eth_chainId del endpoint que estás usando realmente y compáralo con el ID de cadena incrustado en la transacción firmada.
Un tercer fallo son parámetros de comisión demasiado bajos para la tarifa base actual, lo que deja la transacción pendiente. Verifica la tarifa base del último bloque antes de firmar, y prefiere los campos EIP-1559 sobre el precio de gas legacy en redes que los soportan. Si operas a escala, un endpoint confiable importa aquí; consulta las páginas de opciones de precios RPC y nodo dedicado para saber cómo se aprovisionan el rendimiento y la consistencia.
- Colisión de nonce: reconstruye el payload después de cualquier transacción competidora.
- Discrepancia de ID de cadena: lee el ID de cadena del endpoint en vivo, no de memoria.
- Transacción atascada: verifica la tarifa base y la tarifa de prioridad contra el último bloque.
- Firma rechazada por el nodo: confirma que la dirección del firmante coincide con el remitente en el payload.
Limitaciones y compensaciones
El almacenamiento en frío no es una garantía. Desplaza el riesgo de atacantes remotos al acceso físico, la higiene de las copias de seguridad y el error del usuario. Un firmante correctamente aislado de la red (air-gapped) con una frase de recuperación fotografiada y almacenada en una unidad en la nube no es significativamente frío. De manera similar, una hot wallet usada con un keystore respaldado por hardware y límites de sesión estrictos puede ser más segura que una configuración en frío mal operada.
Las hot wallets siguen siendo la herramienta adecuada para saldos pequeños, trading frecuente, estrategias automatizadas e interacción con contratos inteligentes donde la latencia importa. Las cold wallets son la herramienta adecuada para tenencias de largo horizonte y para cualquier clave cuyo compromiso sería catastrófico. La mayoría de las operaciones reales usan ambas: una hot wallet para liquidez diaria y una cold wallet para reservas, con una política explícita sobre cómo se mueve el valor entre ellas.
También hay una advertencia de medición. Las afirmaciones de que un tipo de wallet es 'más seguro' no son directamente medibles sin definir la amenaza y el atacante. Lo que sí puedes medir es si tu flujo de firma produce transacciones que coinciden con tu intención, y si tu endpoint devuelve un estado de cadena consistente durante ese flujo.
Próximos pasos para un flujo de custodia reproducible
Comienza documentando el límite de firma para cada clave que controlas: qué máquina la contiene, qué máquina construye las transacciones y cómo cruza el payload entre ellas. Luego ejecuta el procedimiento de construir-firmar-verificar anterior en una testnet hasta que sea aburrido. Solo después deberías mover valor real.
Para equipos, agrega un segundo revisor a la tabla de verificación y exige que la tabla se adjunte a cualquier transacción firmada en frío. Para desarrolladores, integra el paso de verificación en tus scripts de despliegue para que un ID de cadena o destinatario que no coincida haga fallar la compilación en lugar de llegar a la red. Si necesitas un endpoint confiable para el lado online del flujo, la guía de endpoints RPC multicadena y la guía de alojamiento de nodos blockchain cubren el lado operativo, y la guía de endpoints RPC explica cómo probar uno antes de depender de él.
- Anota la ubicación de la clave y la ruta de firma antes de financiarla.
- Prueba el ciclo completo de construir-firmar-verificar en una testnet con una clave desechable.
- Conserva la tabla de verificación como registro de auditoría para transacciones firmadas en frío.
- Reevalúa la división cada vez que cambien los saldos, el tamaño del equipo o la frecuencia de transacciones.