Skip to main content

Transfer funds

ChainGate lets you transfer funds from your wallet to any valid recipient address across supported blockchains. Whether you're using a UTXO-based chain (like Bitcoin, Litecoin, or Dogecoin) or an EVM-based chain (like Ethereum), the API provides a consistent flow: create a transfer, review fees, and sign + broadcast.

Transaction fees

Every blockchain transaction incurs a fee. Always ensure you have enough funds to cover both the transaction amount and its fee. ChainGate estimates fees for you via recommendedFees() — gas prices on EVM chains, and sat/vB rates on UTXO chains.

Encrypted wallets

If your wallet is encrypted, the askForPassword callback will be triggered when signing the transaction during signAndBroadcast().

Basic transaction flow

import { newWallet, ChainGate } from 'chaingate'

const { wallet } = newWallet()
const cg = new ChainGate()

// 1. Connect wallet to a network
const bitcoin = cg.connect(cg.networks.bitcoin, wallet)

// 2. Create an amount
const amount = cg.networks.bitcoin.amount('0.001')

// 3. Create a transfer (fetches fees, balance, and UTXOs automatically)
const tx = await bitcoin.transfer(amount, 'bc1q...')

// 4. Review recommended fees
const fees = tx.recommendedFees()
console.log('Normal fee enough funds?', fees.normal.enoughFunds)

// 5. Set fee tier and sign + broadcast
tx.setFee(fees.normal)
const broadcasted = await tx.signAndBroadcast()
console.log('TX ID:', broadcasted.transactionId)

// 6. Wait for confirmation (optional)
const cancel = broadcasted.onConfirmed((details) => {
console.log('Confirmed in block', details.blockHeight)
})
// Stop waiting at any time:
// cancel()

The flow is identical across chains. Swap cg.networks.bitcoin for cg.networks.litecoin, cg.networks.dogecoin, cg.networks.bitcoincash, or cg.networks.ethereum — every method (amount(), transfer(), recommendedFees(), signAndBroadcast()) has the same signature everywhere.

Sending an amount specified in fiat

If you prefer to send the fiat equivalent of an amount (e.g., "$25 worth of BTC"):

// Convert 25 USD to BTC at current rates
const amount = await cg.networks.bitcoin.amountFromCurrency('usd', 25)

const tx = await bitcoin.transfer(amount, 'bc1q...')
const fees = tx.recommendedFees()
tx.setFee(fees.normal)
const broadcasted = await tx.signAndBroadcast()

Supported fiat currencies

amountFromCurrency() accepts more than 120 fiat currencies identified by their ISO 4217 3-letter codes:

usd, eur, gbp, jpy, cny, aud, cad, chf, inr, brl, mxn, and many more.

// Send the equivalent of 100 EUR in ETH
const amount = await cg.networks.ethereum.amountFromCurrency('eur', 100)

When you create a transfer, ChainGate automatically fetches recommended fee levels. Use tx.recommendedFees() to inspect them:

Available fee tiers

TierDescription
lowCheapest option, but slower confirmation
normalA balanced option for typical transactions
highHigher fee for faster confirmation
maximumHighest fee for the fastest possible confirmation

Each tier includes:

  • enoughFunds (boolean) — Whether the wallet can cover the amount + fee.
  • missingFunds (bigint | null, optional) — When enoughFunds is false, the exact amount of additional funds needed for the transaction to be broadcastable at this fee. Reported in satoshis on UTXO chains and in wei on EVM chains. May be null on UTXO when the gap cannot be estimated.
  • estimatedConfirmationSecs (number) — Estimated seconds until confirmation.

UTXO-specific properties

  • feePerKbSat (bigint) — Fee rate in satoshis per kilobyte.
  • estimatedFeeSat (bigint | null) — Estimated total fee in satoshis.

