Cómo enviar transacciones cripto en JavaScript: Bitcoin, Ethereum, Litecoin, Dogecoin y más con ChainGate

Una guía práctica en JavaScript para enviar Bitcoin, Ethereum, Avalanche, Litecoin, Dogecoin y Bitcoin Cash desde un solo SDK — por ChainGate.

Bitcoin, Ethereum, Avalanche, Litecoin, Dogecoin and Bitcoin Cash — +6 chains, one JavaScript transfer() call

Leer datos on-chain desde JavaScript siempre ha sido la mitad incómoda del trabajo. Sacar un balance limpio de Bitcoin — saldo confirmado y pendiente, tal y como lo enseña un explorer — implica normalmente correr tu propio indexer o pelearte con una API pública inestable. Listar los NFTs y tokens que una dirección de Ethereum o EVM tiene obliga a coser tres o cuatro endpoints de explorer, cada uno con su propio schema, sus rate limits y campos que faltan. Y en cuanto quieres realmente hacer broadcast de una transacción, vuelves a bitcoinjs-lib para Bitcoin, ethers o viem para el lado EVM, y un fork medio mantenido para Dogecoin y Litecoin. ChainGate cubre todo eso desde un único SDK.

ChainGate condensa todo eso en un único SDK de JavaScript. Construido como un SDK multi-chain para wallets, la misma forma de llamada envía Bitcoin, Ethereum, Avalanche (y cualquier cadena EVM), Litecoin, Dogecoin y Bitcoin Cash, lee balances de direcciones y lista los assets que hay en cualquier dirección. El mismo paquete corre en Node.js y en el navegador sin cambios de código — todas las primitivas de crypto vienen nativas, sin polyfills.

Aquí tienes el envío básico en cinco líneas. El resto de la guía recorre el mismo patrón en cada cadena y luego añade estimación de fees y consulta de balance antes de hacer broadcast:

import { ChainGate, importWallet } from 'chaingate';

const cg = new ChainGate({ apiKey: 'your-api-key' });
const wallet = importWallet({ phrase: process.env.MNEMONIC });
const btc = cg.connect(cg.networks.bitcoin, wallet);

const tx = await btc.transfer(cg.networks.bitcoin.amount('0.001'), 'bc1qRecipient...');
await tx.signAndBroadcast();

La versión corta

  • Un SDK, todas las cadenas. La misma API transfer() cubre Bitcoin, Ethereum, Avalanche, Litecoin, Dogecoin, Bitcoin Cash y cualquier red EVM-compatible.
  • Funciona sin cambios en Node.js 22+ y navegadores modernos — sin configurar polyfills de Buffer ni de crypto.
  • Fees, balance y broadcast vienen integrados — no necesitas una segunda librería para nada de esto.
  • La firma siempre ocurre localmente. Tu clave privada no sale del SDK.

1. Setup: instalar e inicializar ChainGate

Elige tu gestor de paquetes. El SDK se publica como un único paquete de npm — instálalo una sola vez y podrás enviar Bitcoin, Ethereum y el resto desde el mismo import, con los tipos ya incluidos:

# pick your package manager
npm install chaingate
pnpm add chaingate
yarn add chaingate

Después consigue una API key gratuita en el dashboard de ChainGate. Registrarse lleva un minuto y el tier gratuito es suficiente para llevar un prototipo a producción. Inicializa el cliente con esa key una sola vez y reutilízalo para cada cadena:

import { ChainGate } from 'chaingate';

const cg = new ChainGate({ apiKey: process.env.CHAINGATE_API_KEY });

Mismo código en Node.js y en el navegador. El paquete funciona en Node 22+, navegadores modernos (Vite, Next.js, Webpack 5+) y Bun de fábrica, así que la misma librería blockchain de Node.js que importas en el servidor es la que corre en el navegador del usuario. Las primitivas de crypto como Buffer y randomBytes vienen nativas dentro del SDK — nada que polyfillear, nada que configurar.

Los tipos de TypeScript van incluidos. No existe ningún paquete @types/chaingate porque no hace falta — todas las clases, métodos y eventos públicos están tipados en el origen.

2. Enviar Bitcoin, Litecoin, Dogecoin y Bitcoin Cash

