Skip to content

Releases: ChrisCho-H/bitcoin-sdk-js

1.0.1

10 Aug 02:21
Compare
Choose a tag to compare

v1.0.1 with some docs updates

  1. Add description for address generation and bip322 message signing
  2. Add table with navigator

How To Use

Generate Address (P2PKH, P2WPKH, P2SH, P2WSH, P2TR)

1. Address for Single Signer(P2PKH, P2WPKH, P2TR)

import * as bitcoin from 'bitcoin-sdk-js'

// if you need to generate key pair
const keyPair = await bitcoin.wallet.generateKeyPair();
// p2pkh
const legacyAddress = await bitcoin.address.generateAddress(
  keyPair.publicKey,
  'legacy',
);
// p2wpkh
const segwitAddress = await bitcoin.address.generateAddress(
  keyPair.publicKey,
  'segwit',
);
// p2tr
const taprootAddress = await bitcoin.address.generateAddress(
  (
    // It's recommended to tweak public key to generate taproot address
    await bitcoin.tapscript.getTapTweakedPubkey(
      // Schnorr key is same with any bitcoin key pair, except it does not use public key prefix byte '02' or '03'.
      keyPair.publicKey.slice(2),
      await bitcoin.tapscript.getTapTweak(keyPair.publicKey.slice(2)),
    )
  ).tweakedPubKey,
  'taproot',
);
// p2pkh - testnet
const legacyAddressTestnet = await bitcoin.address.generateAddress(
  keyPair.publicKey,
  'legacy',
  'testnet',
);
// p2wpkh - testnet
const segwitAddressTestnet = await bitcoin.address.generateAddress(
  keyPair.publicKey,
  'segwit',
  'testnet',
);
// p2tr - testnet
const taprootAddressTestnet = await bitcoin.address.generateAddress(
  (
    // It's recommended to tweak public key to generate taproot address
    await bitcoin.tapscript.getTapTweakedPubkey(
      // Schnorr key is same with any bitcoin key pair, except it does not use public key prefix byte '02' or '03'.
      keyPair.publicKey.slice(2),
      await bitcoin.tapscript.getTapTweak(keyPair.publicKey.slice(2)),
    )
  ).tweakedPubKey,
  'taproot',
  'testnet',
);

2. Address for Script(P2SH, P2WSH)

import * as bitcoin from 'bitcoin-sdk-js'

// if you need to generate key pair
const keyPair = await bitcoin.wallet.generateKeyPair();
// Script can be any of bitcoin script opcode! bitcoin-sdk-js provides an easy way to build script.
// Below script is timelock + single sig
const script = 
      await bitcoin.script.generateTimeLockScript(2542622) + await bitcoin.script.generateSingleSigScript(keyPair.publicKey);
// p2sh
const legacyScriptAddress = await bitcoin.address.generateScriptAddress(
  script,
  'legacy',
);
// p2wsh
const segwitScriptAddress = await bitcoin.address.generateScriptAddress(
  script,
  'segwit',
);
// p2sh - testnet
const legacyScriptAddressTestnet = await bitcoin.address.generateScriptAddress(
  script,
  'legacy',
  'testnet',
);
// p2wsh - testnet
const segwitScriptAddressTestnet = await bitcoin.address.generateScriptAddress(
  script,
  'segwit',
  'testnet',
);

Create Transaction (P2PKH, P2WPKH, P2SH, P2WSH, P2TR)

1. Transaction for Single Signer(P2PKH, P2WPKH)

import * as bitcoin from 'bitcoin-sdk-js';

