UNPKG

@atomiqlabs/sdk-lib

Version:

Basic SDK functionality library for atomiq

184 lines (149 loc) 6.05 kB
import {decode as bolt11Decode} from "@atomiqlabs/bolt11"; import {ToBTCLNWrapper} from "./ToBTCLNWrapper"; import {isIToBTCSwapInit, IToBTCSwap, IToBTCSwapInit} from "../IToBTCSwap"; import {SwapType} from "../../SwapType"; import {ChainType, SwapData} from "@atomiqlabs/base"; import {Buffer} from "buffer"; import {sha256} from "@noble/hashes/sha2"; import {IntermediaryError} from "../../../errors/IntermediaryError"; import {LNURL, LNURLDecodedSuccessAction, LNURLPaySuccessAction, isLNURLPaySuccessAction} from "../../../utils/LNURL"; import {BtcToken, TokenAmount, Token, BitcoinTokens, toTokenAmount} from "../../Tokens"; import {getLogger} from "../../../utils/Utils"; export type ToBTCLNSwapInit<T extends SwapData> = IToBTCSwapInit<T> & { confidence: number; pr: string; lnurl?: string; successAction?: LNURLPaySuccessAction; }; export function isToBTCLNSwapInit<T extends SwapData>(obj: any): obj is ToBTCLNSwapInit<T> { return typeof (obj.confidence) === "number" && typeof (obj.pr) === "string" && (obj.lnurl == null || typeof (obj.lnurl) === "string") && (obj.successAction == null || isLNURLPaySuccessAction(obj.successAction)) && isIToBTCSwapInit<T>(obj); } export class ToBTCLNSwap<T extends ChainType = ChainType> extends IToBTCSwap<T> { protected outputToken: BtcToken<true> = BitcoinTokens.BTCLN; protected readonly TYPE = SwapType.TO_BTCLN; private readonly confidence: number; private readonly pr: string; readonly paymentHash: string; lnurl?: string; successAction?: LNURLPaySuccessAction; private secret?: string; constructor(wrapper: ToBTCLNWrapper<T>, init: ToBTCLNSwapInit<T["Data"]>); constructor(wrapper: ToBTCLNWrapper<T>, obj: any); constructor(wrapper: ToBTCLNWrapper<T>, initOrObj: ToBTCLNSwapInit<T["Data"]> | any) { if(isToBTCLNSwapInit(initOrObj)) initOrObj.url += "/tobtcln"; super(wrapper, initOrObj); if(!isToBTCLNSwapInit(initOrObj)) { this.confidence = initOrObj.confidence; this.pr = initOrObj.pr; this.lnurl = initOrObj.lnurl; this.successAction = initOrObj.successAction; this.secret = initOrObj.secret; } this.paymentHash = this.getPaymentHash().toString("hex"); this.logger = getLogger("ToBTCLN("+this.getIdentifierHashString()+"): "); this.tryCalculateSwapFee(); } _setPaymentResult(result: { secret?: string; txId?: string }, check: boolean = false): Promise<boolean> { if(result==null) return Promise.resolve(false); if(result.secret==null) throw new IntermediaryError("No payment secret returned!"); if(check) { const secretBuffer = Buffer.from(result.secret, "hex"); const hash = Buffer.from(sha256(secretBuffer)); if(!hash.equals(this.getPaymentHash())) throw new IntermediaryError("Invalid payment secret returned"); } this.secret = result.secret; return Promise.resolve(true); } ////////////////////////////// //// Amounts & fees getOutput(): TokenAmount<T["ChainId"], BtcToken<true>> { const parsedPR = bolt11Decode(this.pr); const amount = (BigInt(parsedPR.millisatoshis) + 999n) / 1000n; return toTokenAmount(amount, this.outputToken, this.wrapper.prices); } ////////////////////////////// //// Getters & utils getOutputTxId(): string | null { return this.getLpIdentifier(); } /** * Returns the lightning BOLT11 invoice where the BTC will be sent to */ getLightningInvoice(): string { return this.pr; } /** * Returns payment secret (pre-image) as a proof of payment */ getSecret(): string | null { return this.secret; } /** * Returns the confidence of the intermediary that this payment will succeed * Value between 0 and 1, where 0 is not likely and 1 is very likely */ getConfidence(): number { return this.confidence; } getIdentifierHash(): Buffer { const paymentHashBuffer = this.getPaymentHash(); if(this.randomNonce==null) return paymentHashBuffer; return Buffer.concat([paymentHashBuffer, Buffer.from(this.randomNonce, "hex")]); } getPaymentHash(): Buffer { if(this.pr==null) return null; const parsed = bolt11Decode(this.pr); return Buffer.from(parsed.tagsObject.payment_hash, "hex"); } protected getLpIdentifier(): string { if(this.pr==null) return null; const parsed = bolt11Decode(this.pr); return parsed.tagsObject.payment_hash; } getRecipient(): string { return this.lnurl ?? this.pr; } ////////////////////////////// //// LNURL-pay /** * Is this an LNURL-pay swap? */ isLNURL(): boolean { return this.lnurl!=null; } /** * Gets the used LNURL or null if this is not an LNURL-pay swap */ getLNURL(): string | null { return this.lnurl; } /** * Checks whether this LNURL payment contains a success message */ hasSuccessAction(): boolean { return this.successAction!=null; } /** * Returns the success action after a successful payment, else null */ getSuccessAction(): LNURLDecodedSuccessAction | null { return LNURL.decodeSuccessAction(this.successAction, this.secret); } ////////////////////////////// //// Storage serialize(): any { return { ...super.serialize(), paymentHash: this.getPaymentHash().toString("hex"), pr: this.pr, confidence: this.confidence, secret: this.secret, lnurl: this.lnurl, successAction: this.successAction }; } }