@ordinalsbot/bitcoin-fee-estimator
Version:
A library for calculating Bitcoin transaction fees
110 lines (97 loc) • 2.76 kB
text/typescript
const BECH32_ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
const BECH32_CONST = 1;
const BECH32M_CONST = 0x2bc830a3;
export function bech32Polymod(values: number[]): number {
const GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
let chk = 1;
for (const v of values) {
const b = chk >> 25;
chk = ((chk & 0x1ffffff) << 5) ^ v;
for (let i = 0; i < 5; i++) {
if ((b >> i) & 1) {
chk ^= GEN[i];
}
}
}
return chk;
}
export function bech32VerifyChecksum(
hrp: string,
data: number[],
encoding: "bech32" | "bech32m",
): boolean {
const consts = encoding === "bech32" ? BECH32_CONST : BECH32M_CONST;
return bech32Polymod([...bech32HrpExpand(hrp), ...data]) === consts;
}
function bech32HrpExpand(hrp: string): number[] {
const result = [];
for (let i = 0; i < hrp.length; i++) {
result.push(hrp.charCodeAt(i) >> 5);
}
result.push(0);
for (let i = 0; i < hrp.length; i++) {
result.push(hrp.charCodeAt(i) & 31);
}
return result;
}
export function bech32Decode(
bechString: string,
): { hrp: string; words: number[]; encoding: "bech32" | "bech32m" } | null {
let hasLower = false;
let hasUpper = false;
for (let i = 0; i < bechString.length; i++) {
if (bechString[i] < "!" || bechString[i] > "~") return null;
if (bechString[i] >= "a" && bechString[i] <= "z") hasLower = true;
if (bechString[i] >= "A" && bechString[i] <= "Z") hasUpper = true;
}
if (hasLower && hasUpper) return null;
const bechStringLower = bechString.toLowerCase();
const pos = bechStringLower.lastIndexOf("1");
if (
pos < 1 ||
pos + 7 > bechStringLower.length ||
bechStringLower.length > 90
)
return null;
const hrp = bechStringLower.substring(0, pos);
const data = [];
for (let i = pos + 1; i < bechStringLower.length; i++) {
const c = bechStringLower[i];
const d = BECH32_ALPHABET.indexOf(c);
if (d === -1) return null;
data.push(d);
}
if (
!bech32VerifyChecksum(hrp, data, "bech32") &&
!bech32VerifyChecksum(hrp, data, "bech32m")
) {
return null;
}
const encoding = bech32VerifyChecksum(hrp, data, "bech32")
? "bech32"
: "bech32m";
return { hrp, words: data.slice(0, -6), encoding };
}
export function bech32ConvertWords(
data: number[],
inBits: number,
outBits: number,
pad: boolean,
): number[] {
let value = 0;
let bits = 0;
const maxV = (1 << outBits) - 1;
const result = [];
for (const d of data) {
value = (value << inBits) | d;
bits += inBits;
while (bits >= outBits) {
bits -= outBits;
result.push((value >> bits) & maxV);
}
}
if (pad && bits > 0) {
result.push((value << (outBits - bits)) & maxV);
}
return result;
}