EVM-specific properties

  • maxFeePerGas (bigint) — Maximum fee per gas in wei (divide by 1_000_000_000n for gwei).
  • maxPriorityFeePerGas (bigint) — Priority fee (tip) per gas in wei.
  • gasEstimationFailed (boolean, optional)true when on-chain gas estimation failed (typically because the wallet can't cover the simulated call) and a built-in heuristic has been used instead. The enoughFunds and missingFunds values still reflect a realistic estimate.

Setting a fee tier

Pass a tier object from recommendedFees() to setFee():

const fees = tx.recommendedFees()
tx.setFee(fees.high) // Use the "high" priority tier

Custom fees

Instead of using a recommended tier, you can set custom fee parameters:

UTXO custom fee

tx.setFee({ feePerKbSat: 15000n })

EVM custom fee

tx.setFee({
maxFeePerGas: 30_000_000_000n, // 30 gwei
maxPriorityFeePerGas: 2_000_000_000n, // 2 gwei
gasLimit: 50_000n // optional gas limit override
})

Checking funds

Always verify you have enough funds before broadcasting:

if (tx.enoughFunds()) {
const broadcasted = await tx.signAndBroadcast()
} else {
console.log('Insufficient funds')
}

How much is missing?

When a tier doesn't have enough funds, missingFunds tells you exactly how much more you need to deposit (or how much to subtract from amount) for the transaction to go through at that fee:

const fees = tx.recommendedFees()

if (!fees.normal.enoughFunds && fees.normal.missingFunds != null) {
// UTXO: missingFunds is in satoshis
// EVM: missingFunds is in wei
console.log('Need additional units:', fees.normal.missingFunds.toString())
}

This is handy for showing a precise "Add X to send this transaction" message in your UI rather than a generic "insufficient funds" warning.

Waiting for confirmation

After broadcasting, you can wait for the transaction to be confirmed on-chain:

const broadcasted = await tx.signAndBroadcast()

const cancel = broadcasted.onConfirmed((details) => {
console.log('Confirmed in block', details.blockHeight)
console.log('TX ID:', details.transactionId)
})

// Cancel polling at any time:
cancel()

EVM Ethereum example (full)

const cg = new ChainGate()
const eth = cg.connect(cg.networks.ethereum, wallet)

const amount = cg.networks.ethereum.amount('0.05')
const tx = await eth.transfer(amount, '0xRecipient...')

const fees = tx.recommendedFees()
console.log('Normal fee:', fees.normal.maxFeePerGas, 'wei')

tx.setFee(fees.high)
const broadcasted = await tx.signAndBroadcast()
console.log('TX ID:', broadcasted.transactionId)

Estimated transaction size

For UTXO transactions, you can check the estimated size (in virtual bytes) at the current fee rate:

const size = tx.estimatedSizeBytes()
console.log('Estimated size:', size, 'vbytes') // null if not enough funds

Chaining EVM transactions

You can broadcast several EVM transactions back-to-back without worrying about colliding on the same nonce — even when the network hasn't yet reflected the previous send.

ChainGate keeps track of the next nonce per address and chain, so the next transfer() call picks up where the previous one left off:

const eth = cg.connect(cg.networks.ethereum, wallet)

const tx1 = await eth.transfer(cg.networks.ethereum.amount('0.01'), '0xRecipientA...')
await tx1.signAndBroadcast()

// Sent right away — picks the next nonce automatically
const tx2 = await eth.transfer(cg.networks.ethereum.amount('0.02'), '0xRecipientB...')
await tx2.signAndBroadcast()

Same behaviour on EVM RPC connectors (Polygon, BNB Smart Chain, custom networks). If you ever need the value yourself, every EVM explorer exposes getNonce(address) that returns the next nonce to use:

const nonce = await cg.explore(cg.networks.ethereum).getNonce('0xYourAddress...')

Key points

  • Uniform API: transfer(), recommendedFees(), setFee(), signAndBroadcast() work for all networks — Bitcoin, Litecoin, Dogecoin, Bitcoin Cash, Ethereum, and any custom EVM RPC network.
  • Create amounts: Use network.amount() for crypto amounts, network.amountFromCurrency() for fiat amounts.
  • Fee tiers: Pass a tier object from recommendedFees() (e.g. fees.normal, fees.high) to setFee(). Each tier includes enoughFunds and estimatedConfirmationSecs.
  • Custom fees: Pass an object with manual fee parameters to setFee() for UTXO ({ feePerKbSat }) or EVM ({ maxFeePerGas, maxPriorityFeePerGas, gasLimit? }).
  • Transaction size: Use estimatedSizeBytes() on UTXO transactions to get the virtual size at the current fee rate.
  • Confirmation tracking: Use broadcasted.onConfirmed() to be notified when the transaction is mined.
  • Signing happens at broadcast: signAndBroadcast() handles signing internally. Encrypted wallets prompt for the password at this point.