UNPKG

@atomiqlabs/sdk

Version:

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

454 lines (453 loc) 16.3 kB
/// <reference types="node" /> import { SwapType } from "../enums/SwapType"; import { EventEmitter } from "events"; import { ISwapWrapper, SwapTypeDefinition } from "./ISwapWrapper"; import { ChainType } from "@atomiqlabs/base"; import { SwapDirection } from "../enums/SwapDirection"; import { Fee } from "../types/fees/Fee"; import { FeeBreakdown } from "../types/fees/FeeBreakdown"; import { PercentagePPM } from "../types/fees/PercentagePPM"; import { TokenAmount } from "../types/TokenAmount"; import { Token } from "../types/Token"; import { SwapExecutionAction } from "../types/SwapExecutionAction"; import { LoggerType } from "../utils/Logger"; import { PriceInfoType } from "../types/PriceInfoType"; import { SwapStateInfo } from "../types/SwapStateInfo"; import { SwapExecutionStep } from "../types/SwapExecutionStep"; /** * Initialization data for creating a swap * * @category Swaps/Base */ export type ISwapInit = { pricingInfo: PriceInfoType; url?: string; expiry: number; swapFee: bigint; swapFeeBtc: bigint; exactIn: boolean; contractVersion: string; }; /** * Type guard to check if an object is an ISwapInit * * @category Swaps/Base */ export declare function isISwapInit(obj: any): obj is ISwapInit; /** * Base abstract class for all swap types * * @category Swaps/Base */ export declare abstract class ISwap<T extends ChainType = ChainType, D extends SwapTypeDefinition<T, ISwapWrapper<T, D>, ISwap<T, D, S>> = SwapTypeDefinition<T, ISwapWrapper<T, any>, ISwap<T, any, any>>, S extends number = number> { /** * Swap type */ protected readonly abstract TYPE: SwapType; /** * Description for the states * @internal */ protected readonly abstract swapStateDescription: Record<S, string>; /** * Name of the states * @internal */ protected readonly abstract swapStateName: (state: number) => string; /** * Swap logger * @internal */ protected readonly abstract logger: LoggerType; /** * Current newest defined version of the swap * @internal */ protected readonly currentVersion: number; /** * Wrapper instance holding this swap * @internal */ protected readonly wrapper: D["Wrapper"]; /** * The current version of the swap * @internal */ protected version: number; /** * Whether a swap was initialized, a swap is considered initialize on first interaction with it, i.e. * calling commit() on a Smart chain -> Bitcoin swaps, calling waitForPayment() or similar on the other * direction. Not initiated swaps are not saved to the persistent storage by default (see * {@link SwapperOptions.saveUninitializedSwaps}) * @internal */ protected initiated: boolean; /** * Expiration of the swap quote * @internal */ protected expiry: number; /** * Pricing information of the swap * @internal */ protected pricingInfo?: PriceInfoType; /** * Swap fee in the non-bitcoin token * @internal */ protected swapFee: bigint; /** * Swap fee in bitcoin satoshis * @internal */ protected swapFeeBtc: bigint; /** * Swap state * @internal */ _state: S; /** * Random nonce to differentiate the swap from others with the same identifier hash (i.e. when quoting the same swap * from multiple LPs) * @internal */ _randomNonce: string; /** * Whether the swap is saved in the persistent storage or not. * * @remarks This field itself is not persisted but is instead derived during runtime * * @internal */ _persisted: boolean; /** * @internal */ _contractVersion?: string; /** * Storage specific metadata that can be used for e.g. optimistic concurrency * * @internal */ _meta?: any; /** * Event emitter emitting `"swapState"` event when swap's state changes */ readonly events: EventEmitter<{ swapState: [D["Swap"]]; }>; /** * URL of the intermediary (LP) used for this swap, already has the swap service specific path appended */ readonly url?: string; /** * Smart chain identifier string corresponding to this swap */ readonly chainIdentifier: T["ChainId"]; /** * Whether a swap is an exact input swap */ readonly exactIn: boolean; /** * A UNIX milliseconds timestamps of when this swap was created */ createdAt: number; protected constructor(wrapper: D["Wrapper"], obj: any); protected constructor(wrapper: D["Wrapper"], swapInit: ISwapInit); /** * Called when swap is deserialized to potentially update the version of the data for the swap * * @internal */ protected abstract upgradeVersion(): void; /** * Waits till the swap reaches a specific state * * @param targetState The state to wait for * @param type Whether to wait for the state exactly or also to a state with a higher number * @param abortSignal Abort signal * @internal */ protected waitTillState(targetState: S, type?: "eq" | "gte" | "neq", abortSignal?: AbortSignal): Promise<void>; /** * Executes the swap with the provided wallet, the exact arguments for this functions differ for various swap * types. Check the `execute()` function signature in the respective swap class to see the required arguments. * * @param args Execution arguments, usually contains a source wallet object, callbacks and options, for exact * syntax check the respective swap class. * * @returns Whether a swap was successfully executed or not, if it wasn't the user can refund or claim manually */ abstract execute(...args: any[]): Promise<boolean>; /** * This attempts to populate missing fields in the pricing info based on the swap amounts * * @internal */ protected tryRecomputeSwapPrice(): void; /** * Returns the specific state along with the human-readable description of that state * * @internal */ protected _getStateInfo(state: S): SwapStateInfo<S>; /** * Re-fetches & revalidates the price data based on the current market prices */ refreshPriceData(): Promise<void>; /** * Checks if the pricing for the swap is valid, according to max allowed price difference set in the ISwapPrice */ hasValidPrice(): boolean; /** * Returns pricing info about the swap */ getPriceInfo(): { marketPrice?: number; swapPrice: number; difference: PercentagePPM; }; /** * Asserts a given signer is the initiator of this swap * * @param signer Signer to check with this swap's initiator * @throws {Error} When signer's address doesn't match with the swap's initiator one * @internal */ protected checkSigner(signer: T["Signer"] | string): void; /** * Await and prepares a list of passed transactions * * @param txsPromise * @internal */ protected prepareTransactions(txsPromise: Promise<T["TX"][]>): Promise<T["TX"][]>; /** * Returns an escrow hash of the swap * * @internal */ abstract _getEscrowHash(): string | null; /** * Checks if the swap's quote is expired for good (i.e. the swap strictly cannot be initiated anymore) * @internal */ abstract _verifyQuoteDefinitelyExpired(): Promise<boolean>; /** * Checks if the swap's quote is still valid * @internal */ abstract _verifyQuoteValid(): Promise<boolean>; /** * Returns the intiator address of the swap - address that created this swap * @internal */ abstract _getInitiator(): string; /** * Sets this swap as initiated * @internal */ _setInitiated(): void; /** * Returns source address of the swap */ abstract getInputAddress(): string | null; /** * Returns destination address of the swap */ abstract getOutputAddress(): string | null; /** * Returns swap input transaction ID on the source chain */ abstract getInputTxId(): string | null; /** * Returns swap output transaction ID on the destination chain */ abstract getOutputTxId(): string | null; /** * Returns the ID of the swap, as used in the storage */ abstract getId(): string; /** * Checks whether there is some action required from the user for this swap - can mean either refundable or claimable */ abstract requiresAction(): boolean; /** * Returns whether the swap is finished and in its terminal state (this can mean successful, refunded or failed) */ abstract isFinished(): boolean; /** * Checks whether the swap's quote has definitely expired and cannot be committed anymore, we can remove such swap */ abstract isQuoteExpired(): boolean; /** * Checks whether the swap's quote is soft expired (this means there is not enough time buffer for it to commit, * but it still can happen) */ abstract isQuoteSoftExpired(): boolean; /** * Returns whether the swap finished successful */ abstract isSuccessful(): boolean; /** * Returns whether the swap failed (e.g. was refunded) */ abstract isFailed(): boolean; /** * Returns whether the swap is currently being processed */ abstract isInProgress(): boolean; /** * Whether a swap was initialized, a swap is considered initialized on first interaction with it, i.e. * calling commit() on a Smart chain -> Bitcoin swaps, calling waitForPayment() or similar on the other * direction. Not initiated swaps are not saved to the persistent storage by default (see * {@link SwapperOptions.saveUninitializedSwaps}) */ isInitiated(): boolean; /** * Returns quote expiry in UNIX millis */ getQuoteExpiry(): number; /** * Returns the type of the swap */ getType(): SwapType; /** * Returns the direction of the swap */ getDirection(): SwapDirection; /** * Returns the current state of the swap */ getState(): S; /** * Returns the current state of the swap along with the human-readable description of the state */ getStateInfo(): SwapStateInfo<S>; /** * Returns a current state-dependent action for the user to execute, or `undefined` if there is no more action * required for this swap - this means that the swap is probably finished (either expired, failed or settled). * * @param options Optional options argument for the additional action context (i.e. passing bitcoin wallet info to * get funded PSBTs or passing the externally-generated swap secret), see the actual type in the respective swap * classes */ abstract getExecutionAction(options?: any): Promise<SwapExecutionAction | undefined>; /** * Returns a list of execution steps the user has to go through for a given swap, to see the possible execution * steps check out {@link SwapExecutionStep}. * * @param options Optional options argument for the additional steps context (i.e. automatic settlement timeout), * see the actual type in the respective swap classes */ abstract getExecutionSteps(options?: any): Promise<SwapExecutionStep[]>; /** * Returns the current action and the full execution steps for a given swap. Prefer this to calling * {@link getExecutionSteps} and {@link getExecutionAction} separately - if called sequentially they might * return the respective steps/actions in different states if you hit the state transition boundary. * * @param options Optional options argument for the additional execution status context, see the actual type in * the respective swap classes */ abstract getExecutionStatus(options?: { skipBuildingAction?: boolean; } & any): Promise<{ steps: SwapExecutionStep[]; currentAction: SwapExecutionAction | undefined; stateInfo: SwapStateInfo<S>; }>; /** * Submits signed transactions obtained from the execution action back to the swap. * * @remarks This endpoint will also wait till the submitted transactions are confirmed (on a smart-chain side) * and till the swap state change is observed from the authoritative chain/intermediary state. * * If invalid transactions are submitted, i.e. sending a simple noop or transfer transaction instead of the * expected tx, this call may wait indefinitely unless aborted via the AbortSignal. * * @param txs Signed transactions * @param abortSignal Abort signal * @param requiredStates Optional list of states that the swap has to be in for the transactions to be * submitted, else throws * @param idempotent Whether the tx submission should be handled idempotently, meaning if any of the supplied * transactions are already processed as e.g. init, claim, refund or execution transactions the function just * returns these transaction IDs without actually submitting them * * @internal */ abstract _submitExecutionTransactions(txs: (T["SignedTXType"] | string | any)[], abortSignal?: AbortSignal, requiredStates?: S[], idempotent?: boolean): Promise<string[]>; /** * Returns output amount of the swap, user receives this much */ abstract getOutput(): TokenAmount; /** * Returns the output token of the swap */ abstract getOutputToken(): Token<T["ChainId"]>; /** * Returns input amount of the swap, user needs to pay this much */ abstract getInput(): TokenAmount; /** * Returns the input token of the swap */ abstract getInputToken(): Token<T["ChainId"]>; /** * Returns input amount of the swap without the fees (swap fee, network fee) */ abstract getInputWithoutFee(): TokenAmount; /** * Returns total fee for the swap, the fee is represented in source currency & destination currency, but is * paid only once */ abstract getFee(): Fee; /** * Returns the breakdown of all the fees paid */ abstract getFeeBreakdown(): FeeBreakdown<T["ChainId"]>; /** * Saves the swap data to the underlying storage, or removes it if it is in a quote expired state * * @internal */ _save(): Promise<void>; /** * Saves the swap data and also emits a swap state change * * @param state Optional state to set before the swap is saved an event emitted * * @internal */ _saveAndEmit(state?: S): Promise<void>; /** * Serializes the swap to a JSON stringifiable representation (i.e. no bigints, buffers etc.) */ serialize(): any; /** * Emits a `swapState` event with the current swap * * @internal */ _emitEvent(): void; /** * Synchronizes swap state from chain and/or LP node, usually ran on startup * * @param save whether to save the new swap state or not * * @returns {boolean} true if the swap changed, false if the swap hasn't changed * * @internal */ abstract _sync(save?: boolean): Promise<boolean>; /** * Runs quick checks on the swap, such as checking the expiry, usually ran periodically every few seconds * * @param save whether to save the new swap state or not * * @returns {boolean} true if the swap changed, false if the swap hasn't changed * * @internal */ abstract _tick(save?: boolean): Promise<boolean>; }