Bitcoin

JellySwap Bitcoin package

What is JellySwap?

JellySwap is a cross-chain atomic swap protocol that supports many different blockchains. The Bitcoin package is used to interact with the Bitcoin Hash Time Locked Contracts and to handle blockchain events. The main components of the module are Providers, Contract, Event, Adapter and Config.

Install package

Using yarn

$ yarn add @jelly-swap/bitcoin

Using npm

$ npm install @jelly-swap/bitcoin

Importing

JavaScript (ES3)

const jellyBtc = require('@jelly-swap/bitcoin');

JavaScript (ES6) / TypeScript

import * as jellyBtc from '@jelly-swap/bitcoin';

Config Module

This module contains the main configuration for the whole package

const config = jellyBtc.Config();

//Expected output
{ 
  network: 'BTC',
  explorer: 'https://blockstream.info/testnet/tx/',
  providerUrl: 'https://spacejelly.network/bitpay/api/BTC/testnet',
  receiverAddress: 'tb1qc4mwllgvmy0xqsdexpm5v8g74ldmv698whnyrw',
  expiration: 10800,
  decimals: 8,
  unix: true,
  apiProviderUrl: 'https://spacejelly.network/btc/api/v1/btc' 
}

Provider Module

A Provider abstracts a connection to the Bitcoin blockchain, for issuing queries and sending signed state changing transactions.

BitcoinProvider - allows you to connect to Bitcoin nodes you control or have access to, including mainnet, testnets, etc.

const btcProvider = new jellyBtc.Providers.BitcoinProvider(
  "https://spacejelly.network/btc/api/v1/btc"
);

Contract Module

Create contract instance:

const btcContract = new jellyBtc.Contract(
  btcWallet,
  network?,
  "https://spacejelly.network/btc/api/v1/btc"?
);

subscribe

await btcContract.subscribe(onEvent, filter?);

getPastEvents

Returns past blockchain events.

const pastEvents = await btcContract.getPastEvents(eventType, filter?, currentBlock?);

getCurrentBlock

Returns the latest block.

const currentBlock = await btcContract.getCurrentBlock();

getBalance

Returns the balance of the given address.

const balance = await btcContract.getBalance(address, network);

newContract

Creates and broadcasts a new swap transaction.

const transactionHashSwap = await btcContract.newContract(swap);

withdraw

Creates and broadcasts a withdraw transaction.

const transactionHashWithdraw = await btcContract.withdraw(withdraw);

refund

Creates and broadcasts a refund transaction.

const transactionHashRefund = await btcContract.refund(contractRefund);

Event module

Event module is used to read past blockchain events and subscribe for new ones

Initialize event module:

const btcEvents = new jellyBtc.Event(btcContract);

getPast

Returns past blockchain events.

const pastEvents = await btcEvents.getPast(eventType, filter?, currentBlock?);

subscribe

await btcEvents.subscribe(onEvent, filter?);

Adapter module

The Adapter module is used to format the client data and prepare the input for the swaps.

Initialize adapter module:

const btcAdapter = new jellyBtc.Adapter();

generateId

Generate swap id based on the input parameters. The same id will be generated by the Solidity Contract.

const id = btcAdapter.generateId(input);

addressValid

Checks if the passed address is valid or not.

const isAddressValid = btcAdapter.addressValid(address);

parseAddress

Returns the address in lower case.

const addressParsed = btcAdapter.parseAddress(address);

parseToNative

Parse BTC amount to satoshi amount i.e. 0.1 to 10000000

const result = btcAdapter.parseToNative(amount);

parseFromNative

Parse satoshi amount to BTC amount i.e. 10000000 to 0.1

const result = btcAdapter.parseFromNative(amount);

formatInput

Returns object of type UserInputSwap.

const swapInput = btcAdapter.formatInput(data, receiver);

Example

const jellyBtc = require("@jelly-swap/bitcoin");
const { Wallet, Networks } = require("@jelly-swap/btc-wallet");

const btcProvider = new jellyBtc.Providers.BitcoinProvider(
  "https://spacejelly.network/btc/api/v1/btc"
);

const network = Networks.bitcoin;

const btcWallet = new Wallet(
  network,
  "witch collapse practice feed shame open despair creek road again ice least",
  "bech32",
  btcProvider
);

const btcContract = new jellyBtc.Contract(
  btcWallet,
  network,
  "https://spacejelly.network/btc/api/v1/btc"
);

const adapter = new jellyBtc.Adapter();

const subscribeFilter = type => {
  const address = "bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4";

  switch (type) {
    case "new": {
      return () => {
        return {
          type: "getSwapsByAddressAndBlock",
          address
        };
      };
    }

    case "withdraw": {
      return () => {
        return {
          type: "getWithdrawByReceiverAndBlock",
          address
        };
      };
    }

    default: {
      break;
    }
  }
};

const swapsFilter = type => {
  const address = "bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4";

  switch (type) {
    case "new": {
      return () => {
        return {
          type: "getSwapsByAddress",
          address
        };
      };
    }

    case "withdraw": {
      return () => {
        return {
          type: "getWithdrawByReceiver",
          address
        };
      };
    }

    default: {
      break;
    }
  }
};

