UNPKG

@xlink-network/xlink-sdk

Version:
342 lines (340 loc) 10.1 kB
import { broadcastRevealableTransaction } from "./chunk-5RDPVP7P.mjs"; import { createBitcoinPegInRecipients } from "./chunk-PDADMSDC.mjs"; import { createRevealTx } from "./chunk-LAKQU47C.mjs"; import { createBridgeOrder_BitcoinToEVM, createBridgeOrder_BitcoinToStacks } from "./chunk-VBBYJHRM.mjs"; import { validateBridgeOrder } from "./chunk-54FCXCUT.mjs"; import { isSupportedBitcoinRoute } from "./chunk-DAZBH3JI.mjs"; import { getBTCPegInAddress } from "./chunk-LZ7NVYPA.mjs"; import { prepareTransaction } from "./chunk-STX72JIW.mjs"; import { createTransaction } from "./chunk-ATRBHFDV.mjs"; import { buildSupportedRoutes, defineRoute } from "./chunk-BSXD4QA3.mjs"; import { getStacksTokenContractInfo, numberToStacksContractNumber } from "./chunk-JJNS722O.mjs"; import { decodeHex } from "./chunk-UZKY7HXV.mjs"; import { BridgeValidateFailedError, UnsupportedBridgeRouteError } from "./chunk-KTUPNUM5.mjs"; import { KnownChainId, KnownTokenId, _allKnownEVMMainnetChains, _allKnownEVMTestnetChains } from "./chunk-GZDQGY7B.mjs"; import { assertExclude, checkNever } from "./chunk-E5Y3FGR6.mjs"; import { bitcoinToSatoshi } from "./chunk-VA3HOX5B.mjs"; import { BigNumber } from "./chunk-KHTJNOQE.mjs"; import { range } from "./chunk-PRLWYSC3.mjs"; // src/xlinkSdkUtils/bridgeFromBitcoin.ts import * as btc from "@scure/btc-signer"; var supportedRoutes = buildSupportedRoutes( [ // from mainnet // to Stacks ...defineRoute( [[KnownChainId.Bitcoin.Mainnet], [KnownChainId.Stacks.Mainnet]], [[KnownTokenId.Bitcoin.BTC, KnownTokenId.Stacks.aBTC]] ), // to EVM ...defineRoute( [[KnownChainId.Bitcoin.Mainnet], [..._allKnownEVMMainnetChains]], [ [KnownTokenId.Bitcoin.BTC, KnownTokenId.EVM.WBTC], [KnownTokenId.Bitcoin.BTC, KnownTokenId.EVM.BTCB], [KnownTokenId.Bitcoin.BTC, KnownTokenId.EVM.aBTC] ] ), // from testnet // to Stacks ...defineRoute( [[KnownChainId.Bitcoin.Testnet], [KnownChainId.Stacks.Testnet]], [[KnownTokenId.Bitcoin.BTC, KnownTokenId.Stacks.aBTC]] ), // to EVM ...defineRoute( [[KnownChainId.Bitcoin.Testnet], [..._allKnownEVMTestnetChains]], [ [KnownTokenId.Bitcoin.BTC, KnownTokenId.EVM.WBTC], [KnownTokenId.Bitcoin.BTC, KnownTokenId.EVM.BTCB], [KnownTokenId.Bitcoin.BTC, KnownTokenId.EVM.aBTC] ] ) ], { isSupported: isSupportedBitcoinRoute } ); async function bridgeFromBitcoin(ctx, info) { 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 bridgeFromBitcoin_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 bridgeFromBitcoin_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()); assertExclude(route.toChain, assertExclude.i()); } else { assertExclude(route.toChain, assertExclude.i()); checkNever(route); } } else { assertExclude(route.fromChain, assertExclude.i()); assertExclude(route.fromChain, assertExclude.i()); checkNever(route); } throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, info.fromToken, info.toToken ); } async function bridgeFromBitcoin_toStacks(sdkContext, info) { const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain); const toTokenContractInfo = getStacksTokenContractInfo( info.toChain, info.toToken ); if (pegInAddress == null || toTokenContractInfo == 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 ); } return broadcastBitcoinTransaction(sdkContext, info, createdOrder); } async function bridgeFromBitcoin_toEVM(sdkContext, info) { 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 ); } return broadcastBitcoinTransaction(sdkContext, info, createdOrder); } async function broadcastBitcoinTransaction(sdkContext, info, createdOrder) { const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain); if (pegInAddress == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC ); } const tx = await constructBitcoinTransaction(sdkContext, { ...info, validateBridgeOrder: (btcTx, revealTx, swapRoute) => { if (revealTx == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC ); } return validateBridgeOrder({ chainId: info.fromChain, commitTx: btcTx, revealTx, terminatingStacksToken: createdOrder.terminatingStacksToken, swapRoute }); }, orderData: createdOrder.data, pegInAddress }); if (tx.revealOutput == null) { throw new UnsupportedBridgeRouteError( info.fromChain, info.toChain, KnownTokenId.Bitcoin.BTC ); } const { txid: apiBroadcastedTxId } = await broadcastRevealableTransaction( sdkContext, { fromChain: info.fromChain, transactionHex: `0x${tx.hex}`, orderData: createdOrder.data, orderOutputIndex: tx.revealOutput.index, orderOutputSatsAmount: tx.revealOutput.satsAmount, xlinkPegInAddress: pegInAddress } ); const { txid: delegateBroadcastedTxId } = await info.sendTransaction({ hex: tx.hex }); if (apiBroadcastedTxId !== delegateBroadcastedTxId) { console.warn( "[xlink-sdk] Transaction id broadcasted by API and delegatee are different:", `API: ${apiBroadcastedTxId}, `, `Delegatee: ${delegateBroadcastedTxId}` ); } return { txid: delegateBroadcastedTxId }; } async function constructBitcoinTransaction(sdkContext, info) { const txOptions = await prepareBitcoinTransaction(sdkContext, info); const tx = createTransaction( txOptions.inputs, txOptions.recipients.concat({ addressScriptPubKey: info.fromAddressScriptPubKey, satsAmount: txOptions.changeAmount }), txOptions.revealOutput ? [] : [info.orderData] ); const { psbt } = await info.signPsbt({ psbt: tx.toPSBT(), signInputs: range(0, tx.inputsLength) }); const signedTx = btc.Transaction.fromPSBT(psbt, { allowUnknownInputs: true, allowUnknownOutputs: true }); if (!signedTx.isFinal) { signedTx.finalize(); } let revealTx; if (txOptions.revealOutput != null) { const created = await createRevealTx(sdkContext, { fromChain: info.fromChain, txId: signedTx.id, vout: txOptions.revealOutput.index, satsAmount: txOptions.revealOutput.satsAmount, orderData: info.orderData, xlinkPegInAddress: info.pegInAddress }); revealTx = decodeHex(created.txHex); } await info.validateBridgeOrder(signedTx.extract(), revealTx, []).catch((err) => { throw new BridgeValidateFailedError(err); }); return { hex: signedTx.hex, revealOutput: txOptions.revealOutput }; } async function prepareBitcoinTransaction(sdkContext, info) { const bitcoinNetwork = info.fromChain === KnownChainId.Bitcoin.Mainnet ? btc.NETWORK : btc.TEST_NETWORK; const recipient = await createBitcoinPegInRecipients(sdkContext, { fromChain: info.fromChain, toChain: info.toChain, fromToken: info.fromToken, toToken: info.toToken, fromAddress: { address: info.fromAddress, scriptPubKey: info.fromAddressScriptPubKey }, toAddress: info.toAddress, fromAmount: BigNumber.from(info.amount), orderData: info.orderData, feeRate: info.networkFeeRate }); const result = await prepareTransaction({ recipients: [ { addressScriptPubKey: recipient.scriptPubKey, satsAmount: recipient.satsAmount }, { addressScriptPubKey: info.pegInAddress.scriptPubKey, satsAmount: bitcoinToSatoshi(info.amount) } ], changeAddressScriptPubKey: info.fromAddressScriptPubKey, feeRate: info.networkFeeRate, reselectSpendableUTXOs: info.reselectSpendableUTXOs }); return { ...result, bitcoinNetwork, revealOutput: { index: 0, satsAmount: recipient.satsAmount } }; } export { supportedRoutes, bridgeFromBitcoin, prepareBitcoinTransaction }; //# sourceMappingURL=chunk-66DYX3RO.mjs.map