# JavaScript (client wallets)

# Introduction

Our Javascript library is designed so you can rapidly and easily provide secure financial services to your users using standard web development knowledge. You no longer have to be a blockchain wizard to give the powers of magic internet money to your customers.

With this library you will be able to create advanced, non-custodial, in-browser wallets.

The mainnet library is currently in a beta stage. Things may change randomly. There is no backward compatibility guarantee yet, even though we try not to break things too often.

# Let's get programming

Note that this tutorial describes Browser + IndexedDB approach, which means that the wallets will be created and persisted inside of a user browser. See calling the REST API or Other programming languages for other approaches.

To get started using Bitcoin Cash on your site, include this tag in your <head> section:

<script src="https://cdn.mainnet.cash/mainnet-0.3.12.js"
 integrity="sha384-yNARexmnMZlxKP+/hVma4GE3pLz61GvdbS0JD5uvIRDTSfigiwpLghUG3aRlYcL2"
 crossorigin="anonymous"></script>

Note that the integrity part guarantees that the script haven't been tampered with. So if a hacker replaces it, the user's browser will not run the script. Or you can download the library and serve it locally.

Now, you can create a test wallet:

const wallet = await TestNetWallet.newRandom();

This wallet will not be persisted, if a user closes his browser, it's gone forever. See below for persistent wallets.

This creates a TestNet wallet.

What is TestNet? Where to get TestNet money and a wallet?

TestNet is where you test your application. TestNet money has no price. Opposite of TestNet is MainNet, which is what people usually mean when they talk about Bitcoin Cash network. You can get free TestNet money using the getTestnetSatoshis() call (see below) or here (opens new window).

If you need a wallet that supports the TestNet, download Electron Cash (opens new window) and run it using electron-cash --testnet flag. For example, on MacOS that would be:

/Applications/Electron-Cash.app/Contents/MacOS/Electron-Cash --testnet

To create a MainNet wallet (Bitcoin Cash production network):

const wallet = await Wallet.newRandom();

If you want to create a wallet from a mnemonic seed phrase, use this call:

const wallet = Wallet.fromSeed('.....');

TIP

Seed phrase wallets use the derivation path m/44'/0'/0'/0/0 by default (Bitcoin.com wallet compatibility)

Optionally, a BIP44 (opens new window) derivation path may be added as a second argument.

const wallet = Wallet.fromSeed("horse duck stapler...", "m/44'/1'/145'/0/0");

If you want to create a wallet from a WIF (private key), use this call:

const wallet = Wallet.fromWIF('.....');

Keep the private key and the seed phrase secret

