Come inviare transazioni cripto in JavaScript: Bitcoin, Ethereum, Litecoin, Dogecoin e altro con ChainGate

Una guida pratica in JavaScript per inviare Bitcoin, Ethereum, Avalanche, Litecoin, Dogecoin e Bitcoin Cash da un solo SDK — di ChainGate.

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

Leggere dati on-chain da JavaScript è sempre stata la metà scomoda del lavoro. Ottenere un saldo Bitcoin pulito — saldo confermato e in attesa, come lo mostra un explorer — di solito significa tenere acceso il proprio indexer o picchiare contro un'API pubblica instabile. Listare gli NFT e i token che un indirizzo Ethereum o EVM detiene richiede di mettere insieme tre o quattro endpoint di explorer, ognuno con il proprio schema, i propri rate limit e campi che mancano. E nel momento in cui vuoi davvero fare broadcast di una transazione, sei di nuovo a bitcoinjs-lib per Bitcoin, ethers o viem sul lato EVM e un fork tenuto in piedi a metà per Dogecoin e Litecoin. ChainGate copre tutto questo da un unico SDK.

ChainGate raccoglie tutto questo lavoro in un unico SDK in JavaScript. Costruito come un SDK multi-chain per wallet, la stessa forma di chiamata invia Bitcoin, Ethereum, Avalanche (e ogni catena EVM), Litecoin, Dogecoin e Bitcoin Cash, legge i saldi degli indirizzi e lista gli asset che ci sono su qualsiasi indirizzo. Lo stesso pacchetto gira in Node.js e nel browser senza modifiche al codice — ogni primitiva crypto viene fornita in modo nativo, senza polyfill.

Qui sotto l'invio base in cinque righe. Il resto della guida ripercorre lo stesso pattern su ogni catena, poi aggiunge stima delle fee e controllo del saldo prima del 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 versione breve

  • Un SDK, tutte le catene. La stessa API transfer() copre Bitcoin, Ethereum, Avalanche, Litecoin, Dogecoin, Bitcoin Cash e ogni rete compatibile con EVM.
  • Gira invariata su Node.js 22+ e nei browser moderni — nessun setup di polyfill per Buffer o crypto.
  • Fee, saldo e broadcast sono già integrati — non serve una seconda libreria per nessuna di queste cose.
  • La firma avviene sempre in locale. La tua chiave privata non lascia l'SDK.

1. Setup: installare e inizializzare ChainGate

Scegli il tuo gestore di pacchetti. L'SDK è pubblicato come unico pacchetto npm — installalo una volta e puoi inviare Bitcoin, Ethereum e il resto dallo stesso import, tipi inclusi:

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

Poi prendi una API key gratuita dalla dashboard di ChainGate. L'iscrizione richiede un minuto e il piano gratuito è abbastanza per portare un prototipo in produzione. Inizializza il client con quella key una sola volta e riutilizzalo per ogni catena:

import { ChainGate } from 'chaingate';

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

Stesso codice in Node.js e nel browser. Il pacchetto funziona su Node 22+, browser moderni (Vite, Next.js, Webpack 5+) e Bun out of the box, quindi la stessa libreria blockchain Node.js che importi sul server è quella che gira nel browser dell'utente. Le primitive crypto come Buffer e randomBytes sono native dentro l'SDK — niente da polyfillare, niente da configurare.

I tipi TypeScript sono inclusi. Non esiste un pacchetto @types/chaingate perché non serve — ogni classe, metodo ed evento pubblico è completamente tipato all'origine.

2. Inviare Bitcoin, Litecoin, Dogecoin e Bitcoin Cash

Bitcoin, Litecoin, Dogecoin e Bitcoin Cash condividono lo stesso modello contabile — gli UTXO. Il saldo di un wallet è la somma degli output di transazione non spesi (unspent transaction outputs) bloccati ai suoi indirizzi. Per inviare fondi, il wallet sceglie abbastanza UTXO per coprire l'importo, costruisce una nuova transazione che li consuma come input, manda al destinatario un output, restituisce il resto a sé stesso come output di change e paga una fee in satoshi per kilobyte (o l'equivalente per ciascuna catena).