Bitcoin, Litecoin, Dogecoin y Bitcoin Cash comparten el mismo modelo contable — UTXOs. El balance de una wallet es la suma de las salidas no gastadas (unspent transaction outputs) bloqueadas en sus direcciones. Para enviar fondos, la wallet escoge UTXOs suficientes para cubrir el importe, construye una nueva transacción que los consume como inputs, envía al destinatario una salida, manda la diferencia a sí misma como output de cambio y paga un fee en satoshis por kilobyte (o el equivalente por cadena).

ChainGate gestiona cada paso de eso — selección de UTXOs, output de cambio, tier de fee y firma — detrás de una sola llamada a transfer(). Le pasas dirección destino y cantidad, te devuelve una transacción firmada lista para hacer broadcast.

Enviar Bitcoin desde Node.js o el navegador

El ejemplo base. Conecta tu wallet a la red de Bitcoin, crea la transferencia, elige un tier de fee y haz broadcast:

import { ChainGate, importWallet, NotEnoughFundsError } from 'chaingate';

const cg = new ChainGate({ apiKey: process.env.CHAINGATE_API_KEY });
const wallet = importWallet({ phrase: process.env.MNEMONIC });

const btc = cg.connect(cg.networks.bitcoin, wallet);
const amount = cg.networks.bitcoin.amount('0.001'); // 0.001 BTC

try {
  const tx = await btc.transfer(amount, 'bc1qRecipient...');
  tx.setFee(tx.recommendedFees().normal);

  const broadcast = await tx.signAndBroadcast();
  console.log('BTC txid:', broadcast.transactionId);
} catch (err) {
  if (err instanceof NotEnoughFundsError) {
    console.error('Not enough BTC for this transfer + fee');
  }
  throw err;
}

Para apuntar a testnet en desarrollo, cambia cg.networks.bitcoin por cg.networks.bitcoinTestnet — el resto del código se queda igual.

Enviar Litecoin desde Node.js o el navegador

Mismo código, distinta red. El diff frente al snippet de Bitcoin es literalmente un identificador:

const ltc = cg.connect(cg.networks.litecoin, wallet);
const amount = cg.networks.litecoin.amount('0.1'); // 0.1 LTC

