@atomiqlabs/sdk
Version:
atomiq labs SDK for cross-chain swaps between smart chains and bitcoin
139 lines (129 loc) • 5.32 kB
text/typescript
import {IToBTCSwap, ToBTCSwapState} from "./IToBTCSwap";
import {ChainType, ClaimEvent, InitializeEvent, RefundEvent} from "@atomiqlabs/base";
import {ISwapWrapperOptions, SwapTypeDefinition} from "../../ISwapWrapper";
import {Intermediary, SingleChainReputationType} from "../../../intermediaries/Intermediary";
import {IntermediaryError} from "../../../errors/IntermediaryError";
import {IEscrowSwapWrapper} from "../IEscrowSwapWrapper";
import {AmountData} from "../../../types/AmountData";
import {mapArrayToObject} from "../../../utils/Utils";
export type IToBTCDefinition<T extends ChainType, W extends IToBTCWrapper<T, any>, S extends IToBTCSwap<T>> = SwapTypeDefinition<T, W, S>;
/**
* Base class for wrappers of escrow-based Smart chain -> Bitcoin (on-chain & lightning) swaps
*
* @category Swaps/Smart chain → Bitcoin
*/
export abstract class IToBTCWrapper<
T extends ChainType,
D extends IToBTCDefinition<T, IToBTCWrapper<T, D>, IToBTCSwap<T, D>>,
O extends ISwapWrapperOptions = ISwapWrapperOptions
> extends IEscrowSwapWrapper<T, D, O> {
/**
* @internal
*/
protected readonly tickSwapState = [ToBTCSwapState.CREATED, ToBTCSwapState.COMMITED, ToBTCSwapState.SOFT_CLAIMED];
/**
* @internal
*/
readonly _pendingSwapStates = [
ToBTCSwapState.CREATED,
ToBTCSwapState.QUOTE_SOFT_EXPIRED,
ToBTCSwapState.COMMITED,
ToBTCSwapState.SOFT_CLAIMED,
ToBTCSwapState.REFUNDABLE
];
/**
* @internal
*/
readonly _refundableSwapStates = [ToBTCSwapState.REFUNDABLE];
/**
* Pre-fetches intermediary's reputation, doesn't throw, instead aborts via abortController and returns null
*
* @param amountData
* @param lp Intermediary
* @param abortController
* @param contractVersion
* @returns Intermediary's reputation or null if failed
* @throws {IntermediaryError} If the intermediary vault doesn't exist
*
* @internal
*/
protected preFetchIntermediaryReputation(
amountData: Omit<AmountData, "amount">,
lp: Intermediary,
abortController: AbortController,
contractVersion: string
): Promise<SingleChainReputationType | undefined> {
return lp.getReputation(this.chainIdentifier, this._contract(contractVersion), [amountData.token.toString()], abortController.signal).then(res => {
if(res==null) throw new IntermediaryError("Invalid data returned - invalid LP vault");
return res;
}).catch(e => {
this.logger.warn("preFetchIntermediaryReputation(): Error: ", e);
abortController.abort(e);
return undefined;
});
}
/**
* Pre-fetches feeRate for a given swap
*
* @param signer Address of the swap initiator
* @param amountData
* @param claimHash optional hash of the swap or null
* @param abortController
* @param contractVersions
* @returns Fee rate
*
* @internal
*/
protected preFetchFeeRate(
signer: string,
amountData: Omit<AmountData, "amount">,
claimHash: {[contractVersion: string]: string} | undefined,
abortController: AbortController,
contractVersions: string[]
): {[contractVersion: string]: Promise<string | undefined>} {
return mapArrayToObject(contractVersions, (contractVersion) => {
return this._contract(contractVersion).getInitPayInFeeRate(signer, this._chain.randomAddress(), amountData.token, claimHash?.[contractVersion])
.catch(e => {
this.logger.warn("preFetchFeeRate(): Error: ", e);
abortController.abort(e);
return undefined;
});
});
}
/**
* @internal
*/
protected async processEventInitialize(swap: D["Swap"], event: InitializeEvent<T["Data"]>): Promise<boolean> {
if(swap._state===ToBTCSwapState.CREATED || swap._state===ToBTCSwapState.QUOTE_SOFT_EXPIRED) {
swap._state = ToBTCSwapState.COMMITED;
return true;
}
return false;
}
/**
* @internal
*/
protected async processEventClaim(swap: D["Swap"], event: ClaimEvent<T["Data"]>): Promise<boolean> {
if(swap._state!==ToBTCSwapState.REFUNDED && swap._state!==ToBTCSwapState.CLAIMED) {
await swap._setPaymentResult({
secret: event.result,
txId: Buffer.from(event.result, "hex").reverse().toString("hex")
}).catch(e => {
this.logger.warn(`processEventClaim(): Failed to set payment result ${event.result}: `, e);
});
swap._state = ToBTCSwapState.CLAIMED;
return true;
}
return false;
}
/**
* @internal
*/
protected processEventRefund(swap: D["Swap"], event: RefundEvent<T["Data"]>): Promise<boolean> {
if(swap._state!==ToBTCSwapState.CLAIMED && swap._state!==ToBTCSwapState.REFUNDED) {
swap._state = ToBTCSwapState.REFUNDED;
return Promise.resolve(true);
}
return Promise.resolve(false);
}
}