async function start() {
  // Subscribe for contract events and pass handle function
  await btcContract.subscribe(() => {}, subscribeFilter);

  // Get past blockchain events
  const swaps = await btcContract.getPastEvents("new", swapsFilter("new"));
  console.log(swaps);
  //...
  // { blockHeight: 616793,
  //   transactionHash: 'd15d985165708af57d08d17986bde6140ed655e49fa420fbc7e39a258900d6c1',
  //   id: '0x47b3f15cf1837a8f293df06d5846514f413025b8c978031e62a59400d0d1c22e',
  //   status: 3,
  //   inputAmount: 221300,
  //   sender: 'bc1qwjeylyfrkn8sckwux789dqtscafujhgy4xcddg',
  //   receiver: 'bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4',
  //   refundAddress: 'bc1qwjeylyfrkn8sckwux789dqtscafujhgy4xcddg',
  //   network: 'BTC',
  //   outputNetwork: 'ETH',
  //   outputAddress: '0xb2cB83E2E367682B2C0d3AAF04131Dd94C41A1A9',
  //   outputAmount: '100000000000000000',
  //   expiration: 1581341813,
  //   hashLock: '0x6a98ad2137e52aaf0eb29f44fc97084bf45a33115a0850c4355a1dcfba95c1d6',
  //   _id: '5e41475f84702846cfe3a4e3',
  //   createdAt: '2020-02-10T12:28:48.794Z',
  //   withdrawTxHash: 'a0aadca779be5e5a9775da357634a3655fc049244a255376bf1f58976d868606' },
  //...

  const withdraws = await btcContract.getPastEvents(
    "withdraw",
    swapsFilter("withdraw")
  );
  console.log(withdraws);
  //...
  // [ { blockHeight: 616794,
  //   transactionHash: 'a0aadca779be5e5a9775da357634a3655fc049244a255376bf1f58976d868606',
  //   id: '0x47b3f15cf1837a8f293df06d5846514f413025b8c978031e62a59400d0d1c22e',
  //   secret: '0xce79be42bd15a541845c6f3e6d24e053460a8062e74082440f768705b6fa2b6a',
  //   hashLock: '0x6a98ad2137e52aaf0eb29f44fc97084bf45a33115a0850c4355a1dcfba95c1d6',
  //   sender: 'bc1qwjeylyfrkn8sckwux789dqtscafujhgy4xcddg',
  //   receiver: 'bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4',
  //   network: 'BTC',
  //   _id: '5e4148ea84702846cfe3a4e4',
  //   createdAt: '2020-02-10T12:13:30.057Z' } ]
  //...

  const refunds = await btcContract.getPastEvents(
    "refund",
    swapsFilter("refund")
  );
  console.log(refunds);
  //...
  // [ { blockHeight: 616794,
  //   transactionHash: 'a0aadca779be5e5a9775da357634a3655fc049244a255376bf1f58976d868606',
  //   id: '0x47b3f15cf1837a8f293df06d5846514f413025b8c978031e62a59400d0d1c22e',
  //   secret: '0xce79be42bd15a541845c6f3e6d24e053460a8062e74082440f768705b6fa2b6a',
  //   hashLock: '0x6a98ad2137e52aaf0eb29f44fc97084bf45a33115a0850c4355a1dcfba95c1d6',
  //   sender: 'bc1qwjeylyfrkn8sckwux789dqtscafujhgy4xcddg',
  //   receiver: 'bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4',
  //   network: 'BTC',
  //   _id: '5e4148ea84702846cfe3a4e4',
  //   createdAt: '2020-02-10T12:13:30.057Z' } ]
  //...

  const currentBlock = await btcProvider.getBlockHeight();
  console.log(currentBlock);
  //616241
  
  const balance = await btcContract.getBalance(
    "bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4",
    network
  );
  console.log(adapter.parseFromNative(balance));
  //0.00183863

  // ================ SWAP ================
  const userInputSwap = {
    inputAmount: "0.00001",
    network: "BTC",
    outputAddress: "0xD6Ff8542eC1606772fF29946218a624b08A1D51E",
    outputAmount: "0.0004488",
    outputNetwork: "ETH",
    secret:
      "pond cram isolate chair float moon tongue firm spirit hand peace together",
    sender: "bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4"
  };

  const swap = adapter.formatInput(userInputSwap);
  console.log(swap);
  // { inputAmount: '1000',
  // network: 'BTC',
  // outputAddress: '0xD6Ff8542eC1606772fF29946218a624b08A1D51E',
  // outputAmount: '0.0004488',
  // outputNetwork: 'ETH',
  // secret: 'pond cram isolate chair float moon tongue firm spirit hand peace together',
  // sender: 'bc1qsdejyek0kw5n2m2xhesc9x5yj35wtsjj2uzpe4',
  // hashLock: '0x4af116ee28ecde1d2b71e49fb964fe76a20c694c94f6895c1baa8ed1d1b06dda',
  // receiver: 'tb1qc4mwllgvmy0xqsdexpm5v8g74ldmv698whnyrw',
  // expiration: 1581359277 }

  const swapHash = await btcContract.newContract(swap);
  console.log(swapHash);
  //5013c0b0005af10cd9db82e63a18e8bc248c843993f9433fcf00d3611663dc56

  // ================ REFUND ================
  const refund = {
    id: "5e41738da5d71552f72e2faf"
  };

  try {
    const refundHash = await btcContract.refund(refund);
    console.log(refundHash);
    //5013c0b0005af10cd9db82e63a18e8bc248c843993f9433fcf00d3611663dc56
  } catch (e) {
    console.log("Refund Error. Check timestamp and id params.");
  }

  // ================ WITHDRAW ================
  const withdraw = {
    id: "5e41738da5d71552f72e2faf",
    secret: "c5f715ae9c3f79aed4af080f881385e22cec3ecb2811740e7018daea79dcc15c"
  };

  try {
    const withdrawHash = await btcContract.withdraw(withdraw);
    console.log(withdrawHash);
    //5013c0b0005af10cd9db82e63a18e8bc248c843993f9433fcf00d3611663dc56
  } catch (e) {
    console.log("Withdraw Error. Check timestamp, id and secret params.");
  }
}

Last updated