const tx = await ltc.transfer(amount, 'ltc1qRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();

Litecoin usa direcciones SegWit ltc1q… por defecto y confirma bloques cada dos minutos y medio — unas cuatro veces más rápido que Bitcoin — lo que lo hace popular para pagos de bajo valor donde la velocidad de liquidación importa más que la profundidad de seguridad.

Enviar Dogecoin desde Node.js o el navegador

Mismo diff, distinta cadena:

const doge = cg.connect(cg.networks.dogecoin, wallet);
const amount = cg.networks.dogecoin.amount('100'); // 100 DOGE

const tx = await doge.transfer(amount, 'DRecipientAddress...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();

El ecosistema de JavaScript de Dogecoin se ha quedado atrás respecto al resto de la familia UTXO. La mayoría de forks populares de bitcoinjs-lib llevan años sin actualizar y las pocas alternativas mantenidas son privadas. ChainGate es una de las muy pocas opciones que ofrecen soporte de Dogecoin de calidad de producción en JavaScript hoy.

Enviar Bitcoin Cash desde Node.js o el navegador

Bitcoin Cash usa direcciones CashAddr por defecto, pero el SDK también acepta el formato legacy — puedes pegar cualquiera de los dos en transfer() y la red confirmará igual:

const bch = cg.connect(cg.networks.bitcoincash, wallet);
const amount = cg.networks.bitcoincash.amount('0.05'); // 0.05 BCH

// CashAddr or legacy — both work
const tx = await bch.transfer(amount, 'bitcoincash:qpRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();

La validación de direcciones es bidireccional: cg.networks.bitcoincash.isValidAddress() devuelve true para ambos formatos, así que no necesitas normalizar la entrada del usuario antes de enviar.

Enviar UTXO desde el navegador

Los cuatro snippets UTXO de arriba corren sin cambios en un bundle de Vite, Next.js o Webpack 5 — el mismo import funciona en cliente y la misma wallet firma localmente sin contactar con la API.

Una nota sobre seguridad: nunca cargues claves privadas de producción directamente en un frontend. O mantén la firma en el servidor y deja sólo el broadcast en el navegador, o haz que el usuario aporte la clave a través de un flujo tipo wallet que controle él.

3. Enviar Ethereum y cadenas EVM

El modelo de cuentas de Ethereum es distinto — no hay UTXOs, sólo una dirección con un balance y un nonce que crece monotónicamente. ChainGate esconde esa diferencia detrás de la misma llamada transfer() que usaste en Bitcoin. Las diferencias (gestión de nonce, base fee + tip de EIP-1559, gas limit) se calculan por ti:

const eth = cg.connect(cg.networks.ethereum, wallet);
const amount = cg.networks.ethereum.amount('0.01'); // 0.01 ETH

const tx = await eth.transfer(amount, '0xRecipient...');
tx.setFee(tx.recommendedFees().normal);

const broadcast = await tx.signAndBroadcast();
console.log('ETH txHash:', broadcast.transactionId);

Enviar Avalanche con Node.js o en el navegador

La C-Chain de Avalanche está cableada como red de primera clase junto a Ethereum — misma forma de llamada, misma wallet, cantidades en AVAX. El diff respecto al snippet de Ethereum es un solo identificador:

const avax = cg.connect(cg.networks.avalanche, wallet);
const amount = cg.networks.avalanche.amount('1'); // 1 AVAX

const tx = await avax.transfer(amount, '0xRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();

La C-Chain finaliza bloques en unos dos segundos y usa el mismo modelo de fees EIP-1559 que Ethereum, así que los envíos de AVAX se liquidan rápido y la salida de recommendedFees() tiene la misma forma. Eso convierte a Avalanche en una elección popular para pagos de alto throughput donde el gas de Ethereum dejaría al usuario fuera.

Misma wallet, mismo SDK, todas las cadenas EVM. Las URLs RPC alojadas cubren Polygon, Arbitrum, Base, BNB Chain y Sonic de fábrica; para cualquier otra red EVM — Optimism, Linea, Scroll, Gnosis, tu propio nodo, una testnet — pasa la URL JSON-RPC directamente a evmRpc(). Enviar POL en Polygon es un diff de una línea:

const polygon = cg.networks.evmRpc({
  rpcUrl: cg.rpcUrls.polygon,
  chainId: 137,
  name: 'Polygon',
  symbol: 'POL',
});

const pol = cg.connect(polygon, wallet);
const amount = polygon.amount('5'); // 5 POL

const tx = await pol.transfer(amount, '0xRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();

El nonce se gestiona automáticamente. El SDK lee el último nonce pendiente del RPC elegido antes de firmar, así que no tienes que llevar la cuenta tú. La construcción del fee va por defecto a EIP-1559 (maxFeePerGas + maxPriorityFeePerGas) en las cadenas que lo soportan y cae a gas pricing legacy en las que no.

El chainId que pasas a evmRpc() tiene que ser el que reporta el propio endpoint RPC, o el SDK firmará para la red equivocada. La lista de ID de cadena (chain ID) de las redes EVM tiene el número de Polygon, Arbitrum, Base, Optimism y otras 35 redes y testnets, y muestra cómo leerlo directamente desde cualquier URL RPC.

Si el nodo responde a un envío con "nonce too low", "replacement transaction underpriced" o "insufficient funds for gas * price + value", la guía de errores de transacción en Ethereum explica qué significa cada mensaje y cuáles cuestan gas.

El soporte de navegador es idéntico al de las cadenas UTXO. Este es el código que pondrías detrás de un botón 'Send' en una dApp.

4. Estimar fees antes de enviar

Muestra siempre el fee antes de hacer broadcast. A los usuarios no les gustan las sorpresas, las transacciones atascadas cuestan tiempo y un paso de 'revisa y confirma' convierte un flujo de UI de un solo disparo en algo en lo que el usuario realmente confía.

ChainGate expone el mismo método recommendedFees() en cada cadena — devuelve cuatro tiers (low, normal, high, maximum) con los números que cada tier necesita. En Bitcoin obtienes una tasa feePerKbSat y el total estimado en satoshis; en Ethereum y otras cadenas EVM obtienes maxFeePerGas y maxPriorityFeePerGas de EIP-1559 como bigint en wei, más una ventana estimada de confirmación:

// Bitcoin — fee rate in sat/kB, plus the total estimated fee in satoshis
const btcTx = await btc.transfer(cg.networks.bitcoin.amount('0.01'), 'bc1q...');
const btcFees = btcTx.recommendedFees();

console.table({
  low:    { satPerKb: btcFees.low.feePerKbSat,    feeSat: btcFees.low.estimatedFeeSat,    enough: btcFees.low.enoughFunds },
  normal: { satPerKb: btcFees.normal.feePerKbSat, feeSat: btcFees.normal.estimatedFeeSat, enough: btcFees.normal.enoughFunds },
  high:   { satPerKb: btcFees.high.feePerKbSat,   feeSat: btcFees.high.estimatedFeeSat,   enough: btcFees.high.enoughFunds },
});

// Ethereum — EIP-1559 numbers in gwei
const ethTx = await eth.transfer(cg.networks.ethereum.amount('0.1'), '0x...');
const ethFees = ethTx.recommendedFees();
const toGwei = (wei) => Number(wei) / 1e9;

console.table({
  low:     { gwei: toGwei(ethFees.low.maxFeePerGas),     sec: ethFees.low.estimatedConfirmationSecs },
  normal:  { gwei: toGwei(ethFees.normal.maxFeePerGas),  sec: ethFees.normal.estimatedConfirmationSecs },
  high:    { gwei: toGwei(ethFees.high.maxFeePerGas),    sec: ethFees.high.estimatedConfirmationSecs },
  maximum: { gwei: toGwei(ethFees.maximum.maxFeePerGas), sec: ethFees.maximum.estimatedConfirmationSecs },
});

El patrón que funciona en producción: construye la transacción, llama a recommendedFees(), enseña los cuatro tiers al usuario, deja que elija y luego llama a signAndBroadcast(). El objeto de la transacción se mantiene en memoria entre estimar y hacer broadcast — no hace falta reconstruir nada.

5. Consultar balance antes de enviar

La consulta de balance debe ir al principio de cualquier flujo de envío. Detecta errores de tecleo, evita gastar gas en una transacción que va a fallar y le da al usuario un número que puede contrastar. Cada cadena expone el mismo one-liner — pasas una dirección (o usas la wallet conectada) y obtienes el balance confirmado y, en las cadenas que lo soportan, el saldo pendiente:

// Ethereum — confirmed balance + live USD price
const eth = cg.connect(cg.networks.ethereum, wallet);
const ethBalance = await eth.addressBalance();
const ethUsd = await ethBalance.confirmed.toCurrency('usd');
console.log(`${ethBalance.confirmed.base()} ETH ($${ethUsd})`);

// Bitcoin — confirmed and unconfirmed split
const btc = cg.connect(cg.networks.bitcoin, wallet);
const btcBalance = await btc.addressBalance();
console.log(`Confirmed:   ${btcBalance.confirmed.base()} BTC`);
console.log(`Unconfirmed: ${btcBalance.unconfirmed.base()} BTC`);

Combinada con la estimación de fee, la pre-flight completa son tres llamadas — balance, fee y luego send. Conéctalas una vez y cada flujo de envío de tu app obtiene la misma validación previa gratis, ya consultes un balance cripto desde Node.js en el servidor o desde el navegador.

6. ChainGate vs bitcoinjs-lib, ethers y otras alternativas

ChainGate no busca reemplazar todas las librerías de bajo nivel anteriores. Cada una tiene un trabajo claro. Así se ven los trade-offs cuando necesitas enviar transacciones en más de una cadena:

CapacidadChainGatebitcoinjs-libethers / viemImplementación propia
Cadenas soportadasBTC, ETH, AVAX+EVM, LTC, DOGE, BCHSolo BitcoinSolo EVM1 por librería
Líneas para enviar BTC~5~40+n/avaría
Listo para navegadorSí — de fábricaRequiere polyfillsSíVaría
Estimación de fees integradaSíNoParcialNo
Tipos de TypeScriptFirst-classComunidadSíVaría

Quédate con bitcoinjs-lib o ethers cuando necesites control byte a byte sobre la forma de la transacción, o cuando tu app hable con una sola cadena y el tamaño del bundle sea el factor decisivo. Cambia a ChainGate cuando estés haciendo malabares con más de una cadena, contra el reloj o llevando un equipo pequeño que no puede permitirse especializarse por blockchain.

7. Precios y tier gratuito

El tier gratuito es generoso a propósito — todos los planes, incluido el gratuito, traen el SDK completo: balances, listados de assets, estimación de fees, firma y broadcast en cada cadena que soportamos. Es suficiente para enviar un prototipo, llevar un proyecto personal o poner una herramienta interna en producción. Sin tarjeta al registrarse.

Los planes de pago suben los rate limits y añaden soporte prioritario y capacidad de burst. Cambiar de plan se hace con un clic dentro del dashboard.

Mira la página de precios para los números actualizados y la ruta de upgrade.

8. Por dónde seguir

Ya tienes el modelo: un SDK, varias cadenas y la misma forma de cinco líneas en cada una. Desde aquí, la documentación cubre el resto de la superficie — firma de mensajes, transferencias de ERC-20, transferencias de NFT, historial de transacciones decodificado. Los ejemplos en GitHub son un buen punto de partida con recetas end-to-end que puedes copiar. Y el dashboard de la API está a un clic cuando quieras conseguir una key y enviar algo a producción. Si quieres profundizar en la mecánica detrás de un fee, la guía complementaria sobre el gas de Ethereum desgrana de dónde salen los números de recommendedFees() de verdad. La guía sobre hashes de transacción explica qué aspecto tiene en cada cadena el transactionId que te devuelve signAndBroadcast() y cómo comprobarlo en un explorador de bloques.

Preguntas frecuentes

¿ChainGate guarda mis claves privadas?
No. La construcción de la wallet, la derivación de claves y la firma se ejecutan localmente en tu dispositivo. La API de ChainGate sólo gestiona las llamadas de lectura y hace broadcast de transacciones ya firmadas — tu seed phrase y tu clave privada no salen del SDK.
¿Puedo usar ChainGate en el navegador?

Sí — todos los ejemplos de esta guía corren sin cambios en un bundle de Vite, Next.js o Webpack 5. El SDK incluye su propia crypto compatible con navegadores, así que no hay nada extra que instalar.

¿Qué redes están soportadas?

ChainGate ofrece soporte de API completa para Bitcoin, Bitcoin Testnet, Litecoin, Dogecoin, Bitcoin Cash, Ethereum y Avalanche, endpoints JSON-RPC alojados para Polygon, Arbitrum, Base, BNB Chain y Sonic, y soporte de bring-your-own-RPC para cualquier otra cadena EVM — incluyendo Optimism, Linea, Scroll, Gnosis, testnets y nodos auto-alojados. La matriz completa está en la documentación.

¿Puedo enviar transacciones en testnet?

Sí — no hay flag especial de testnet. Pasa la red de testnet al mismo cg.connect() que usas en mainnet: para Bitcoin es cg.networks.bitcoinTestnet; para una testnet EVM, pasa la URL JSON-RPC y el chain ID de la testnet a cg.networks.evmRpc({ rpcUrl, chainId, name, symbol }). El resto del código es idéntico a los ejemplos de mainnet de esta guía.

¿Qué pasa si una transacción falla?

El SDK expone un error tipado que puedes capturar y manejar. La mayoría de incidencias previas al broadcast son recuperables — el objeto de la transacción se reconstruye con un nonce nuevo o una nueva selección de UTXOs en el reintento, así que un segundo intento suele pasar.

¿ChainGate es open source?

El SDK de JavaScript que firma y hace broadcast en tu máquina está publicado en GitHub bajo licencia MIT — léelo, cópialo a tu repo o hazle un fork. La API de ChainGate detrás es un servicio gestionado que se encarga del resto por ti. La mayoría de los equipos usan el SDK open source con la API alojada, pero puedes apuntar el SDK a cualquier URL RPC que controles si prefieres alojar tu propia infraestructura.

¿Por qué ChainGate necesita una API key?
La API key autentica las llamadas al backend de ChainGate, que es lo que cubre las partes que el SDK no puede resolver por sí solo: procesa las peticiones JSON-RPC a la cadena que estés usando, lee UTXOs y balances y hace broadcast de las transacciones firmadas. La firma sigue ocurriendo localmente con tu wallet; la key sólo autoriza el tráfico de lectura y de broadcast que pasa por la infraestructura de ChainGate. El plan gratuito te genera una key desde el dashboard sin tarjeta de crédito.

Construye con ChainGate

Lleva estas ideas al código — ChainGate te ofrece wallets, transacciones y RPC gestionado para Ethereum y más de 20 blockchains con una sola librería.