ChainGate gestisce ogni passaggio — selezione degli UTXO, output di change, tier di fee e firma — dietro una sola chiamata transfer(). Gli passi un indirizzo di destinazione e un importo, ti restituisce una transazione firmata pronta per il broadcast.

Inviare Bitcoin da Node.js o dal browser

L'esempio base. Connetti il wallet alla rete Bitcoin, crea il transfer, scegli un tier di fee e fai il 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;
}

Per puntare alla testnet in fase di sviluppo, sostituisci cg.networks.bitcoin con cg.networks.bitcoinTestnet — il resto del codice resta identico.

Inviare Litecoin da Node.js o dal browser

Stesso codice, rete diversa. Il diff rispetto allo snippet di Bitcoin è letteralmente un identificatore:

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 indirizzi SegWit ltc1q… per default e conferma blocchi ogni due minuti e mezzo — circa quattro volte più veloce di Bitcoin —, motivo per cui è una scelta popolare per pagamenti di piccolo valore in cui la velocità di settlement conta più della profondità di sicurezza.

Inviare Dogecoin da Node.js o dal browser

Stesso diff, catena diversa:

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();

L'ecosistema JavaScript di Dogecoin è rimasto indietro rispetto al resto della famiglia UTXO. La maggior parte dei fork popolari di bitcoinjs-lib non viene aggiornata da anni e le poche alternative mantenute sono private. ChainGate è una delle pochissime opzioni che oggi offrano supporto Dogecoin di livello produzione in JavaScript.

Inviare Bitcoin Cash da Node.js o dal browser

Bitcoin Cash usa per default indirizzi CashAddr ma l'SDK accetta anche il formato legacy — puoi incollare entrambi in transfer() e la rete confermerà comunque:

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 validazione degli indirizzi è bidirezionale: cg.networks.bitcoincash.isValidAddress() restituisce true per entrambi i formati, quindi non devi normalizzare l'input dell'utente prima di inviare.

Inviare UTXO dal browser

I quattro snippet UTXO qui sopra girano senza modifiche in un bundle Vite, Next.js o Webpack 5 — lo stesso import funziona lato client e lo stesso wallet firma localmente senza contattare l'API.

Una nota di sicurezza: non caricare mai chiavi private di produzione direttamente in un frontend. O tieni la firma sul server e dal browser parte solo il broadcast, oppure fai sì che l'utente fornisca la chiave attraverso un flusso in stile wallet che controlli lui.

3. Inviare Ethereum e le catene EVM

Il modello a conti di Ethereum è diverso — niente UTXO, solo un indirizzo con un saldo e un nonce monotono crescente. ChainGate nasconde la differenza dietro la stessa chiamata transfer() che hai usato su Bitcoin. Le differenze (gestione del nonce, base fee + tip EIP-1559, gas limit) sono calcolate per te:

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);

Inviare Avalanche con Node.js o nel browser

La C-Chain di Avalanche è cablata come rete di prima classe accanto a Ethereum — stessa forma di chiamata, stesso wallet, importi denominati in AVAX. Il diff rispetto allo snippet Ethereum è un solo identificatore:

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 finalizza i blocchi in circa due secondi e usa lo stesso modello di fee EIP-1559 di Ethereum, quindi i trasferimenti di AVAX si chiudono in fretta e l'output di recommendedFees() ha la stessa forma. Tutto questo rende Avalanche una scelta popolare per pagamenti ad alto throughput dove il gas di Ethereum metterebbe l'utente fuori gioco.

Stesso wallet, stesso SDK, ogni catena EVM. Le URL RPC ospitate coprono Polygon, Arbitrum, Base, BNB Chain e Sonic out of the box; per qualsiasi altra rete EVM — Optimism, Linea, Scroll, Gnosis, il tuo nodo, una testnet — passa direttamente la URL JSON-RPC a evmRpc(). Inviare POL su Polygon è un diff di una riga:

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();

La gestione del nonce è automatica. L'SDK legge l'ultimo nonce pendente dall'RPC scelto prima di firmare, quindi non devi gestirlo a mano. La costruzione delle fee usa di default EIP-1559 (maxFeePerGas + maxPriorityFeePerGas) sulle catene che lo supportano e ricade sul gas pricing legacy su quelle che non lo fanno.

