UNPKG

@atomiqlabs/sdk-lib

Version:

Basic SDK functionality library for atomiq

159 lines (136 loc) 5.84 kB
import { ChainEvent, ChainSwapType, ChainType, InitializeEvent, SpvVaultClaimEvent, SpvVaultCloseEvent, SpvVaultFrontEvent, SwapEvent } from "@atomiqlabs/base"; import {ISwap} from "../swaps/ISwap"; import {EventListener} from "@atomiqlabs/base/src/events/ChainEvents"; import {SwapType} from "../swaps/enums/SwapType"; import {UnifiedSwapStorage} from "../storage/UnifiedSwapStorage"; import {getLogger} from "../utils/Utils"; function chainEventToEscrowHash(event: ChainEvent<any>) { if(event instanceof SwapEvent) return event.escrowHash; if( event instanceof SpvVaultFrontEvent || event instanceof SpvVaultClaimEvent || event instanceof SpvVaultCloseEvent ) return event.btcTxId; } export type SwapEventListener< T extends ChainType, S extends ISwap<T> > = (event: ChainEvent<T["Data"]>, swap: S) => Promise<void>; const logger = getLogger("UnifiedSwapEventListener: "); export class UnifiedSwapEventListener< T extends ChainType > { readonly storage: UnifiedSwapStorage<T>; readonly events: T["Events"]; readonly listeners: { [key in SwapType]?: { listener: SwapEventListener<T, any>, reviver: new (obj: any) => ISwap<T> } } = {}; constructor(unifiedStorage: UnifiedSwapStorage<T>, events: T["Events"]) { this.storage = unifiedStorage; this.events = events; } async processEvents(events: ChainEvent<T["Data"]>[]) { const swapsByEscrowHash: {[key: string]: ISwap<T>} = {}; events.forEach(event => { swapsByEscrowHash[chainEventToEscrowHash(event)] = null; }); logger.debug("processEvents(): Processing events with escrow hashes: ", Object.keys(swapsByEscrowHash)); const swaps = await this.storage.query<ISwap<T>>( [ [{key: "escrowHash", value: Object.keys(swapsByEscrowHash)}] ], (val: any) => { const obj = this.listeners[val.type]; if(obj==null) return null; return new obj.reviver(val); } ); swaps.forEach(swap => swapsByEscrowHash[swap._getEscrowHash()] = swap); //We need to do this because FromBTCLNAutoSwaps might not yet know its escrowHash // hence we try to get the claimHash and try to query based on that, FromBTCLNAutoSwaps // will use their claimHash as escrowHash before they know the real escrowHash const htlcCheckInitializeEvents: {[claimHash: string]: InitializeEvent<T["Data"]>} = {}; for(let event of events) { const swap = swapsByEscrowHash[chainEventToEscrowHash(event)]; if(swap!=null) { const obj = this.listeners[swap.getType()]; if(obj==null) continue; await obj.listener(event, swap); continue; } if(event instanceof InitializeEvent<T["Data"]>) { if(event.swapType===ChainSwapType.HTLC) { const swapData: T["Data"] = await event.swapData(); htlcCheckInitializeEvents[swapData.getClaimHash()] = event; } } } logger.debug("processEvents(): Additionally checking HTLC claim hashes: ", Object.keys(htlcCheckInitializeEvents)); if(Object.keys(htlcCheckInitializeEvents).length===0) return; //Try to query based on claimData const claimDataSwaps = await this.storage.query<ISwap<T>>( [ [{key: "escrowHash", value: Object.keys(htlcCheckInitializeEvents)}] ], (val: any) => { const obj = this.listeners[val.type]; if(obj==null) return null; return new obj.reviver(val); } ); const swapsByClaimDataHash: {[claimData: string]: ISwap} = {}; claimDataSwaps.forEach(swap => swapsByClaimDataHash[swap._getEscrowHash()] = swap); logger.debug("processEvents(): Additional HTLC swaps founds: ", swapsByClaimDataHash); for(let claimData in htlcCheckInitializeEvents) { const event = htlcCheckInitializeEvents[claimData]; const swap = swapsByClaimDataHash[claimData]; if(swap!=null) { const obj = this.listeners[swap.getType()]; if(obj==null) continue; await obj.listener(event, swap); } } } listener: EventListener<T["Data"]>; async start() { if(this.listener!=null) return; logger.info("start(): Starting unified swap event listener"); await this.storage.init(); logger.debug("start(): Storage initialized"); await this.events.init(); logger.debug("start(): Events initialized"); this.events.registerListener(this.listener = async (events) => { await this.processEvents(events); return true; }); logger.info("start(): Successfully initiated the unified swap event listener!"); } stop(): Promise<void> { logger.info("stop(): Stopping unified swap event listener"); this.events.unregisterListener(this.listener); return this.events.stop(); } registerListener<S extends ISwap<T>>(type: SwapType, listener: SwapEventListener<T, S>, reviver: new (val: any) => S): void { this.listeners[type] = { listener, reviver }; } unregisterListener(type: SwapType): boolean { if(this.listeners[type]) return false; delete this.listeners[type]; return true; } }