UNPKG

@atomiqlabs/sdk

Version:

atomiq labs SDK for cross-chain swaps between smart chains and bitcoin

165 lines (151 loc) 5.74 kB
import {BTC_NETWORK, isBytes, PubT, validatePubkey} from "@scure/btc-signer/utils"; import {Buffer} from "buffer"; import {Address, OutScript, Transaction} from "@scure/btc-signer"; import {CoinselectAddressTypes} from "../bitcoin/coinselect2"; import { randomBytes } from "./Utils"; export function fromOutputScript(network: BTC_NETWORK, outputScriptHex: string): string { return Address(network).encode(OutScript.decode(Buffer.from(outputScriptHex, "hex"))); } export function toOutputScript(network: BTC_NETWORK, address: string): Buffer { const outputScript = Address(network).decode(address); switch(outputScript.type) { case "pkh": case "sh": case "wpkh": case "wsh": return Buffer.from(OutScript.encode({ type: outputScript.type, hash: outputScript.hash })); case "tr": try { return Buffer.from(OutScript.encode({ type: "tr", pubkey: outputScript.pubkey })); } catch (e: any) { let msg = ""; if(e.name!=null) msg += ": "+e.name; if(e.message!=null) msg += ": "+e.message; if(typeof(e)==="string") msg += ": "+e; msg += ", isBytes: "+isBytes(outputScript.pubkey); try { validatePubkey(outputScript.pubkey, PubT.schnorr) msg += ", validatePubkey: success"; } catch (e: any) { msg += ", validatePubkeyError: "; if(e.name!=null) msg += ": "+e.name; if(e.message!=null) msg += ": "+e.message; if(typeof(e)==="string") msg += ": "+e; } throw new Error(msg); } } throw new Error(`Unrecognized output script type: ${outputScript.type}`); } export function toCoinselectAddressType(outputScript: Uint8Array): CoinselectAddressTypes { const data = OutScript.decode(outputScript); switch(data.type) { case "pkh": return "p2pkh"; case "sh": return "p2sh-p2wpkh"; case "wpkh": return "p2wpkh" case "wsh": return "p2wsh" case "tr": return "p2tr" } throw new Error("Unrecognized address type!"); } function getDummySpec(type: CoinselectAddressTypes) { switch(type) { case "p2pkh": return { type: "pkh", hash: randomBytes(20) } as const; case "p2sh-p2wpkh": return { type: "sh", hash: randomBytes(20) } as const; case "p2wpkh": return { type: "wpkh", hash: randomBytes(20) } as const; case "p2wsh": return { type: "wsh", hash: randomBytes(32) } as const; case "p2tr": return { type: "tr", pubkey: Buffer.from("0101010101010101010101010101010101010101010101010101010101010101", "hex") } as const; } throw new Error("Unrecognized address type!"); } export function getDummyOutputScript(type: CoinselectAddressTypes): Uint8Array { return OutScript.encode(getDummySpec(type)); } export function getDummyAddress(network: BTC_NETWORK, type: CoinselectAddressTypes): string { return Address(network).encode(getDummySpec(type)); } /** * General parsers for PSBTs, can parse hex or base64 encoded PSBTs * @param _psbt */ export function parsePsbtTransaction(_psbt: Transaction | string): Transaction { if (typeof (_psbt) === "string") { let rawPsbt: Buffer; if (/^(?:[0-9a-fA-F]{2})+$/.test(_psbt)) { //Hex rawPsbt = Buffer.from(_psbt, "hex"); } else if (/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(_psbt)) { //Base64 rawPsbt = Buffer.from(_psbt, "base64"); } else { throw new Error("Provided psbt string not base64 nor hex encoded!"); } return Transaction.fromPSBT(rawPsbt, { allowUnknownOutputs: true, allowUnknownInputs: true, allowLegacyWitnessUtxo: true, }); } else { return _psbt; } } export function getVoutIndex(psbt: Transaction, network: BTC_NETWORK, address: string, amount: bigint): number | undefined { const script = toOutputScript(network, address); for(let i=0;i<psbt.outputsLength;i++) { const output = psbt.getOutput(i); if( output.amount===amount && output.script!=null && script.equals(Buffer.from(output.script)) ) { return i; } } } export function getSenderAddress(psbt: Transaction, network: BTC_NETWORK, inputIndex: number = 0): string | undefined { if(psbt.inputsLength<=inputIndex) return undefined; const input = psbt.getInput(inputIndex); let script: Uint8Array | undefined; if(input.witnessUtxo?.script!=null) { script = input.witnessUtxo.script as Uint8Array; } else if(input.nonWitnessUtxo!=null && input.index!=null) { script = input.nonWitnessUtxo.outputs[input.index]?.script; } if(script==null) return undefined; try { return Address(network).encode(OutScript.decode(script)); } catch (e) { return Buffer.from(script).toString("hex"); } }