feat: add option extraOutputs to bitcoin/brc20/runes routes

This commit is contained in:
c4605
2025-04-27 11:35:28 +02:00
parent c461224504
commit d56c39db8e
7 changed files with 198 additions and 16 deletions

View File

@@ -109,6 +109,11 @@ export interface BridgeFromBRC20Input {
networkFeeChangeAddress: string
networkFeeChangeAddressScriptPubKey: Uint8Array
extraOutputs?: {
address: BitcoinAddress
satsAmount: bigint
}[]
signPsbt: BridgeFromBRC20Input_signPsbtFn
sendTransaction: (tx: {
hex: string
@@ -124,6 +129,10 @@ export interface BridgeFromBRC20Input {
export interface BridgeFromBRC20Output {
txid: string
extraOutputs: {
index: number
satsAmount: bigint
}[]
}
export async function bridgeFromBRC20(
@@ -395,6 +404,7 @@ async function bridgeFromBRC20_toStacks(
withHardLinkageOutput: false,
bridgeFeeOutput,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
@@ -444,6 +454,7 @@ async function bridgeFromBRC20_toEVM(
withHardLinkageOutput: false,
bridgeFeeOutput,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
@@ -506,6 +517,7 @@ async function bridgeFromBRC20_toBitcoin(
withHardLinkageOutput: true,
bridgeFeeOutput,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
@@ -568,6 +580,7 @@ async function bridgeFromBRC20_toMeta(
withHardLinkageOutput: true,
bridgeFeeOutput,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
@@ -583,7 +596,13 @@ async function broadcastBRC20Transaction(
sendTransaction: BridgeFromBRC20Input["sendTransaction"]
},
createdOrder: CreateBridgeOrderResult,
): Promise<{ txid: string }> {
): Promise<{
txid: string
extraOutputs: {
index: number
satsAmount: bigint
}[]
}> {
const pegInAddress = getMetaPegInAddress(info.fromChain, info.toChain)
if (pegInAddress == null) {
throw new UnsupportedBridgeRouteError(
@@ -665,7 +684,10 @@ async function broadcastBRC20Transaction(
)
}
return { txid: delegateBroadcastedTxId }
return {
txid: delegateBroadcastedTxId,
extraOutputs: tx.extraOutputs,
}
}
type ConstructBRC20TransactionInput = PrepareBRC20TransactionInput & {
@@ -694,6 +716,10 @@ async function constructBRC20Transaction(
index: number
satsAmount: bigint
}
extraOutputs: {
index: number
satsAmount: bigint
}[]
}> {
const txOptions = await prepareBRC20Transaction(sdkContext, info)
@@ -749,6 +775,7 @@ async function constructBRC20Transaction(
return {
hex: signedTx.hex,
revealOutput: txOptions.revealOutput,
extraOutputs: txOptions.extraOutputs,
}
}
@@ -771,6 +798,10 @@ export type PrepareBRC20TransactionInput = KnownRoute_FromBRC20 & {
satsAmount: BigNumber
}
hardLinkageOutput: null | BitcoinAddress
extraOutputs: {
address: BitcoinAddress
satsAmount: bigint
}[]
}
/**
* Bitcoin Tx Structure:
@@ -807,6 +838,10 @@ export async function prepareBRC20Transaction(
index: number
satsAmount: bigint
}
extraOutputs: {
index: number
satsAmount: bigint
}[]
}
> {
const bitcoinNetwork =
@@ -858,6 +893,10 @@ export async function prepareBRC20Transaction(
satsAmount: BITCOIN_OUTPUT_MINIMUM_AMOUNT,
},
]),
...info.extraOutputs.map(o => ({
addressScriptPubKey: o.address.scriptPubKey,
satsAmount: o.satsAmount,
})),
],
changeAddressScriptPubKey: info.networkFeeChangeAddressScriptPubKey,
feeRate: info.networkFeeRate,
@@ -872,6 +911,7 @@ export async function prepareBRC20Transaction(
pegInOrderDataCausedOffset + (info.bridgeFeeOutput == null ? 0 : 1)
const hardLinkageCausedOffset =
bridgeFeeCausedOffset + (info.hardLinkageOutput == null ? 0 : 1)
const extraOutputsStartOffset = hardLinkageCausedOffset
return {
...result,
@@ -899,5 +939,9 @@ export async function prepareBRC20Transaction(
index: hardLinkageCausedOffset - 1,
satsAmount: BITCOIN_OUTPUT_MINIMUM_AMOUNT,
},
extraOutputs: info.extraOutputs.map((o, i) => ({
index: extraOutputsStartOffset + i,
satsAmount: o.satsAmount,
})),
}
}

View File

@@ -89,9 +89,16 @@ export interface BridgeFromBitcoinInput {
toAddressScriptPubKey?: Uint8Array
amount: SDKNumber
networkFeeRate: bigint
swapRoute?: SwapRoute_WithMinimumAmountsToReceive_Public
networkFeeRate: bigint
reselectSpendableUTXOs: BridgeFromBitcoinInput_reselectSpendableUTXOs
extraOutputs?: {
address: BitcoinAddress
satsAmount: bigint
}[]
signPsbt: BridgeFromBitcoinInput_signPsbtFn
sendTransaction: (tx: {
hex: string
@@ -107,6 +114,10 @@ export interface BridgeFromBitcoinInput {
export interface BridgeFromBitcoinOutput {
txid: string
extraOutputs: {
index: number
satsAmount: bigint
}[]
}
export async function bridgeFromBitcoin(
@@ -289,7 +300,12 @@ async function bridgeFromBitcoin_toStacks(
return broadcastBitcoinTransaction(
sdkContext,
{ ...info, withHardLinkageOutput: false, swapRoute: info.swapRoute },
{
...info,
withHardLinkageOutput: false,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
}
@@ -331,7 +347,12 @@ async function bridgeFromBitcoin_toEVM(
return broadcastBitcoinTransaction(
sdkContext,
{ ...info, withHardLinkageOutput: false, swapRoute: info.swapRoute },
{
...info,
withHardLinkageOutput: false,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
}
@@ -386,7 +407,12 @@ async function bridgeFromBitcoin_toMeta(
return broadcastBitcoinTransaction(
sdkContext,
{ ...info, withHardLinkageOutput: true, swapRoute: info.swapRoute },
{
...info,
withHardLinkageOutput: true,
swapRoute: info.swapRoute,
extraOutputs: info.extraOutputs ?? [],
},
createdOrder,
)
}
@@ -401,7 +427,13 @@ async function broadcastBitcoinTransaction(
sendTransaction: BridgeFromBitcoinInput["sendTransaction"]
},
createdOrder: CreateBridgeOrderResult,
): Promise<{ txid: string }> {
): Promise<{
txid: string
extraOutputs: {
index: number
satsAmount: bigint
}[]
}> {
const pegInAddress = getBTCPegInAddress(info.fromChain, info.toChain)
if (pegInAddress == null) {
throw new UnsupportedBridgeRouteError(
@@ -486,7 +518,10 @@ async function broadcastBitcoinTransaction(
)
}
return { txid: delegateBroadcastedTxId }
return {
txid: delegateBroadcastedTxId,
extraOutputs: tx.extraOutputs,
}
}
type ConstructBitcoinTransactionInput = PrepareBitcoinTransactionInput & {
@@ -495,7 +530,6 @@ type ConstructBitcoinTransactionInput = PrepareBitcoinTransactionInput & {
| SwapRouteViaALEX_WithMinimumAmountsToReceive_Public
| SwapRouteViaEVMDexAggregator_WithMinimumAmountsToReceive_Public
signPsbt: BridgeFromBitcoinInput["signPsbt"]
pegInAddress: BitcoinAddress
validateBridgeOrder: (
pegInTx: Uint8Array,
revealTx: undefined | Uint8Array,
@@ -511,6 +545,10 @@ async function constructBitcoinTransaction(
index: number
satsAmount: bigint
}
extraOutputs: {
index: number
satsAmount: bigint
}[]
}> {
const txOptions = await prepareBitcoinTransaction(sdkContext, info)
@@ -565,6 +603,7 @@ async function constructBitcoinTransaction(
return {
hex: signedTx.hex,
revealOutput: txOptions.revealOutput,
extraOutputs: txOptions.extraOutputs,
}
}
@@ -578,6 +617,10 @@ export type PrepareBitcoinTransactionInput = KnownRoute_FromBitcoin & {
orderData: Uint8Array
hardLinkageOutput: null | BitcoinAddress
pegInAddress: BitcoinAddress
extraOutputs: {
address: BitcoinAddress
satsAmount: bigint
}[]
}
/**
* Bitcoin Tx Structure:
@@ -603,6 +646,10 @@ export async function prepareBitcoinTransaction(
index: number
satsAmount: bigint
}
extraOutputs: {
index: number
satsAmount: bigint
}[]
}
> {
const bitcoinNetwork =
@@ -642,6 +689,10 @@ export async function prepareBitcoinTransaction(
satsAmount: BITCOIN_OUTPUT_MINIMUM_AMOUNT,
},
]),
...info.extraOutputs.map(o => ({
addressScriptPubKey: o.address.scriptPubKey,
satsAmount: o.satsAmount,
})),
],
changeAddressScriptPubKey: info.fromAddressScriptPubKey,
feeRate: info.networkFeeRate,
@@ -650,20 +701,30 @@ export async function prepareBitcoinTransaction(
),
})
const pegInOrderDataCausedOffset = 1
const pegInBitcoinTokenCausedOffset = pegInOrderDataCausedOffset + 1
const hardLinkageCausedOffset =
pegInBitcoinTokenCausedOffset + (info.hardLinkageOutput == null ? 0 : 1)
const extraOutputsStartOffset = hardLinkageCausedOffset
return {
...result,
bitcoinNetwork,
revealOutput: {
index: 0,
index: pegInOrderDataCausedOffset - 1,
satsAmount: recipient.satsAmount,
},
hardLinkageOutput:
info.hardLinkageOutput == null
hardLinkageCausedOffset == null
? undefined
: {
index: 2,
index: hardLinkageCausedOffset - 1,
satsAmount: BITCOIN_OUTPUT_MINIMUM_AMOUNT,
},
extraOutputs: info.extraOutputs.map((o, i) => ({
index: extraOutputsStartOffset + i,
satsAmount: o.satsAmount,
})),
}
}

View File

@@ -120,6 +120,11 @@ export interface BridgeFromRunesInput {
networkFeeChangeAddressScriptPubKey: Uint8Array
reselectSpendableNetworkFeeUTXOs: BridgeFromRunesInput_reselectSpendableNetworkFeeUTXOs
extraOutputs?: {
address: BitcoinAddress
satsAmount: bigint
}[]
signPsbt: BridgeFromRunesInput_signPsbtFn
sendTransaction: (tx: {
hex: string
@@ -135,6 +140,10 @@ export interface BridgeFromRunesInput {
export interface BridgeFromRunesOutput {
txid: string
extraOutputs: {
index: number
satsAmount: bigint
}[]
}
export async function bridgeFromRunes(
@@ -336,6 +345,7 @@ async function bridgeFromRunes_toStacks(
...info,
withHardLinkageOutput: false,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
swapRoute: info.swapRoute,
},
createdOrder,
@@ -385,6 +395,7 @@ async function bridgeFromRunes_toEVM(
...info,
withHardLinkageOutput: false,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
swapRoute: info.swapRoute,
},
createdOrder,
@@ -447,6 +458,7 @@ async function bridgeFromRunes_toBitcoin(
...info,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
swapRoute: info.swapRoute,
},
createdOrder,
@@ -509,6 +521,7 @@ async function bridgeFromRunes_toMeta(
...info,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
swapRoute: info.swapRoute,
},
createdOrder,
@@ -525,7 +538,13 @@ async function broadcastRunesTransaction(
sendTransaction: BridgeFromRunesInput["sendTransaction"]
},
createdOrder: CreateBridgeOrderResult,
): Promise<{ txid: string }> {
): Promise<{
txid: string
extraOutputs: {
index: number
satsAmount: bigint
}[]
}> {
const pegInAddress = getMetaPegInAddress(info.fromChain, info.toChain)
if (pegInAddress == null) {
throw new UnsupportedBridgeRouteError(
@@ -607,7 +626,10 @@ async function broadcastRunesTransaction(
)
}
return { txid: delegateBroadcastedTxId }
return {
txid: delegateBroadcastedTxId,
extraOutputs: tx.extraOutputs,
}
}
type ConstructRunesTransactionInput = PrepareRunesTransactionInput & {
@@ -636,6 +658,10 @@ async function constructRunesTransaction(
index: number
satsAmount: bigint
}
extraOutputs: {
index: number
satsAmount: bigint
}[]
}> {
const txOptions = await prepareRunesTransaction(
sdkContext,
@@ -695,6 +721,7 @@ async function constructRunesTransaction(
return {
hex: signedTx.hex,
revealOutput: txOptions.revealOutput,
extraOutputs: txOptions.extraOutputs,
}
}
@@ -718,6 +745,10 @@ export type PrepareRunesTransactionInput = KnownRoute_FromRunes & {
satsAmount: BigNumber
}
hardLinkageOutput: null | BitcoinAddress
extraOutputs: {
address: BitcoinAddress
satsAmount: bigint
}[]
}
/**
* Bitcoin Tx Structure:
@@ -757,6 +788,10 @@ export async function prepareRunesTransaction(
index: number
satsAmount: bigint
}
extraOutputs: {
index: number
satsAmount: bigint
}[]
}
> {
const bitcoinNetwork =
@@ -845,6 +880,7 @@ export async function prepareRunesTransaction(
const hardLinkageCausedOffset =
bridgeFeeCausedOffset + (info.hardLinkageOutput == null ? 0 : 1)
const pegInRuneTokensCausedOffset = hardLinkageCausedOffset + 1
const extraOutputsStartOffset = pegInRuneTokensCausedOffset
const runesOpReturnScript = toBitcoinOpReturnScript({
edicts: [
@@ -942,6 +978,10 @@ export async function prepareRunesTransaction(
index: hardLinkageCausedOffset - 1,
satsAmount: BITCOIN_OUTPUT_MINIMUM_AMOUNT,
},
extraOutputs: info.extraOutputs.map((o, i) => ({
index: extraOutputsStartOffset + i,
satsAmount: o.satsAmount,
})),
}
}

View File

@@ -1,5 +1,8 @@
import { UTXOSpendable } from "../bitcoinHelpers"
import { getBitcoinHardLinkageAddress } from "../bitcoinUtils/btcAddresses"
import {
BitcoinAddress,
getBitcoinHardLinkageAddress,
} from "../bitcoinUtils/btcAddresses"
import { SDK_NAME } from "../bitcoinUtils/constants"
import { getMetaPegInAddress } from "../metaUtils/btcAddresses"
import { isSupportedBRC20Route } from "../metaUtils/peggingHelpers"
@@ -65,6 +68,11 @@ export interface EstimateBridgeTransactionFromBRC20Input {
networkFeeChangeAddress: string
networkFeeChangeAddressScriptPubKey: Uint8Array
reselectSpendableNetworkFeeUTXOs: BridgeFromBRC20Input_reselectSpendableNetworkFeeUTXOs
extraOutputs?: {
address: BitcoinAddress
satsAmount: bigint
}[]
}
export interface EstimateBridgeTransactionFromBRC20Output {
@@ -203,6 +211,7 @@ async function estimateFromBRC20_toStacks(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -246,6 +255,7 @@ async function estimateFromBRC20_toEVM(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -303,6 +313,7 @@ async function estimateFromBRC20_toBitcoin(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -360,6 +371,7 @@ async function estimateFromBRC20_toMeta(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}

View File

@@ -1,4 +1,5 @@
import {
BitcoinAddress,
getBitcoinHardLinkageAddress,
getBTCPegInAddress,
} from "../bitcoinUtils/btcAddresses"
@@ -65,6 +66,11 @@ export interface EstimateBridgeTransactionFromBitcoinInput {
| SwapRouteViaEVMDexAggregator_WithMinimumAmountsToReceive_Public
networkFeeRate: bigint
reselectSpendableUTXOs: BridgeFromBitcoinInput_reselectSpendableUTXOs
extraOutputs?: {
address: BitcoinAddress
satsAmount: bigint
}[]
}
export interface EstimateBridgeTransactionFromBitcoinOutput {
@@ -187,6 +193,7 @@ async function estimateFromBitcoin_toStacks(
...info,
orderData: createdOrder.data,
withHardLinkageOutput: true,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -227,6 +234,7 @@ async function estimateFromBitcoin_toEVM(
...info,
orderData: createdOrder.data,
withHardLinkageOutput: true,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -281,6 +289,7 @@ async function estimateFromBitcoin_toMeta(
...info,
orderData: createdOrder.data,
withHardLinkageOutput: true,
extraOutputs: info.extraOutputs ?? [],
})
}

View File

@@ -1,4 +1,7 @@
import { getBitcoinHardLinkageAddress } from "../bitcoinUtils/btcAddresses"
import {
BitcoinAddress,
getBitcoinHardLinkageAddress,
} from "../bitcoinUtils/btcAddresses"
import { SDK_NAME } from "../bitcoinUtils/constants"
import { getMetaPegInAddress } from "../metaUtils/btcAddresses"
import { isSupportedRunesRoute } from "../metaUtils/peggingHelpers"
@@ -66,6 +69,11 @@ export interface EstimateBridgeTransactionFromRunesInput {
networkFeeChangeAddress: string
networkFeeChangeAddressScriptPubKey: Uint8Array
reselectSpendableNetworkFeeUTXOs: BridgeFromRunesInput_reselectSpendableNetworkFeeUTXOs
extraOutputs?: {
address: BitcoinAddress
satsAmount: bigint
}[]
}
export interface EstimateBridgeTransactionFromRunesOutput {
@@ -204,6 +212,7 @@ async function estimateFromRunes_toStacks(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -247,6 +256,7 @@ async function estimateFromRunes_toEVM(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -304,6 +314,7 @@ async function estimateFromRunes_toBitcoin(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}
@@ -361,6 +372,7 @@ async function estimateFromRunes_toMeta(
orderData: createdOrder.data,
withHardLinkageOutput: true,
bridgeFeeOutput,
extraOutputs: info.extraOutputs ?? [],
})
}

View File

@@ -4,3 +4,7 @@ export function entries<T>(obj: T): [keyof T, T[keyof T]][] {
export function fromEntries<T>(entries: [keyof T, T[keyof T]][]): T {
return Object.fromEntries(entries) as T
}
export function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
return Object.fromEntries(keys.map(key => [key, obj[key]])) as Pick<T, K>
}