@xlink-network/xlink-sdk
Version:
342 lines (340 loc) • 10.1 kB
JavaScript
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