Remember to keep the private key (in "WIF" form) and/or the seed phrase (aka "mnemonic") secret as they allow spending money from this wallet. You can access them using wallet.privateKeyWif and wallet.mnemonic (you'll also need the derivation path from wallet.derivationPath)

Available networks:

  • mainnet: Wallet
  • Testnet: TestNetWallet
  • RegTest: RegTestWallet (see below)

TIP

You can see a working demo here (opens new window) and a video of it here (opens new window)

# Named wallets (persistent)

Named wallets are used to saved the private key generated by the browser, so that you can run await Wallet.named(`user`) and always get the same wallet back.

Note that it's better to run something like await Wallet.named(`user:${id}`), i.e. use some ID of the user, so that if the same browser has multiple users, they'll all get their own wallet. (Like, if a user has multiple accounts on your site)

To create a persistent wallet (saved to the IndexedDB of user's browser):

const wallet = await TestNetWallet.named('user:1234');

user:1234 is an optional name for the wallet. The wallet is saved in user's browser for future re-use.

# Getting the balance

To get the balance of the wallet you can do this:

await seller.getBalance('usd') // 0.00
await seller.getBalance('bch') // 0.00
await seller.getBalance('sat') // 0

You can ask for usd, sat, bch (or satoshi, satoshis, sats - just in case you forget the exact name).

  • 1 satoshi = 0.000 000 01 Bitcoin Cash
  • 1 Bitcoin Cash = 100,000,000 satoshis

USD returns the amount at the current exchange rate.

# Watch-only wallets

You can find out a balance of any cashaddr (say bchtest:qq1234567) like this:

const wallet = await TestNetWallet.watchOnly('bchtest:qq1234567');
await wallet.getBalance('usd');

# Sending money

Let's create another wallet and send some of our money there:

const seller = await TestNetWallet.named('seller');

const txData = await wallet.send([
    [seller.depositAddress(), 0.01, 'USD'],
]);

Note that you can send to many addresses at once.

If your address holds SLP tokens, you have to use the wallet.slpAware().send([...]) method to prevent accidental token burning. SLP checks are a bit slow, so they are opt-in.

There is also an options parameter that specifies how money is spent, for example specifying which unspent outputs are used as inputs: {utxoIds: ["...", "..."]}. SLP awareness can also be passed as a member of options parameter: {slpAware: true}.

Let's print the balance of the seller's wallet:

console.log(await seller.getBalance('USD'));
// 0.01

Great! You've just made your first transaction!

Now you can send all of your money somewhere else:

const txData = await seller.sendMax(wallet.depositAddress());

# Waiting for a transaction

# QR codes

Let's say you want to display a QR code for you user to pay you money and show an alert when money arrives?

Let's display a QR code first. Create a placeholder first:

<p style="text-align: center;">
    <img src="https://cdn.mainnet.cash/wait.svg" style="width: 15em;" id="deposit">
</p>

Then you can replace it with an actual QR code of the deposit address:

document.querySelector('#deposit').src = wallet.getDepositQr().src;

# Waiting for balance

You can wait for a certain minimal balance on the wallet using the waitForBalance function.

const balance = await wallet.waitForBalance(1.0, 'usd');

The balance variable contains the actual balance of the wallet.

# Waiting for transaction

You can wait for a wallet transaction and halt the program execution until it arrives.

const rawTransaction = await wallet.waitForTransaction();

The returned object follows this specification (opens new window)

If you are willing to spy on monitor transactions of an address you do not own, you can create a watchOnly wallet.

# Waiting for block

If you want to wait for the next block or wait for blockhain to reach certain block height you can use the following method of the wallet's network provider:

const nextBlockInfo = await wallet.provider.waitForBlock();

const futureBlockInfo = await wallet.provider.waitForBlock(770000);

The response object's schema (opens new window) is simple:

{
  height: number;
  hex: string;
}

# Simple Ledger Protocol (SLP)

We currently fully support the SLP type 1 tokens specification (opens new window)

The interfaces were designed to be largely similar to those of BCH wallets.

Creating an SLP enabled wallet is similar to a BCH one, just add an .slp static accessor after the wallet class you want to use:

const wallet = await TestNetWallet.slp.newRandom();

The SLP wallets are using the m/44'/245'/0'/0/0 BIP44 derivation path unlike normal BCH wallets which use m/44'/0'/0'/0/0. This is done to lower the chances of accidental token burns.

If you want to instantiate an SLP wallet which will use a different derivation path (assuming you already have your BIP39 seed phrase):

const wallet = await Wallet.slp.fromSeed("abandon abandon abandon ...", "m/44'/123'/0'/0/0");

Note, that SLP-enabled wallets are by default SLP aware and token burn checks are ensured when spending UTXOs.

The SLP functions are then available via Wallet.slp accessor:

const slpAddress = wallet.slp.getDepositAddress();

const qrCode = wallet.slp.getDepositQr();

Note, that working with SLP tokens requires a certain amount of BCH available in your wallet so that you can pay miners for the SLP transactions.

# Token creation - Genesis

To create your own token you should prepare and broadcast a special genesis transaction containing all the information about the token being created: token name, ticker, decimals - number of significant digits after comma, initial token amount, url you want to associate with token. Some of these properties are optional.

With the endBaton parameter you can decide to keep the possibility of additional token creation, which is governed by so called minting baton or immediately discard this baton to make the token circulation amount to be fixed.

The transaction id in which the token is created will become its permanent and unique identifier.

Note, that there might be many tokens with the same name. Remember, that only 64 character long string-ids do identify your token uniquely und unambiguously.

In the following example 10000.00 MNC tokens will be created.

const genesisOptions = {
  name: "Mainnet coin",
  ticker: "MNC",
  decimals: 2,
  initialAmount: 10000,
  documentUrl: "https://mainnet.cash",
  endBaton: false
};
const {tokenId} =  await wallet.slp.genesis(genesisOptions)

# Looking up token information

If you want to get the genesis information of some token, you can simply call getTokenInfo:

const info = await wallet.slp.getTokenInfo(tokenId);

# Additional token creation - Minting

If you decide to increase the token circulation supply, you would need to mint more tokens. You are required to have the ownership of the minting baton to do so. Similarly to genesis, you can keep the baton or discard it.

In the following example we issue 50 more tokens we just created in genesis:

const mintOptions = {
  value: "10000",
  tokenId: tokenId,
  endBaton: false,
  tokenReceiverSlpAddr: "slptest:qqm4gsaa2gvk7flvsvj7f0w4rlq32vqhkq32uar866",
  batonReceiverSlpAddr: "slptest:qqm4gsaa2gvk7flvsvj7f0w4rlq32vqhkq32uar866"
}

const {txId, balance} = await wallet.slp.mint(mintOptions);

Optional tokenReceiverSlpAddr and batonReceiverSlpAddr allow to specify the receiver of tokens and minting baton. This is how you can pass the mintin baton to other authority.

# Sending tokens

Sending tokens around is easy and is very similar to sending BCH. You can include many send requests in one call too!

const {txId, balance} = await wallet.slp.send([
  {
    slpaddr: bobWallet.slp.slpaddr,
    value: 5,
    tokenId: tokenId
  }
]);

Or you can send all tokens available with a simple sendMax method

const result = await wallet.slp.sendMax(otherWallet.slp.slpaddr, tokenId);

Note, you can not send several different tokens in one go.

# Token balances

You can get all token balances of your wallet or a balance of a specific token with the following methods:

const tokenBalance = wallet.slp.getBalance(tokenId);
const allBalances = wallet.slp.getAllBalances();

# SLP address UTXOs

If you want to get the information about SLP UTXOs of an address, look up the locked satoshi values, etc., you can do the following call:

const utxos = wallet.slp.getFormattedSlpUtxos();

# Watching/waiting for transactions

You can set up a hook to monitor SLP transactions with the following:

const cancelFn = wallet.slp.watchTransactions((tx) => {
  ...
}, tokenId);

Leave out the tokenId to monitor all token transactions.

Call the cancelFn() to unsubscribe from receiving callback calls.

If you want just to wait for the next transaction you can use the following call:

const tx = await wallet.slp.waitForTransaction(tokenId);

This will halt the program execution until the transaction arrives.

# Watching/waiting for balance

Similarly a to transaction watching/waiting we provide the convenient methods to watch/wait for balance.

const cancelFn = wallet.slp.watchBalance((balance) => {
  ...
}, tokenId);

You can wait for the SLP wallet to reach a certain minimal token balance:

const actualBalance = await wallet.slp.waitForBalance(10, tokenId);

This will halt the program execution until the balance reaches the target value.

# Non-fungible tokens (NFT)

NFT1 is a simple extension to the SLP token type 1 protocol which allows many NFT tokens to be grouped together using a single ID. Having the ability to group NFTs in a provable manner opens the doors for many more token applications, and makes SLP more similar to other NFT protocols (e.g., ERC-721). NFT1 uses the same validation rules as SLP token type 1 with a few additional constraints. See reference (opens new window). Non-fungible tokens can be produced by simply minting a non-divisible token supply of 1 without a minting baton.

All operations apart from genesis and minting, which are used for SLP tokens Type1, support the NFT tokens and are used the same way with same interfaces.

# NFT Parent Genesis

To create the NFT parent group use the following code snippet

const genesisOptions = {
  name: "Mainnet NFT Parent",
  ticker: "MNC_NFTP",
  decimals: 0,
  initialAmount: 10000,
  documentUrl: "https://mainnet.cash",
  endBaton: false
};
const genesisResult =  await wallet.slp.nftParentGenesis(genesisOptions);
const parentTokenId = genesisResult.tokenId;

Note: these tokens are transferrable and mintable. Decimal places of 0 is adviced.

# NFT Child Genesis

NFT child tokens are unique and one of a kind. To create an NFT child token use the following code snippet

const genesisOptions = {
  name: "Mainnet NFT Child",
  ticker: "MNC_NFTC",
  decimals: 0,
  initialAmount: 1,
  documentUrl: "https://mainnet.cash",
  endBaton: true,
  parentTokenId: parentTokenId
};
const {tokenId} =  await wallet.slp.nftParentGenesis(genesisOptions);

In the process of the child genesis, a parent token of quantity 1 will be spent, so ensure you possess some. If you have more than 1 (n), the tokens will be split into (n-1) and 1.

Note: these tokens are transferrable but not mintable. Regardless of options supplied, the following options will be overriden: endBaton will be set to true, initialAmount: 0, decimals: 0. Otherwise they will be considered as invalid by the SLP validators.

# List of your NFTs

See Token Balances

# TestNet faucet

You can have some TestNet satoshi or SLP tokens for your convenience. Visit our faucet refilling station at https://rest-unstable.mainnet.cash/faucet.html (opens new window)

Your address will be refilled up to 10000 TestNet satoshi or up to 10 SLP tokens upon a call. There are request rate limiters set up to prevent abuse.

We've integrated the faucet into the library so that you can do easy calls like the following:

const txid = await wallet.getTestnetSatoshis();
...
const sendResponse = await wallet.returnTestnetSatoshis();

Same for SLP:

const txid = await wallet.getTestnetSlp(tokenId);
...
const slpSendResponse = await wallet.returnTestnetSlp(tokenId);

# Escrow contracts

WARNING

Alpha release

Ok, let's now assume that you are building a service where you want to connect a buyer and a seller (a freelance marketplace or a non-custodial exchange), but at the same time you don't want to hold anyone's money, but only act as an arbiter in case something goes wrong. It's possible in Bitcoin Cash and it's called "an escrow contract".

You'll need three addresses for this: buyer, seller and arbiter.

Note: The source for the contract is here (opens new window).

  1. Buyer sends the money to the contract and could then release the money to the seller
  2. The seller could refund the buyer
  3. Arbiter can either complete the transaction to the seller or refund to the buyer, but cannot steal the money
let escrow = new EscrowContract({
  arbiterAddr: arbiter.getDepositAddress(),
  buyerAddr: buyer.getDepositAddress(),
  sellerAddr: seller.getDepositAddress(),
});

You can now send money to the contract:

await buyer.send([ [ escrow.getAddress(), 3700, "satoshis" ], ]);

Check the balance of the contract (in satoshis):

await escrow.getBalance()
// 3700

Note: Escrow contract is big (in bytes) and requires a big fee, so the minimum what you can send to it is about 3700 satoshis.

Now, we can execute the necessary functions:

  1. Buyer releases the funds
await escrow.run(buyer.privateKeyWif, "spend");
  1. Seller refunds
await escrow.run(seller.privateKeyWif, "refund");
  1. Arbiter releases the funds or refunds
await escrow.run(arbiter.privateKeyWif, "spend");
await escrow.run(arbiter.privateKeyWif, "refund");

# Saving the contract to the database

The arbiter needs to save the contract somewhere (in a database or some other storage), so that when the time comes, he could execute the necessary functions:

Save:

const contractId = escrow.toString();

Restore it later:

const restoredEscrow = EscrowContract.fromId(contractId);

The escrow object implements a CashScript contract from a stock template, but if the template doesn't suit your needs, it is also possible to use any contract written in CashScript (opens new window).

pragma cashscript ^0.5.3;

contract escrow(bytes20 sellerPkh, bytes20 buyerPkh, bytes20 arbiterPkh, int contractAmount, int contractNonce) {

    function spend(pubkey signingPk, sig s, int amount, int nonce) {
        require(hash160(signingPk) == arbiterPkh || hash160(signingPk) == buyerPkh);
        require(checkSig(s, signingPk));
        require(amount >= contractAmount);
        require(nonce == contractNonce);
        bytes34 output = new OutputP2PKH(bytes8(amount), sellerPkh);
        require(hash256(output) == tx.hashOutputs);
    }

    function refund(pubkey signingPk, sig s, int amount, int nonce) {
        require(hash160(signingPk) == arbiterPkh||hash160(signingPk) == sellerPkh);
        require(checkSig(s, signingPk));
        require(amount >= contractAmount);
        require(nonce == contractNonce);
        bytes34 output = new OutputP2PKH(bytes8(amount), buyerPkh);
        require(hash256(output) == tx.hashOutputs);
    }
}

# Generic CashScript

WARNING

Alpha release

What is CashScript?

From CashScript.org:

CashScript (opens new window) is a high-level programming language for smart contracts on Bitcoin Cash. It offers a strong abstraction layer over Bitcoin Cash' native virtual machine, Bitcoin Script. Its syntax is based on Ethereum's smart contract language Solidity, but its functionality is very different since smart contracts on Bitcoin Cash differ greatly from smart contracts on Ethereum. For a detailed comparison of them, refer to the blog post Smart Contracts on Ethereum, Bitcoin and Bitcoin Cash (opens new window).

In the EscrowContract example in the previous section, the contract source is hardcoded. But with a generic Contract, the full script is defined by the user.

Contracts are objects that simply wrap a CashScript Contract, with utilities to serialize and deserialize them. In addition, there are some functions on wallets to facilitate sending arguments to the contract.

For example, taking a simple pay with timeout example from the CashScript playground (opens new window):

pragma cashscript ^0.5.0;

contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
    // Require recipient's signature to match
    function transfer(sig recipientSig) {
        require(checkSig(recipientSig, recipient));
    }

    // Require timeout time to be reached and sender's signature to match
    function timeout(sig senderSig) {
        require(checkSig(senderSig, sender));
        require(tx.time >= timeout);
    }
}

This can be loaded up by passing the script, constructor arguments and network to a new Contract.

Before you dive in, it might be a good time to consider using RegTest?

The below code is for TestNet, but it's highly recommended to develop and test your contracts on RegTest. Besides the ability to give yourself free coins, it's also possible to mine blocks to test your contract functioning over different time conditions.


const script = `contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
    // Require recipient's signature to match
    function transfer(sig recipientSig) {
        require(checkSig(recipientSig, recipient));
    }

    // Require timeout time to be reached and sender's signature to match
    function timeout(sig senderSig) {
        require(checkSig(senderSig, sender));
        require(tx.time >= timeout);
    }
}`
const alice = await TestNetWallet.newRandom();

In the case that you only have Charlie's cashaddr, it won't be possible to get the full public key, but a public key hash may be used in the contract instead

const charlie = await TestNetWallet.newRandom();

In javascript, the contract can take a binary argument as a Uint8Array or hexadecimal strings just like CashScript, but passing passing true here causes getPublicKey() to return hex, (in case you wanted to paste into the data into CashScript playground.) the default is a Uint8Array.

const alicePk = alice.getPublicKey(true);
const charliePk = charlie.getPublicKey(true);

Next pass the script, arguments and network to create a new Contract.

// Some block height very far in the past.
const after = 215;

let contract = new Contract(
  script,
  [alicePk, charliePk, after],
  Network.TESTNET
);

This will give you a contract object that can be serialized and deserialized just like a wallet:

// serialized to a string
let contractStr = contract.toString()
//testnet:TURSa05EV...nPT06TWpFMQ==:cHJhZ...gfQp9:61149027"

// recreate the Contract object from a string
let contractCopy = Contract.fromId(contractStr)

The deposit address is available with the same interface as a wallet.

contract.getDepositAddress()
//"bchtest:pzpt6y6ganwagvr6far4pe82yvtflx60zstdyhlzld"

The interface to list utxos is identical to wallets as well...

await contract.getUtxos()
// [
//  {
//   txid: "8806ef4f1185f268a5083fbd651d974b939d2c68afa2be28652c4ccce06703c4", 
//   vout: 0, 
//   satoshis: 1000, 
//   height: 0
//  }
// ]

Once the contract is funded, contract functions may be called just like in CashScript:

let transferFn = contract.getContractFunction("transfer");

// the signature template for charlie is available on the wallet
const sig = charlie.getSignatureTemplate();
const charlieAddr = charlie.getDepositAddress()

// The function may be called by passing the arguments to the function
// specifying a `to` destination and a CashScript function method, i.e. send
let txn = await transferFn(sig).to(charlieAddr, 7000).send();  

Wallets have the following convenience methods for passing data to CashScript:

Wallet Method Returns CashScript Type
getSignatureTemplate() SignatureTemplate sig
getPublicKeyCompressed() Hex String or Uint8Array pubkey
getPublicKeyHash() Hex String or Uint8Array bytes20

In Javascript, passing either hex or a Uint8Array to CashScript will work.

# Utilities

# Currency conversions

Need to find out how many BCH are there currently in 1 USD, or find out how many satoshis are there in 100 USD? Easy!

await convert(100, "usd", "sat")
// 28067024

# Signed Messages

One of the perks of having a wallet is the ability to sign message text or verify the signatures of other parties using their public key.

Full-nodes and SPV wallets often include this feature as standard.

# Signing a message with a wallet

Let's try signing with an example from a common test case:

message = "Chancellor on brink of second bailout for banks"
francisWallet = await Wallet.fromWIF(
      `L1TnU2zbNaAqMoVh65Cyvmcjzbrj41Gs9iTLcWbpJCMynXuap6UN`
    );

francisWallet.cashaddr
// "bitcoincash:qqehccy89v7ftlfgr9v0zvhjzyy7eatdkqt05lt3nw"

signature = (await francisWallet.sign(message)).signature;
// H/9jMOnj4MFbH3d7t4yCQ9i7DgZU/VZ278w3+ySv2F4yIsdqjsc5ng3kmN8OZAThgyfCZOQxZCWza9V5XzlVY0Y=

// or 

sigResult = await francisWallet.sign(message);

Where the full sigResult result is:


{
  "raw": {
    "ecdsa": "/2Mw6ePgwVsfd3u3jIJD2LsOBlT9VnbvzDf7JK/YXjIix2qOxzmeDeSY3w5kBOGDJ8Jk5DFkJbNr1XlfOVVjRg==",
    "schnorr": "rSeWfhxN6tI+3hNQpHwU6E+pZC34rk6gR/h8hqxS0YjUd6mxsOd4OCmMkGJXsqNvVZ1F/Fs/Y81dyzSDBhxp9w==",
    "der": "MEUCIQD/YzDp4+DBWx93e7eMgkPYuw4GVP1Wdu/MN/skr9heMgIgIsdqjsc5ng3kmN8OZAThgyfCZOQxZCWza9V5XzlVY0Y="
  },
  "details": {
    "recoveryId": 0,
    "compressed": true,
    "messageHash": "gE9BDBFAOqW+yoOzABjnM+LQRWHd4dvUVrsTR+sIWsU="
  },
  "signature": "H/9jMOnj4MFbH3d7t4yCQ9i7DgZU/VZ278w3+ySv2F4yIsdqjsc5ng3kmN8OZAThgyfCZOQxZCWza9V5XzlVY0Y="
}

Please be aware

The above contains both ECDSA and Schnorr signatures. If they had been created using the same random nonce, an attacker could derive the private key. To avoid this risk, the underlying library (libauth (opens new window)) uses nonces for Schorr signatures with the additional data field set to Schnorr+SHA256. Such measures are an important security requirement for any financial software producing both types of signatures.

For most cases, the relevant information here is the "signature" field, which can be used in an SPV wallet such as electron cash or with bitcoin.com's verify tool (opens new window). The following signature will validate as belonging to Francis' address:

Bitcoin Address: bitcoincash:qqehccy89v7ftlfgr9v0zvhjzyy7eatdkqt05lt3nw

Message: Chancellor on brink of second bailout for banks

Signature: H/9jMOnj4MFbH3d7t4yCQ9i7DgZU/VZ278w3+ySv2F4yIsdqjsc5ng3kmN8OZAThgyfCZOQxZCWza9V5XzlVY0Y=

It should also be noted that the signature is "recoverable", meaning the publicKey can be derived from it and the message. This is important when validating against a cashaddr, because only a publicKeyHash can be derived from a cashaddr.

If one of the "raw" signatures are used instead, the publicKey may have to be passed manually.

# Verifying a message with a wallet

To verify the above signature (without having access to the private key), by using a watchOnly wallet to represent the party in the example above.


francisPublic = await Wallet.watchOnly("bitcoincash:qqehccy89v7ftlfgr9v0zvhjzyy7eatdkqt05lt3nw")

message = "Chancellor on brink of second bailout for banks"
sig = "H/9jMOnj4MFbH3d7t4yCQ9i7DgZU/VZ278w3+ySv2F4yIsdqjsc5ng3kmN8OZAThgyfCZOQxZCWza9V5XzlVY0Y="

verifyResult = await francisPublic.verify(message, sig);

where verifyResult is

{
  "valid": true,
  "details": {
    "signatureValid": true,
    "signatureType": "recoverable",
    "messageHash": "gE9BDBFAOqW+yoOzABjnM+LQRWHd4dvUVrsTR+sIWsU=",
    "publicKeyHashMatch": true
  }
}

In the default case, with a "signatureType" of "recoverable", the cashaddr publicKeyHash has been checked against the hashed publicKey, which is recovered from the provided message and signature.

Under the hood, all signature types, the message is serialized or formatted in four parts before hashing:

\x18                       // 1) length the prefix
Bitcoin Signed Message:\n  // 2) A prefix w/newline
<\x???>                    // 3) length of the message
<message>                  // 4) the message string as utf8 encoded binary 

The above message formatting is typically handled automatically by the signing software (i.e. wallet.sign(...)), and the messageHash is the double sha256 of the above as binary. For verification, only if the signature itself is valid and the recovered publicKey is valid for the provided cashaddr will the response have "valid":true, and the additional details given may be safely ignored in most cases.

# RegTest wallets

During the local development and testing, you might not want to use TestNet coins, so you can use so-called "RegTest wallets".

What is RegTest?

RegTest is a mode, in which you can run your Bitcoin Cash node locally, and you can get as many test coins as you need, but they exist on your machine only. RegTest wallets are supported by the mainnet library.

A full Bitcoin node, an Electrum server and open Postgres server configuration is available for testing in a Docker Compose file at jest/regtest-docker-compose.yml

It can be brought up with:

./jest/docker/start.sh 

To stop it:

./jest/docker/stop.sh

The Electrum server (Fulcrum) is available at ws://127.0.0.1:60003 on your local machine.
The regtest BCHN node is on port 18443 available with RPC using credentials in .env.regtest. An open Postgres server is also available on port 15432

To use this wallet from your code:

const wallet = await RegTestWallet.newRandom();

# WebSockets

We provide some functionality over websockets where traditional REST servers would timeout. Examples are waiting for transactions and watching balances . Websockets allow to subscribe to server events, sending responses and notifications asynchronously.

Check out the jsfiddle demo (opens new window)

Websockets are supported by all major browsers and using them is easy, no external libraries are needed:

let socket = new WebSocket("wss://rest-unstable.mainnet.cash/api/v1/wallet");
socket.onopen = (event) => {
  const request = {method: "watchBalance", data: {cashaddr: address}};
  socket.send(JSON.stringify(request));
};

socket.onmessage = (event) => {
  const balance = JSON.parse(event.data);
  // do something
};

The output of this code snippet will look like this:

{
  bch: 0.00009383,
  sat: 9383,
  usd: 0.029358468699999998
}

See websocket API reference

# Changing Electrum Servers

By default, creating a new wallet will use a common connection to a single server communicating with the electrum cash protocol (opens new window).

These connections are stored on globalThis under a variable matching ticker for the network (BCH, tBCH, rBCH).

If you need to create a new connection manually, it can be done by passing the network and servers, where servers is either a single url or an array of urls.

let conn = new Connection(
  "mainnet",
  "wss://bch.imaginary.cash:50004" 
  )
await conn.networkProvider.getBlockHeight()
// 669347

This connection can be used to replace the common provider on glboalThis.BCH or assigned to a particular wallet by overwriting the provider object of the wallet.