Il chainId che passi a evmRpc() deve essere quello che l'endpoint RPC stesso riporta, altrimenti l'SDK firmerà per la rete sbagliata. La lista dei chain ID EVM riporta il numero di Polygon, Arbitrum, Base, Optimism e altre 35 reti e testnet, e mostra come leggerlo direttamente da qualsiasi URL RPC.

Se il nodo risponde a una trasmissione con "nonce too low", "replacement transaction underpriced" o "insufficient funds for gas * price + value", la guida agli errori delle transazioni Ethereum spiega cosa significa ogni messaggio e quali costano gas.

Il supporto del browser è identico a quello delle catene UTXO. È lo stesso codice che metteresti dietro a un bottone 'Send' in una dApp.

4. Stimare le fee prima dell'invio

Mostra sempre la fee prima del broadcast. Agli utenti non piacciono le sorprese, le transazioni bloccate costano tempo e un passaggio di 'review and confirm' trasforma un flusso UI a colpo unico in qualcosa di cui l'utente si fida davvero.

ChainGate espone lo stesso metodo recommendedFees() su ogni catena — restituisce quattro tier (low, normal, high, maximum) con i numeri che servono a ciascuno. Su Bitcoin ottieni un tasso feePerKbSat e il totale stimato in satoshi; su Ethereum e altre catene EVM ottieni maxFeePerGas e maxPriorityFeePerGas EIP-1559 come bigint in wei più una finestra stimata di conferma:

// 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 },
});

Il pattern che funziona in produzione: costruisci la transazione, chiama recommendedFees(), mostra i quattro tier all'utente, lascialo scegliere e poi chiama signAndBroadcast(). L'oggetto della transazione resta in memoria tra stima e broadcast — non serve ricostruire nulla.

5. Controllare il saldo prima dell'invio

Un controllo del saldo merita di stare in cima a qualsiasi flusso di invio. Intercetta gli errori di battitura, evita di sprecare gas in una transazione destinata a fallire e dà all'utente un numero da confrontare. Ogni catena espone lo stesso one-liner — passi un indirizzo (o usi il wallet connesso) e ottieni il saldo confermato e, sulle catene che lo supportano, la quota non confermata:

// 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`);

Insieme alla stima della fee, il pre-flight completo sono tre chiamate — saldo, fee, poi invio. Collegale una volta sola e ogni flusso di invio della tua app ottiene gratis la stessa validazione preventiva, sia che tu controlli un saldo cripto da Node.js sul server o dal browser.

6. ChainGate vs bitcoinjs-lib, ethers e altre alternative

ChainGate non vuole rimpiazzare ogni libreria di basso livello che lo precede. Ognuna ha un compito chiaro. Ecco come si distribuiscono i trade-off quando devi inviare transazioni su più di una catena:

CapacitàChainGatebitcoinjs-libethers / viemImplementazione propria
Catene supportateBTC, ETH, AVAX+EVM, LTC, DOGE, BCHSolo BitcoinSolo EVM1 per libreria
Righe per inviare BTC~5~40+n/avaria
Pronto per il browserSì — out of the boxServono polyfillSìVaria
Stima delle fee integrataSìNoParzialeNo
Tipi TypeScriptFirst-classComunitàSìVaria

Resta su bitcoinjs-lib o ethers quando ti serve un controllo byte per byte sulla forma della transazione, o quando la tua app parla con una sola catena e la dimensione del bundle è il fattore decisivo. Passa a ChainGate quando stai gestendo più catene contemporaneamente, rincorri una scadenza o porti avanti un piccolo team che non può permettersi una specializzazione per blockchain.

7. Prezzi e piano gratuito

Il piano gratuito è generoso di proposito — ogni piano, incluso quello gratuito, include l'SDK completo: saldi, listing degli asset, stima delle fee, firma e broadcast su ogni catena che supportiamo. È abbastanza per portare in produzione un prototipo, mandare avanti un progetto personale o un tool interno. Niente carta di credito al momento dell'iscrizione.

