Merge pull request #900 from shocknet/refund-filed-swps
refund failed swaps
This commit is contained in:
commit
67f9dfde8b
2 changed files with 39 additions and 11 deletions
|
|
@ -140,7 +140,9 @@ export class SubmarineSwaps {
|
||||||
privateKey: ECPairInterface,
|
privateKey: ECPairInterface,
|
||||||
refundAddress: string,
|
refundAddress: string,
|
||||||
feePerVbyte: number,
|
feePerVbyte: number,
|
||||||
cooperative: boolean = true
|
cooperative: boolean = true,
|
||||||
|
allowUncooperativeFallback: boolean = true,
|
||||||
|
cooperativeErrorMessage?: string
|
||||||
): Promise<{
|
): Promise<{
|
||||||
ok: true,
|
ok: true,
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
|
|
@ -192,7 +194,11 @@ export class SubmarineSwaps {
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!cooperative) {
|
if (!cooperative) {
|
||||||
return { ok: true, transaction: refundTx }
|
return {
|
||||||
|
ok: true,
|
||||||
|
transaction: refundTx,
|
||||||
|
cooperativeError: cooperativeErrorMessage,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For cooperative refund, get Boltz's partial signature
|
// For cooperative refund, get Boltz's partial signature
|
||||||
|
|
@ -210,7 +216,11 @@ export class SubmarineSwaps {
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!boltzSigRes.ok) {
|
if (!boltzSigRes.ok) {
|
||||||
this.log(ERROR, 'Failed to get Boltz partial signature, falling back to uncooperative refund')
|
this.log(ERROR, 'Failed to get Boltz partial signature')
|
||||||
|
if (!allowUncooperativeFallback) {
|
||||||
|
return { ok: false, error: `Failed to get Boltz partial signature: ${boltzSigRes.error}` }
|
||||||
|
}
|
||||||
|
this.log(ERROR, 'Falling back to uncooperative refund')
|
||||||
// Fallback to uncooperative refund
|
// Fallback to uncooperative refund
|
||||||
return await this.constructTaprootRefund(
|
return await this.constructTaprootRefund(
|
||||||
swapId,
|
swapId,
|
||||||
|
|
@ -221,7 +231,9 @@ export class SubmarineSwaps {
|
||||||
privateKey,
|
privateKey,
|
||||||
refundAddress,
|
refundAddress,
|
||||||
feePerVbyte,
|
feePerVbyte,
|
||||||
false
|
false,
|
||||||
|
allowUncooperativeFallback,
|
||||||
|
boltzSigRes.error
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,6 +265,9 @@ export class SubmarineSwaps {
|
||||||
return { ok: true, transaction: refundTx }
|
return { ok: true, transaction: refundTx }
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.log(ERROR, 'Cooperative refund failed:', error.message)
|
this.log(ERROR, 'Cooperative refund failed:', error.message)
|
||||||
|
if (!allowUncooperativeFallback) {
|
||||||
|
return { ok: false, error: `Cooperative refund failed: ${error.message}` }
|
||||||
|
}
|
||||||
// Fallback to uncooperative refund
|
// Fallback to uncooperative refund
|
||||||
return await this.constructTaprootRefund(
|
return await this.constructTaprootRefund(
|
||||||
swapId,
|
swapId,
|
||||||
|
|
@ -263,7 +278,9 @@ export class SubmarineSwaps {
|
||||||
privateKey,
|
privateKey,
|
||||||
refundAddress,
|
refundAddress,
|
||||||
feePerVbyte,
|
feePerVbyte,
|
||||||
false
|
false,
|
||||||
|
allowUncooperativeFallback,
|
||||||
|
error.message
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,9 +321,10 @@ export class SubmarineSwaps {
|
||||||
refundAddress: string,
|
refundAddress: string,
|
||||||
currentHeight: number,
|
currentHeight: number,
|
||||||
lockupTxHex?: string,
|
lockupTxHex?: string,
|
||||||
feePerVbyte?: number
|
feePerVbyte?: number,
|
||||||
|
allowEarlyRefund?: boolean
|
||||||
}): Promise<{ ok: true, publish: { done: false, txHex: string, txId: string } | { done: true, txId: string } } | { ok: false, error: string }> => {
|
}): Promise<{ ok: true, publish: { done: false, txHex: string, txId: string } | { done: true, txId: string } } | { ok: false, error: string }> => {
|
||||||
const { swapId, claimPublicKey, swapTree, timeoutBlockHeight, privateKeyHex, refundAddress, currentHeight, lockupTxHex, feePerVbyte = 2 } = params
|
const { swapId, claimPublicKey, swapTree, timeoutBlockHeight, privateKeyHex, refundAddress, currentHeight, lockupTxHex, feePerVbyte = 2, allowEarlyRefund = false } = params
|
||||||
|
|
||||||
this.log('Starting refund process for swap:', swapId)
|
this.log('Starting refund process for swap:', swapId)
|
||||||
|
|
||||||
|
|
@ -325,14 +343,21 @@ export class SubmarineSwaps {
|
||||||
}
|
}
|
||||||
this.log('Lockup transaction retrieved:', lockupTx.getId())
|
this.log('Lockup transaction retrieved:', lockupTx.getId())
|
||||||
|
|
||||||
// Check if swap has timed out
|
const hasTimedOut = currentHeight >= timeoutBlockHeight
|
||||||
if (currentHeight < timeoutBlockHeight) {
|
|
||||||
|
// For stuck swaps, only allow refund after timeout. For completed (failed) swaps,
|
||||||
|
// we may attempt a cooperative refund before timeout.
|
||||||
|
if (!hasTimedOut && !allowEarlyRefund) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
error: `Swap has not timed out yet. Current height: ${currentHeight}, timeout: ${timeoutBlockHeight}`
|
error: `Swap has not timed out yet. Current height: ${currentHeight}, timeout: ${timeoutBlockHeight}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.log(`Swap has timed out. Current height: ${currentHeight}, timeout: ${timeoutBlockHeight}`)
|
if (hasTimedOut) {
|
||||||
|
this.log(`Swap has timed out. Current height: ${currentHeight}, timeout: ${timeoutBlockHeight}`)
|
||||||
|
} else {
|
||||||
|
this.log(`Swap has not timed out yet, attempting cooperative refund`)
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the private key
|
// Parse the private key
|
||||||
const privateKey = ECPairFactory(ecc).fromPrivateKey(Buffer.from(privateKeyHex, 'hex'))
|
const privateKey = ECPairFactory(ecc).fromPrivateKey(Buffer.from(privateKeyHex, 'hex'))
|
||||||
|
|
@ -347,7 +372,8 @@ export class SubmarineSwaps {
|
||||||
privateKey,
|
privateKey,
|
||||||
refundAddress,
|
refundAddress,
|
||||||
feePerVbyte,
|
feePerVbyte,
|
||||||
true // Try cooperative first
|
true, // Try cooperative first
|
||||||
|
hasTimedOut // only allow uncooperative fallback once timeout has passed
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!refundTxRes.ok) {
|
if (!refundTxRes.ok) {
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ export class Swaps {
|
||||||
if (!swap) {
|
if (!swap) {
|
||||||
throw new Error("Swap not found or already used")
|
throw new Error("Swap not found or already used")
|
||||||
}
|
}
|
||||||
|
const allowEarlyRefund = !!swap.failure_reason
|
||||||
const swapper = this.subSwappers[swap.service_url]
|
const swapper = this.subSwappers[swap.service_url]
|
||||||
if (!swapper) {
|
if (!swapper) {
|
||||||
throw new Error("swapper service not found")
|
throw new Error("swapper service not found")
|
||||||
|
|
@ -124,6 +125,7 @@ export class Swaps {
|
||||||
refundAddress,
|
refundAddress,
|
||||||
swapTree: swap.swap_tree,
|
swapTree: swap.swap_tree,
|
||||||
timeoutBlockHeight: swap.timeout_block_height,
|
timeoutBlockHeight: swap.timeout_block_height,
|
||||||
|
allowEarlyRefund,
|
||||||
feePerVbyte: satPerVByte,
|
feePerVbyte: satPerVByte,
|
||||||
lockupTxHex: swap.lockup_tx_hex,
|
lockupTxHex: swap.lockup_tx_hex,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue