How to send bitcoin BTC using send-crypto using WIF key in node,js

981 Views Asked by At

I am trying to send bitcoin using the send-crypto library. It seems to require the private key to be in hex format, but my key is of the form "L3fKJ..." which I understand to be in WIF format? I think I need to use ECPair to convert it but it does not work.

The current error is "TypeError: ECPair.fromWIF is not a function". If I create a new private using send-crypto the balance code works fine.

If there is an easier way without using send-crypto will to try that too.

const bitcoin = require('bitcoinjs-lib');
const ECPair = require('ecpair');
const CryptoAccount = require("send-crypto");

/* Load account from private key */
const privateKeyWIF = "L3fKJ...";
const keyPair = ECPair.fromWIF(privateKeyWIF);
const privateKey = keyPair.privateKey;
console.log(privateKey);
const account = new CryptoAccount(privateKey);

async function start() {
    console.log(await account.address("BTC"));

    console.log(await account.getBalance("BTC"));
    console.log(await account.getBalance("BTC", {address:"bc1qe6..."}));

    const balance = await account.getBalance("BTC");
    await account.send("bc1qe6...", balance, "BTC", {
            subtractFee: true,
    });

};
start();
2

There are 2 best solutions below

3
On BEST ANSWER

So this is the code that ended up working. Note it looks like the bitcoinjs code needs to be different depending on the version, not at all backward compatible, this code is working on the latest as of Oct 2023.

// npm install --save send-crypto
// npm install --save bitcoinjs-lib
// npm install --save ecpair
// npm install --save tiny-secp256k1

const bitcoin = require('bitcoinjs-lib');
const ECPairFactory = require('ecpair');
const ecc = require('tiny-secp256k1');
const CryptoAccount = require("send-crypto");

const network = bitcoin.networks.bitcoin;

// Load your private key (WIF)
const ECPair = ECPairFactory.ECPairFactory(ecc);
const privateKeyWIF = 'L3fK...';
const keyPair = ECPair.fromWIF(privateKeyWIF, network);

/* Load account from private key */
//const privateKey = process.env.PRIVATE_KEY || CryptoAccount.newPrivateKey();
const privateKey = keyPair.privateKey;
console.log(privateKey);
const account = new CryptoAccount(privateKey);

async function start() {
    console.log(await account.address("BTC"));

    console.log(await account.getBalance("BTC"));
    console.log(await account.getBalance("BTC", {address:"bc1q..."}));

    const balance = await account.getBalance("BTC");
    await account.send("bc1q...", balance, "BTC", {
            subtractFee: true,
    });

    /*const txHash = await account
            .send("bc1q...", 0.01, "BTC")
            .on("transactionHash", console.log)
            .on("confirmation", console.log);*/


    console.log(await account.getBalance("BTC"));
    console.log(await account.getBalance("BTC", {address:"bc1q..."}));

};
start();
6
On

Test if the ECPair module should be required from the bitcoinjs-lib library, not as a separate module. (in this 2016 thread, you could see const ECPair = require(‘bitcoinjs-lib/src/ecpair’), but I don't think it is needed anymore)

const { ECPair } = require('bitcoinjs-lib');

If you are open to using libraries other than send-crypto, you might want to consider using bitcoinjs-lib directly to handle transaction creation and broadcasting.


What would the code be to use bitcoinjs-lib directly

You would need to create and broadcast a Bitcoin transaction using your WIF (Wallet Import Format) private key, which would involve:

  • Specify the network (testnet is used in this example for safety).
  • Load your private key and derive your public key and address.
  • Define an asynchronous function createTransaction to:
    • Fetch UTXOs associated with your address.
    • Create a new transaction, adding inputs from the UTXOs.
    • Specify the recipient and amount.
    • Optionally send change back to your own address.
    • Sign the transaction.
    • Return the hexadecimal representation of the transaction.
  • Define an asynchronous function broadcastTransaction to:
    • Call createTransaction to create the transaction.
    • Broadcast the transaction to the network using a third-party API.
  • Call broadcastTransaction to execute the process.

Looking at "Bitcoin basics: Programming with bitcoinjs-lib" from Deezy, or examples like bitcoin-transaction and J1ARVIS/Portfolio src/core/blockchain/btc/BtcLib.js, that would look like:

const bitcoin = require('bitcoinjs-lib');
const fetch = require('node-fetch');

// Your network (testnet in this case)
const network = bitcoin.networks.testnet;

// Load your private key (WIF)
const privateKeyWIF = 'cRpa...';  // Replace with your private key
const keyPair = bitcoin.ECPair.fromWIF(privateKeyWIF, network);
const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network });

async function createTransaction() {
  // Fetch UTXOs (Unspent Transaction Outputs) for your address
  const utxosResponse = await fetch(`https://api.blockcypher.com/v1/btc/test3/addrs/${address}?unspentOnly=true`);
  const utxosData = await utxosResponse.json();

  // Create a transaction builder
  const txb = new bitcoin.TransactionBuilder(network);

  // Sum of available funds
  let totalUtxoValue = 0;

  // Add inputs from UTXOs
  utxosData.txrefs.forEach((txref, index) => {
    txb.addInput(txref.tx_hash, txref.tx_output_n);
    totalUtxoValue += txref.value;
  });

  // Destination address and amount to send (in satoshis)
  const targetAddress = 'tb1q...';  // Replace with destination address
  const amountToSend = 10000;  // Replace with amount in satoshis

  // Calculate fee (example: 1 satoshi per byte)
  const fee = txb.buildIncomplete().byteLength() * 1;

  // Add output
  txb.addOutput(targetAddress, amountToSend);

  // Send change back to self, if any
  const change = totalUtxoValue - amountToSend - fee;
  if (change > 0) {
    txb.addOutput(address, change);
  }

  // Sign each input
  for (let i = 0; i < txb.__inputs.length; i++) {
    txb.sign(i, keyPair);
  }

  // Build the transaction and get the hexadecimal representation
  const tx = txb.build();
  const txHex = tx.toHex();

  return txHex;
}

async function broadcastTransaction() {
  const txHex = await createTransaction();

  // Broadcast the transaction
  const broadcastResponse = await fetch('https://api.blockcypher.com/v1/btc/test3/txs/push', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ tx: txHex }),
  });

  const broadcastData = await broadcastResponse.json();
  console.log(broadcastData);
}

broadcastTransaction();

Do replace placeholders (e.g., cRpa..., tb1q...) with your actual values.

Note: That script makes use of a third-party API (BlockCypher) for fetching UTXOs and broadcasting the transaction.

That code does use a WIF (Wallet Import Format) key to create a key pair, which is then used to build and sign a Bitcoin transaction.

const keyPair = bitcoin.ECPair.fromWIF(privateKeyWIF, network);

The bitcoin.ECPair.fromWIF function is called with the privateKeyWIF variable (which should hold your WIF key) and the network variable (which specifies the Bitcoin network to use, such as mainnet or testnet).
The function returns a keyPair object which contains the private and public keys, and is used later in the code to sign the transaction.