@atomiqlabs/sdk
Version:
atomiq labs SDK for cross-chain swaps between smart chains and bitcoin
454 lines (453 loc) • 16.3 kB
TypeScript
/// <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>;
}