/**
* This module provides conversion and validation functions for units
* (Satoshis, BTC) and hex strings.
*
* @module utils
*/
import BigNumber from "bignumber.js";
import { crypto } from "bitcoinjs-lib";
const VALID_BASE64_REGEX =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/;
const VALID_HEX_REGEX = /^[0-9A-Fa-f]*$/;
/**
* Converts a byte array to its hex representation.
*
* @param {number[] | Buffer} byteArray - input byte array
* @returns {string} hex representation of input array
*
* @example
* import {toHexString} from "unchained-bitcoin";
* const hex = toHexString([255, 0, 15, 16, 31, 32]);
* console.log(hex) // ff000f101f20
*
*/
export function toHexString(byteArray) {
return Array.prototype.map.call(byteArray, function (byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}
/**
* Validate whether the given string is base64.
*
* - Valid base64 consists of whole groups of 4 characters containing `a-z`, `A-Z`, 0-9,
* `+`, or `/`. The end of the string may be padded with `==` or `=` to
* complete the four character group.
*
* @param {string} inputString - string to validate
* @returns {boolean} true if base64, false otherwise.
*/
export function validBase64(inputString) {
return (VALID_BASE64_REGEX).test(inputString);
}
/**
* Validate whether the given string is hex.
*
* - Valid hex consists of an even number of characters 'a-f`, `A-F`,
* or `0-9`. This is case-insensitive.
*
* - The presence of the common prefix `0x` will make the input be
* considered invalid (because of the` `x`).
*
* @param {string} inputString - string to validate
* @returns {string} empty if valid or corresponding validation message if not
*
* @example
* import {validateHex} from "unchained-bitcoin";
* console.log(validateHex('00112233gg')) // "Invalid hex: ..."
* console.log(validateHex('0xdeadbeef')) // "Invalid hex: ..."
* console.log(validateHex('deadbeef')) // ""
* console.log(validateHex('DEADbeef')) // ""
*
*/
export function validateHex(inputString) {
if (inputString.length % 2) {
return 'Invalid hex: odd-length string.';
}
if (!VALID_HEX_REGEX.test(inputString)) {
return 'Invalid hex: only characters a-f, A-F and 0-9 allowed.';
}
return '';
}
/**
* Convert a value in Satoshis to BTC.
*
* - Accepts both positive and negative input values.
* - Rounds down (towards zero) input value to the nearest Satoshi.
*
* @param {BigNumber|string|number} satoshis - value in Satoshis
* @returns {BigNumber} value in BTC
*
* @example
* import {satoshisToBitcoins} from "unchained-bitcoin";
* console.log(satoshisToBitcoins(123450000)); // 1.2345
* console.log(satoshisToBitcoins('0.5')); // 0
* console.log(satoshisToBitcoins('-100000000.5')); // -1.0
*
*/
export function satoshisToBitcoins(satoshis) {
const originalValue = BigNumber(satoshis);
const roundedValue = originalValue.integerValue(BigNumber.ROUND_DOWN);
return roundedValue.shiftedBy(-8);
}
/**
* Convert a value in BTC to Satoshis.
*
* - Accepts both positive and negative input values.
* - Rounds down output value to the nearest Satoshi.
*
* @param {BigNumber|string|number} btc - value in BTC
* @returns {BigNumber} value in satoshis
*
* @example
* import {bitcoinsToSatoshis} from "unchained-bitcoin";
* console.log(bitcoinsToSatoshis(1.2345)); // 123450000
* console.log(bitcoinsToSatoshis(-1.2345)); // -123450000
*/
export function bitcoinsToSatoshis(btc) {
return BigNumber(btc).shiftedBy(8).integerValue(BigNumber.ROUND_DOWN);
}
export const ZERO = BigNumber(0);
/**
* Given a buffer as a digest, pass through sha256 and ripemd160
* hash functions. Returns the result
* @param {Buffer} buf - buffer to get hash160 of
* @returns {Buffer} hash160 of the given buffer
*/
export function hash160(buf) {
return crypto.ripemd160(crypto.sha256(buf));
}