UNPKG

@xlink-network/xlink-sdk

Version:
190 lines (176 loc) 5.93 kB
import { getBTCPegInAddress } from "../bitcoinUtils/btcAddresses" import { ReselectSpendableUTXOsFn } from "../bitcoinUtils/prepareTransaction" import { createBridgeOrder_BitcoinToEVM, createBridgeOrder_BitcoinToStacks, } from "../stacksUtils/createBridgeOrder" import { numberToStacksContractNumber } from "../stacksUtils/xlinkContractHelpers" import { UnsupportedBridgeRouteError } from "../utils/errors" import { assertExclude, checkNever } from "../utils/typeHelpers" import { KnownChainId, KnownTokenId } from "../utils/types/knownIds" import { prepareBitcoinTransaction, supportedRoutes } from "./bridgeFromBitcoin" import { ChainId, SDKNumber, TokenId, toSDKNumberOrUndefined } from "./types" import { SDKGlobalContext } from "./types.internal" export interface EstimateBridgeTransactionFromBitcoinInput { fromChain: ChainId toChain: ChainId fromToken: TokenId toToken: TokenId fromAddress: string fromAddressScriptPubKey: Uint8Array toAddress: string amount: SDKNumber networkFeeRate: bigint reselectSpendableUTXOs: ReselectSpendableUTXOsFn } export interface EstimateBridgeTransactionFromBitcoinOutput { fee: SDKNumber estimatedVSize: SDKNumber } export async function estimateBridgeTransactionFromBitcoin( ctx: SDKGlobalContext, info: EstimateBridgeTransactionFromBitcoinInput, ): Promise<EstimateBridgeTransactionFromBitcoinOutput> { const route = await supportedRoutes.checkRouteValid(ctx, info) if (KnownChainId.isBitcoinChain(route.fromChain)) { if (KnownChainId.isStacksChain(route.toChain)) { if ( KnownTokenId.isBitcoinToken(route.fromToken) && KnownTokenId.isStacksToken(route.toToken) ) { return estimateFromBitcoin_toStacks(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken, }) } } else if (KnownChainId.isEVMChain(route.toChain)) { if ( KnownTokenId.isBitcoinToken(route.fromToken) && KnownTokenId.isEVMToken(route.toToken) ) { return estimateFromBitcoin_toEVM(ctx, { ...info, fromChain: route.fromChain, toChain: route.toChain, fromToken: route.fromToken, toToken: route.toToken, }) } } else if ( KnownChainId.isBRC20Chain(route.toChain) || KnownChainId.isRunesChain(route.toChain) ) { assertExclude(route.toChain, assertExclude.i<KnownChainId.BRC20Chain>()) assertExclude(route.toChain, assertExclude.i<KnownChainId.RunesChain>()) // TODO: bitcoin to brc20/runes is not supported yet } else { assertExclude(route.toChain, assertExclude.i<KnownChainId.BitcoinChain>()) checkNever(route) } } else { assertExclude(route.fromChain, assertExclude.i<KnownChainId.EVMChain>()) assertExclude(route.fromChain, assertExclude.i<KnownChainId.StacksChain>()) checkNever(route) } throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken, ) } async function estimateFromBitcoin_toStacks( sdkContext: Pick<SDKGlobalContext, "backendAPI">, info: Omit< EstimateBridgeTransactionFromBitcoinInput, "fromChain" | "toChain" | "fromToken" | "toToken" > & { fromChain: KnownChainId.BitcoinChain toChain: KnownChainId.StacksChain fromToken: KnownTokenId.BitcoinToken toToken: KnownTokenId.StacksToken }, ): Promise<EstimateBridgeTransactionFromBitcoinOutput> { const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain) if (pegInAddress == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC, ) } const createdOrder = await createBridgeOrder_BitcoinToStacks({ fromChain: info.fromChain, fromBitcoinScriptPubKey: info.fromAddressScriptPubKey, toChain: info.toChain, toToken: info.toToken, toStacksAddress: info.toAddress, swapSlippedAmount: numberToStacksContractNumber(info.amount), swapRoute: [], }) if (createdOrder == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC, ) } const resp = await prepareBitcoinTransaction(sdkContext, { ...info, orderData: createdOrder.data, pegInAddress, }) return { fee: toSDKNumberOrUndefined(resp.fee), estimatedVSize: toSDKNumberOrUndefined(resp.estimatedVSize), } } async function estimateFromBitcoin_toEVM( sdkContext: Pick<SDKGlobalContext, "backendAPI">, info: Omit< EstimateBridgeTransactionFromBitcoinInput, "fromChain" | "toChain" | "fromToken" | "toToken" > & { fromChain: KnownChainId.BitcoinChain toChain: KnownChainId.EVMChain fromToken: KnownTokenId.BitcoinToken toToken: KnownTokenId.EVMToken }, ): Promise<EstimateBridgeTransactionFromBitcoinOutput> { const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain) if (pegInAddress == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC, ) } const createdOrder = await createBridgeOrder_BitcoinToEVM({ fromChain: info.fromChain, toChain: info.toChain, toToken: info.toToken, fromBitcoinScriptPubKey: info.fromAddressScriptPubKey, toEVMAddress: info.toAddress, swapSlippedAmount: numberToStacksContractNumber(info.amount), swapRoute: [], }) if (createdOrder == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC, ) } const resp = await prepareBitcoinTransaction(sdkContext, { ...info, orderData: createdOrder.data, pegInAddress, }) return { fee: toSDKNumberOrUndefined(resp.fee), estimatedVSize: toSDKNumberOrUndefined(resp.estimatedVSize), } }