UNPKG

@lifi/sdk

Version:

LI.FI Any-to-Any Cross-Chain-Swap SDK

253 lines (225 loc) 7.17 kB
import type { ChainId, LiFiStep } from '@lifi/types' import { executionState } from './executionState.js' import { getProcessMessage } from './processMessages.js' import type { Execution, ExecutionStatus, LiFiStepExtended, Process, ProcessStatus, ProcessType, } from './types.js' export type FindOrCreateProcessProps = { step: LiFiStepExtended type: ProcessType chainId?: ChainId status?: ProcessStatus startedAt?: number } /** * Manages status updates of a route and provides various functions for tracking processes * @param {string} routeId The route dd this StatusManger belongs to. * @returns {StatusManager} An instance of StatusManager. */ export class StatusManager { private readonly routeId: string private shouldUpdate = true constructor(routeId: string) { this.routeId = routeId } /** * Initializes the execution object of a Step. * @param step The current step in execution * @returns The initialized execution object for this step and a function to update this step */ initExecutionObject = (step: LiFiStepExtended): Execution => { if (!step.execution) { step.execution = { status: 'PENDING', process: [], startedAt: Date.now(), } this.updateStepInRoute(step) } // Change status to PENDING after resuming from FAILED if (step.execution.status === 'FAILED') { step.execution.startedAt = Date.now() step.execution.status = 'PENDING' this.updateStepInRoute(step) } return step.execution } /** * Updates the execution object of a Step. * @param step The current step in execution * @param status The status for the execution * @param execution Optional. Information about received tokens * @returns The step with the updated execution object */ updateExecution( step: LiFiStepExtended, status: ExecutionStatus, execution?: Partial<Execution> ): LiFiStep { if (!step.execution) { throw Error("Can't update empty execution.") } step.execution.status = status if (status === 'DONE') { step.execution.doneAt = Date.now() } if (execution) { step.execution = { ...step.execution, ...execution, } } this.updateStepInRoute(step) return step } /** * Finds a process of the specified type in the step's execution * @param step The step to search in * @param type The process type to find * @param status Optional status to update the process with if found * @returns The found process or undefined if not found */ findProcess( step: LiFiStepExtended, type: ProcessType, status?: ProcessStatus ): Process | undefined { if (!step.execution?.process) { throw new Error("Execution hasn't been initialized.") } const process = step.execution.process.find((p) => p.type === type) if (process && status && process.status !== status) { process.status = status this.updateStepInRoute(step) } return process } /** * Create and push a new process into the execution. * @param step The step that should contain the new process. * @param type Type of the process. Used to identify already existing processes. * @param chainId Chain Id of the process. * @param status By default created process is set to the STARTED status. We can override new process with the needed status. * @returns Returns process. */ findOrCreateProcess = ({ step, type, chainId, status, startedAt, }: FindOrCreateProcessProps): Process => { const process = this.findProcess(step, type, status) if (process) { return process } const newProcess: Process = { type: type, startedAt: startedAt ?? Date.now(), message: getProcessMessage(type, status ?? 'STARTED'), status: status ?? 'STARTED', chainId: chainId, } step.execution!.process.push(newProcess) this.updateStepInRoute(step) return newProcess } /** * Update a process object. * @param step The step where the process should be updated * @param type The process type to update * @param status The status the process gets. * @param [params] Additional parameters to append to the process. * @returns The update process */ updateProcess = ( step: LiFiStepExtended, type: ProcessType, status: ProcessStatus, params?: Partial<Process> ): Process => { if (!step.execution) { throw new Error("Can't update an empty step execution.") } const currentProcess = this.findProcess(step, type) if (!currentProcess) { throw new Error("Can't find a process for the given type.") } switch (status) { case 'CANCELLED': currentProcess.doneAt = Date.now() break case 'FAILED': currentProcess.doneAt = Date.now() step.execution.status = 'FAILED' break case 'DONE': currentProcess.doneAt = Date.now() break case 'PENDING': step.execution.status = 'PENDING' currentProcess.pendingAt = Date.now() break case 'ACTION_REQUIRED': step.execution.status = 'ACTION_REQUIRED' currentProcess.actionRequiredAt = Date.now() break default: break } currentProcess.status = status currentProcess.message = getProcessMessage(type, status) // set extra parameters or overwritte the standard params set in the switch statement if (params) { for (const [key, value] of Object.entries(params)) { currentProcess[key] = value } } // Sort processes, the ones with DONE status go first step.execution.process = [ ...step.execution.process.filter((process) => process.status === 'DONE'), ...step.execution.process.filter((process) => process.status !== 'DONE'), ] this.updateStepInRoute(step) // updates the step in the route return currentProcess } /** * Remove a process from the execution * @param step The step where the process should be removed from * @param type The process type to remove */ removeProcess = (step: LiFiStepExtended, type: ProcessType): void => { if (!step.execution) { throw new Error("Execution hasn't been initialized.") } const index = step.execution.process.findIndex((p) => p.type === type) step.execution.process.splice(index, 1) this.updateStepInRoute(step) } updateStepInRoute = (step: LiFiStep): LiFiStep => { if (!this.shouldUpdate) { return step } const data = executionState.get(this.routeId) if (!data) { throw new Error('Execution data not found.') } const stepIndex = data.route.steps.findIndex( (routeStep) => routeStep.id === step.id ) if (stepIndex === -1) { throw new Error("Couldn't find a step to update.") } data.route.steps[stepIndex] = { ...data.route.steps[stepIndex], ...step } data.executionOptions?.updateRouteHook?.(data.route) return data.route.steps[stepIndex] } allowUpdates(value: boolean): void { this.shouldUpdate = value } }