I piani a pagamento alzano i rate limit e aggiungono supporto prioritario e capacità di burst. Il passaggio a un piano superiore è un clic dentro la dashboard.

Vedi la pagina dei prezzi per i numeri aggiornati e il percorso di upgrade.

8. Da dove proseguire

Hai il modello: un SDK, più catene e la stessa forma da cinque righe su ognuna. Da qui, la documentazione copre il resto della superficie — firma di messaggi, trasferimenti ERC-20, movimenti di NFT, cronologia transazioni decodificata. Gli esempi su GitHub sono un buon punto di partenza per ricette end-to-end da copiare. E la dashboard dell'API è a un clic quando vuoi prendere una key e mandare qualcosa in produzione. Per scendere nei meccanismi che stanno dietro a una fee, la guida complementare sul gas di Ethereum smonta da dove vengono davvero i numeri di recommendedFees(). La guida agli hash di transazione spiega che aspetto ha su ogni chain il transactionId restituito da signAndBroadcast() e come verificarlo su un block explorer.

Domande frequenti

ChainGate trattiene le mie chiavi private?
No. La costruzione del wallet, la derivazione delle chiavi e la firma vengono eseguite localmente sul tuo dispositivo. L'API di ChainGate gestisce solo le chiamate in lettura e il broadcast di transazioni già firmate — la tua seed phrase e la tua chiave privata non escono mai dall'SDK.
Posso usare ChainGate nel browser?

Sì — ogni esempio in questa guida gira invariato in un bundle Vite, Next.js o Webpack 5. L'SDK include una propria implementazione crypto compatibile con il browser, quindi non c'è nulla di extra da installare.

Quali reti sono supportate?

ChainGate offre supporto API completo per Bitcoin, Bitcoin Testnet, Litecoin, Dogecoin, Bitcoin Cash, Ethereum e Avalanche, endpoint JSON-RPC ospitati per Polygon, Arbitrum, Base, BNB Chain e Sonic, e supporto bring-your-own-RPC per ogni altra catena EVM — comprese Optimism, Linea, Scroll, Gnosis, testnet e nodi self-hosted. La matrice completa è nella documentazione.

Posso inviare transazioni in testnet?

Sì — non c'è nessun flag testnet speciale. Passa la rete testnet alla stessa chiamata cg.connect() che usi in mainnet: per Bitcoin è cg.networks.bitcoinTestnet; per una testnet EVM, passa la URL JSON-RPC e il chain ID della testnet a cg.networks.evmRpc({ rpcUrl, chainId, name, symbol }). Il resto del codice è identico agli esempi mainnet di questa guida.

Cosa succede se una transazione fallisce?

L'SDK espone un errore tipato che puoi catturare e gestire. La maggior parte dei problemi pre-broadcast è recuperabile — l'oggetto della transazione viene ricostruito con un nonce nuovo o una nuova selezione di UTXO al retry, quindi un secondo tentativo di solito passa.

ChainGate è open source?

L'SDK JavaScript che firma e fa broadcast sulla tua macchina è pubblicato su GitHub con licenza MIT — leggi il sorgente, copialo nel tuo repo o crea un fork. L'API di ChainGate dietro è un servizio gestito che si occupa del resto per te. La maggior parte dei team usa l'SDK open source con l'API ospitata, ma puoi puntare l'SDK a qualsiasi URL RPC che controlli se preferisci ospitare la tua infrastruttura.

Perché ChainGate ha bisogno di una API key?
La API key autentica le chiamate al backend di ChainGate, che è ciò che copre le parti che l'SDK non può fare da solo — elabora le richieste JSON-RPC verso la catena che stai usando, legge UTXO e balance e fa broadcast delle transazioni firmate. La firma avviene comunque in locale con il tuo wallet; la key autorizza solo il traffico di lettura e di broadcast che passa per l'infrastruttura di ChainGate. Il piano gratuito ti rilascia una key dal dashboard senza carta di credito.

Costruisci con ChainGate

Porta queste idee nel codice — ChainGate ti dà wallet, transazioni e RPC gestito per Ethereum e più di 20 altre blockchain in un’unica libreria.