// if you need to generate key pair
const keyPair = await bitcoin.wallet.generateKeyPair();
const pubkey = keyPair.publicKey;
const privkey = keyPair.privateKey;

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// most common transaction which sends bitcoin to single sig address
await tx.addOutput({
    address: await bitcoin.address.generateAddress(pubkey),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if input utxo only requires single sig(p2wpkh or p2pkh)
await tx.signInput(privkey, 0);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

2. Transaction for Multi Signer(P2SH, P2WSH)

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// sends bitcoin to 2-of-3 multisig transaction
await tx.addOutput({
    // all the smart contract output is recommended to use script address!
    address: await bitcoin.address.generateScriptAddress(
        // this generate multisig smart contract, which is possible up to 15-of-15 
        await bitcoin.script.generateMultiSigScript(2, [pubkey1, pubkey2, pubkey3]),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if input utxo requires multi sig(p2wsh or p2sh)
await tx.multiSignInput(
    [pubkey1, pubkey2, pubkey3],
    [privkey1, privkey2],
    0, // input index to sign
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

3. Transaction for Single(or Multi) Signer with TimeLock (or || and) HashLock(P2SH, P2WSH)

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// sends bitcoin to single(or multi) sig script with timelock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // able to spend this output after given block height
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with hashlock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // able to spend this output if 'secretHex' is given
        (await bitcoin.script.generateHashLockScript('secretHex')) + 
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with timelock + hashlock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        /* 
        WATCH OUT! Order matters. You must place script in the order of
        timelock, hashlock and single(or multi) sig
        to spend this output with bitcoin-sdk-js
        */
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        (await bitcoin.script.generateHashLockScript('secretHex')) +
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if transaction use timelock input, must set tx locktime bigger than input timelock
await tx.setLocktime(2542622);

// if input utxo requires single sig with smart contract(p2wsh or p2sh)
await tx.signInput(
    privkey,
    0, // input index to sign
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622), // is timelock input? provide script
    'secretHex', // is hashlock input? provide secretHex to unlock
);

// or if input utxo requires multi sig with smart contract(p2wsh or p2sh)
await tx.multiSignInput(
    [pubkey1, pubkey2, pubkey3],
    [privkey1, privkey2],
    0, // input index to sign
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622), // is timelock input? provide script
    'secretHex', // is hashlock input? provide secretHex to unlock
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

4. Transaction for TimeLock (or || and) HashLock without Signer(P2SH, P2WSH)

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction...
Read more

1.0.0

30 Jul 13:36
Compare
Choose a tag to compare

v1.0.0 finally released!

bitcoin-sdk-js

✨ Bitcoin TypeScript/JavaScript Library for NodeJS, Browser and Mobile ✨

Bitcoin is hard. Especially for those not familiar with crypto, it is even hard
to create a simple bitcoin transaction.

bitcoin-sdk-js provides various features which help to create various type of bitcoin transaction so easily🚀,
including advanced smart contract like multisig, hashlock, timelock, combination of them, and even your own smart contract.

Legacy, Segwit, Taproot features are all suppoted!

Install

npm install bitcoin-sdk-js

How To Use

1. Transaction for Single Signer(P2WPKH)

import * as bitcoin from 'bitcoin-sdk-js';

// if you need to generate key pair
const keyPair = await bitcoin.wallet.generateKeyPair();
const pubkey = keyPair.publicKey;
const privkey = keyPair.privateKey;

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// most common transaction which sends bitcoin to single sig address
await tx.addOutput({
    address: await bitcoin.address.generateAddress(pubkey),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if input utxo only requires single sig(p2wpkh or p2pkh)
await tx.signInput(privkey, 0);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

2. Transaction for Multi Signer(P2WSH)

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// sends bitcoin to 2-of-3 multisig transaction
await tx.addOutput({
    // all the smart contract output is recommended to use script address!
    address: await bitcoin.address.generateScriptAddress(
        // this generate multisig smart contract, which is possible up to 15-of-15 
        await bitcoin.script.generateMultiSigScript(2, [pubkey1, pubkey2, pubkey3]),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if input utxo requires multi sig(p2wsh or p2sh)
await tx.multiSignInput(
    [pubkey1, pubkey2, pubkey3],
    [privkey1, privkey2],
    0, // input index to sign
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

3. Transaction for Single(or Multi) Signer with TimeLock (or || and) HashLock(P2WSH)

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// sends bitcoin to single(or multi) sig script with timelock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // able to spend this output after given block height
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with hashlock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // able to spend this output if 'secret' is given
        (await bitcoin.script.generateHashLockScript('secretHex')) + 
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with timelock + hashlock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        /* 
        WATCH OUT! Order matters. You must place script in the order of
        timelock, hashlock and single(or multi) sig
        to spend this output with bitcoin-sdk-js
        */
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        (await bitcoin.script.generateHashLockScript('secretHex')) +
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if transaction use timelock input, must set tx locktime bigger than input timelock
await tx.setLocktime(2542622);

// if input utxo requires single sig with smart contract(p2wsh or p2sh)
await tx.signInput(
    privkey,
    0, // input index to sign
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622), // is timelock input? provide script
    'secretHex', // is hashlock input? provide secret to unlock
);

// or if input utxo requires multi sig with smart contract(p2wsh or p2sh)
await tx.multiSignInput(
    [pubkey1, pubkey2, pubkey3],
    [privkey1, privkey2],
    0, // input index to sign
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622), // is timelock input? provide script
    'secretHex', // is hashlock input? provide secret to unlock
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

4. Transaction for TimeLock (or || and) HashLock without Signer(P2WSH)

import * as bitcoin from 'bitcoin-sdk-js';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as an input
await tx.addInput({
  txHash: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo(unit is satoshi)
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// sends bitcoin to script with timelock + hashlock (no sig required)
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // if you only want hashlock, you can remove generateTimeLockScript
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        (await bitcoin.script.generateHashLockScript('secretHex')),   
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if transaction use timelock input, must set tx locktime bigger than input timelock
await tx.setLocktime(2542622);

// if input utxo requires to unlock hash with smart contract(p2wsh or p2sh)
await tx.unlockHashInput(
    'secretHex',
    0,
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622),// is timelock input? provide script
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

Advanced feature

Custom smart contract

You might use tapscript for this!

import * as bitcoin from 'bitcoin-sdk-js';

// You can send and spend any smart contract. Below example is classic HTLC
const HTLC = bitcoin.Opcode.OP_IF +
        (await bitcoin.script.generateTimeLockScript(2576085)) +
        (await bitcoin.data.pushData(pubkey1)) + // must specify data to read(if not opcode)
        pubkey1 +
        bitcoin.Opcode.OP_ELSE +
        (await bitcoin.script.generateHashLockScript('abcdef')) +
        (await bitcoin.data.pushData(pubkey2)) + // must specify data to read(if not opcode)
        pubkey2 +
        bitcoin.Opcode.OP_ENDIF +
        bitcoin.Opcode.OP_CHECKSIG
// p2wsh address, custom contract address must be p2wsh(p2sh) (or taproot address, check next chapter!)
const toAddress = await bitcoin.address.generateScriptAddress(HTLC);

// Then, can be spent as an input by signing by scriptSig!
// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// if transaction use timelock input, must set tx locktime bigger than input timelock
await tx.setLocktime(2576085); 

// spend by executing OP_IF branch
await tx.signInputByScriptSig(
    // script sig list(order matters!!! bitcoin script is stack-based LIFO)
    [
      await bitcoin.crypto.sign(
        // method to get input message hash to sign
        await tx.getInputHashToSign(
          HTLC, // redeem script as p2wsh
          0, // input index
        ),
        privkey1, // signer private key
      ),
      '01', // execute OP_IF
      HTLC, // redeem script as p2wsh
    ],
    0, // input index
  );
// Or spend by executing OP_ELSE branch
await tx.signInputByScriptSig(
    // script sig list(order matters!!! bitcoin script is stack-based LIFO)
    [
      await bitcoin.crypto.sign(
        // method to get input message hash to sign
        await tx.getInputHashToSign(
          HTLC, // redeem script as p2wsh
          0, // input index
        ),
        privkey2, // signer private key
      ),
      'abcdef', // unlock hash
      '', // execute OP_ELSE
      HTLC, // redeem script as p2sh
    ],
    0, // input index
  );

// You can broadcast signed tx here: https://blockstream.info/tes...
Read more

0.3.2

18 Jun 05:03
Compare
Choose a tag to compare

v0.3.2

0.3.1

11 Jun 11:44
Compare
Choose a tag to compare

v0.3.1

  • add script number encode to handle 5 byte signed integer (to convert it to hex string)

0.3.0

11 Jun 11:42
Compare
Choose a tag to compare

v0.3.0

  • Remove public key parameter when sign input as get public key from private key inside function!

Below is code snippet example from https://bitcoin.stackexchange.com/questions/123028/how-to-create-and-sign-a-segwit-transaction-using-any-npm-pacakge/123030#123030

import * as bitcoin from 'bitcoin-sdk-js';

const privkey =
  'e6c58327b4bf1314296bba7adb66f94c36ea5611ac07830c229aa7ee6f9caac3';
const toAddress = 'tb1qmjqyknun9umlwlle5nuy8jz9ms7v85cp7k2nfe';
const toValue = 300000;
const txHash =
  '8bcd197873dbff5cc67cb3e0b1cbbdf097c7af94b0e9365adecb424e408294bf';
const inputValue = 310981;

const tx = new bitcoin.Transaction();

await tx.addInput({
  txHash: txHash,
  index: 1,
  value: inputValue,
});
await tx.addOutput({
  address: toAddress,
  value: toValue,
});
await tx.signInput(privkey, 0);

const txHex = await tx.getSignedHex();

0.2.0

17 May 00:45
Compare
Choose a tag to compare

v0.2.0

  • Add Bitcoin-Message feature
  • Legacy p2pkh address signing supported
  • Segwit p2wpkh and Taproot p2tr address signing supported following BIP322

How To Use? Check below test code
https://github.com/ChrisCho-H/bitcoin-sdk-js/blob/main/test/crypto.spec.ts#L87

0.1.1

14 May 11:19
Compare
Choose a tag to compare

v0.1.1

  • Finally Taproot features are all supported!!!
  • You can easily build Bitcoin Smart Contract with Tapscript!
  • Convert value unit to satoshi to prevent wrong floating porint calculation
  • Fix non-taproot transaction bug in v0.1.0(unpublished)

Taproot and Tapscript spend

import * as bitcoin from 'bitcoin-sdk-js';

// Let's send and spend HTLC in taproot and tapscript way!
/*
  Schnorr key is same with any bitcoin key pair, except it does not use public key prefix byte '02' or '03'.
  This key pair will be used as a master key to spend UTXO in taproot, after tweaked(will explain how to step by step).
*/
const schnorrPubkey = (await bitcoin.wallet.generateKeyPair()).publicKey.slice(2); // remove first byte (which is parity bit)
const schnorrPrivkey = (await bitcoin.wallet.generateKeyPair()).privateKey;

/*
  HTLC consists of conditional branch, in which OP_IF contains Time Lock Contract and OP_ELSE contains Hash Lock.
  We can construct tapscript tree where each leaf has its own contract(script), enable unlimited spending branches.
*/
// Originally OP_IF branch Time Lock Contract
const timeLockContract =
    (await bitcoin.script.generateTimeLockScript(2576085)) +
    (await bitcoin.data.pushData(schnorrPubkey1) + // must specify data to read(if not opcode)
    schnorrPubkey1 + // you must use schnorr key even in tapscript
    bitcoin.Opcode.OP_CHECKSIG;
// Originally OP_ELSE branch Hash Lock Contract
const hashLockContract =
    (await bitcoin.script.generateHashLockScript('abcdef')) +
    (await bitcoin.data.pushData(schnorrPubkey2) + // must specify data to read(if not opcode)
    schnorrPubkey2 + // you must use schnorr key even in tapscript
    bitcoin.Opcode.OP_CHECKSIG;

/*
  You can get tapbranch from a pair of script, and repeat over to reach to the root of tree(which is 'taproot') 
  Here we have only a single pair of script, so tapbranch of it is taproot
*/
const taproot = await bitcoin.tapscript.getTapBranch([
    await bitcoin.tapscript.getTapLeaf(timeLockContract),
    await bitcoin.tapscript.getTapLeaf(hashLockContract),
]);
// Then, get taptweak with schnorr key generated above
const tapTweak = await bitcoin.tapscript.getTapTweak(
    schnorrPubkey,
    taproot,
);
// Finally, you can tweak schnorr public key, which can be used as taproot key!
const tapTweakedPubkey = await bitcoin.tapscript.getTapTweakedPubkey(
    schnorrPubkey,
    tapTweak,
);
// generate taproot address with taproot key(and send to it!)
const toAddress = await bitcoin.address.generateAddress(tapTweakedPubkey.tweakedPubKey, 'taproot');



// Then, can be spent as an input!
// one thing to keep in mind, taproot or tapscript spend needs to provide all the script public key of input
await tx.addInput({
    txHash: txId,
    index: 0,
    value: value,
    script: await bitcoin.script.getScriptByAddress(toAddress),
});

await tx.setLocktime(2576085); // if transaction use timelock input, must set tx locktime bigger than input timelock

// taproot spend? just sign input with tweaked schnorr key(which is, taproot private key)
await tx.signInput(
    '', // we don't need to provide taproot public key when sign
    await bitcoin.tapscript.getTapTweakedPrivkey(schnorrPrivkey, tapTweak), // just provide private key
    0,
    'taproot',
);

// Or tapscript spend? Let's spend by hash lock script!
const sigStack = [
    await bitcoin.crypto.sign(
      await tx.getInputHashToSign(hashLockContract, 0, 'tapscript'),
      schnorrPrivkey2,
      'schnorr',
    ), // schnorr signature
    'abcdef', // hash to unlock
    hashLockContract, // spend script
    // you must specify path to find taproot with provided script(For more detail, check BIP341!)
    await bitcoin.tapscript.getTapControlBlock(
      schnorrPubkey,
      tapTweakedPubkey.parityBit,
      await bitcoin.tapscript.getTapLeaf(timeLockContract), // path to find taproot
    ),
];

await tx.signInputByScriptSig(sigStack, 0);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

📜 License

This software is licensed under the MIT ©.

0.0.3

09 Feb 07:56
Compare
Choose a tag to compare

v0.0.3

  • Custom contract option is enabled
  • You can send and spend any bitcoin smart contract!!!

Advanced feature

Custom smart contract

import * as bitcoin from 'bitcoin-sdk-js';

// You can send and spend any smart contract. Below example is classic HTLC
const HTLC = bitcoin.Opcode.OP_IF +
        (await bitcoin.script.generateTimeLockScript(2576085)) +
        (await bitcoin.data.pushData(pubkey1)) + // must specify data to read(if not opcode)
        pubkey1 +
        bitcoin.Opcode.OP_ELSE +
        (await bitcoin.script.generateHashLockScript('abcdef')) +
        (await bitcoin.data.pushData(pubkey2)) + // must specify data to read(if not opcode)
        pubkey2 +
        bitcoin.Opcode.OP_ENDIF +
        bitcoin.Opcode.OP_CHECKSIG
// p2sh address, custom contract address must be p2sh(p2wsh)
const toAddress = await bitcoin.address.generateScriptAddress(HTLC);

// Then, can be spent as an input by signing by scriptSig!
await tx.setLocktime(2576085); // if transaction use timelock input, must set tx locktime bigger than input timelock
// spend by executing OP_IF branch
await tx.signInputByScriptSig(
    // script sig list(order matters!!! bitcoin script is stack-based LIFO)
    [
      await bitcoin.crypto.sign(
        // method to get input message hash to sign
        await tx.getInputHashToSign(
          HTLC, // redeem script as p2sh
          0, // input index
        ),
        privkey1, // signer private key
      ),
      '01', // execute OP_IF(if legacy(not segwit), OP_1 instead of '01') 
    ],
    HTLC, // redeem script as p2sh
    0, // input index
  );
// Or spend by executing OP_ELSE branch
await tx.signInputByScriptSig(
    // script sig list(order matters!!! bitcoin script is stack-based LIFO)
    [
      await bitcoin.crypto.sign(
        // method to get input message hash to sign
        await tx.getInputHashToSign(
          HTLC, // redeem script as p2sh
          0, // input index
        ),
        privkey2, // signer private key
      ),
      'abcdef', // unlock hash
      '', // execute OP_ELSE 
    ],
    HTLC, // redeem script as p2sh
    0, // input index
  );

0.0.2

22 Jan 14:11
108ab42
Compare
Choose a tag to compare

bitcoin-sdk-js

✨Bitcoin Typescript SDK for Node, Browser and Mobile✨

Bitcoin is hard. Especially for those not familiar with crypto, it is even hard
to create a simple bitcoin transaction.

bitcoin-sdk-js provides various features which help to create various type of bitcoin transaction so easily🚀,
including advanced smart contract like multisig, hashlock, timelock and combination of them😍.

Install

npm install bitcoin-sdk-js

How To Use

import * as bitcoin from 'bitcoin-sdk-js';

const value = 1; // unit is bitcoin
const fee = 0.00001; // unit is bitcoin
const txId = '1192fefcee7fc7a6a64563d0899fdcf00585f69f3564c1e73e8b7d480f45900c';

// initialize Bitcoin Transaction object
const tx = new bitcoin.Transaction();

// add UTXO to spend as input
await tx.addInput({
  id: txId, // transaction id of utxo
  index: 0, // index of utxo in transaction
  value: value, // value of utxo
} as bitcoin.UTXO);

// add Target output to send Bitcoin
// most common transaction which sends bitcoin to single sig address
await tx.addOutput({
    address: await bitcoin.address.generateAddress(pubkey),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to 2-of-3 multisig transaction
await tx.addOutput({
    // all the smart contract output is recommended to use script address!
    address: await bitcoin.address.generateScriptAddress(
        // this generate multisig smart contract, which is possible up to 15-of-15 
        await bitcoin.script.generateMultiSigScript(2, [pubkey1, pubkey2, pubkey3]),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with timelock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // able to spend this output after given block height
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with hashlock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // able to spend this output if 'secret' is given
        (await bitcoin.script.generateHashLockScript('secret')) + 
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to single(or multi) sig script with timelock + hashlock
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        /* 
        WATCH OUT! Order matters. You must place script in the order of
        timelock, hashlock and single(or multi) sig
        to spend this output with bitcoin-sdk-js
        */
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        (await bitcoin.script.generateHashLockScript('secret')) +
        // you can use generateMultiSigScript instead of single sig here
        (await bitcoin.script.generateSingleSigScript(pubkey)),
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// sends bitcoin to script with timelock + hashlock (no sig required)
await tx.addOutput({
    address: await bitcoin.address.generateScriptAddress(
        // if you only want hashlock, you can remove generateTimeLockScript
        (await bitcoin.script.generateTimeLockScript(2542622)) +
        (await bitcoin.script.generateHashLockScript('secret')),   
    ),
    value: value - fee, // value of utxo - fee
} as bitcoin.Target);

// if transaction use timelock input, must set tx locktime bigger than input timelock
tx.setLocktime(2542622);

// if input utxo only requires single sig(p2wpkh or p2pkh)
await tx.signInput(pubkey, privkey, 0);

// or if input utxo only requires multi sig(p2wsh or p2sh)
await tx.multiSignInput(
    [pubkey1, pubkey2, pubkey3],
    [privkey1, privkey2],
    0, // input index to sign
);

// or if input utxo requires single sig with smart contract(p2wsh or p2sh)
await tx.signInput(
    pubkey,
    privkey,
    0, // input index to sign
    // below are optional, use to sign legacy utxo or unlock smart contract utxo
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622), // is timelock input? provide script
    'secret', // is hashlock input? provide secret to unlock
);


// or if input utxo requires multi sig with smart contract(p2wsh or p2sh)
await tx.multiSignInput(
    [pubkey1, pubkey2, pubkey3],
    [privkey1, privkey2],
    0, // input index to sign
    // below are optional, use to sign legacy utxo or unlock smart contract utxo
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622), // is timelock input? provide script
    'secret', // is hashlock input? provide secret to unlock
);

// or if input utxo requires unlock hash with smart contract(p2wsh or p2sh)
await tx.unlockHashInput(
    'secret',
    0,
    // below are optional, use to unlock legacy utxo or timlock utxo with hashlock
    'segwit', // default is segwit, you might use legacy if necessary
    await bitcoin.script.generateTimeLockScript(2542622),// is timelock input? provide script
);

// You can broadcast signed tx here: https://blockstream.info/testnet/tx/push
const txToBroadcast: string = await tx.getSignedHex();

📜 License

This software is licensed under the MIT ©.

Full Changelog: https://github.com/ChrisCho-H/bitcoin-sdk-js/commits/v0.0.2