diff --git a/package-lock.json b/package-lock.json
index de08c2c3..223ded98 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"@nestjs/throttler": "^5.0.1",
"@nestjs/websockets": "^10.2.10",
"@tanstack/react-table": "^8.10.7",
+ "@vulpemventures/secp256k1-zkp": "^3.2.1",
"apollo-server-express": "^3.13.0",
"balanceofsatoshis": "^17.5.2",
"bcryptjs": "^2.4.3",
@@ -32,7 +33,7 @@
"bip32": "^4.0.0",
"bip39": "^3.1.0",
"bitcoinjs-lib": "^6.1.5",
- "boltz-core": "^1.0.4",
+ "boltz-core": "^2.1.1",
"cookie": "^0.6.0",
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
@@ -5251,9 +5252,9 @@
}
},
"node_modules/@openzeppelin/contracts": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.0.tgz",
- "integrity": "sha512-bv2sdS6LKqVVMLI5+zqnNrNU/CA+6z6CmwFXm/MzmOPBRSO5reEJN7z0Gbzvs0/bv/MZZXNklubpwy3v2+azsw=="
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz",
+ "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA=="
},
"node_modules/@otplib/core": {
"version": "12.0.1",
@@ -6416,23 +6417,20 @@
"dev": true
},
"node_modules/@vulpemventures/secp256k1-zkp": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@vulpemventures/secp256k1-zkp/-/secp256k1-zkp-3.1.0.tgz",
- "integrity": "sha512-64Ic62HK/JkjMzKPWcvlw7st/elRrozNqnN6oTaM+M7p1jsJRkCvPWskO5lYxtufKI0Zk2vDLfVBrTTVewBEwg==",
- "peer": true,
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@vulpemventures/secp256k1-zkp/-/secp256k1-zkp-3.2.1.tgz",
+ "integrity": "sha512-2U4nuNbXuUgMmxhuoILbRMoD2DE7KND3udk5cYilIS1MHvMtje9ywUm/zsI0g7d7x8g2A57xri+wvqCC/fCnJg==",
"dependencies": {
- "@types/node": "^13.9.2",
- "long": "^4.0.0"
+ "long": "^5.2.3"
},
"engines": {
- "node": ">=12.0.0"
+ "node": ">=12"
}
},
- "node_modules/@vulpemventures/secp256k1-zkp/node_modules/@types/node": {
- "version": "13.13.52",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
- "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==",
- "peer": true
+ "node_modules/@vulpemventures/secp256k1-zkp/node_modules/long": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
@@ -8303,12 +8301,13 @@
}
},
"node_modules/boltz-core": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/boltz-core/-/boltz-core-1.0.4.tgz",
- "integrity": "sha512-fMaU5pFMkA26cab0J5ghoLpBVA9/BZtF/jprFUGwvHR+4b5lEtiUkEIy2WorfBEhyXJ+jIL7w8Cvqrrlimo0nQ==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/boltz-core/-/boltz-core-2.1.1.tgz",
+ "integrity": "sha512-nbbMQbWcpJKoPvf1KNKZOlcOoflIrcretVhdt4rQ+QXVRULj+lCbl8t3FCfYLNKurWxl7OC3j/f+ILFCU7tkIw==",
"dependencies": {
"@boltz/bitcoin-ops": "^2.0.0",
- "@openzeppelin/contracts": "^5.0.0",
+ "@openzeppelin/contracts": "^5.0.1",
+ "@vulpemventures/secp256k1-zkp": "^3.2.1",
"bip32": "^4.0.0",
"bip65": "^1.0.3",
"bip66": "^1.1.5",
@@ -8321,8 +8320,7 @@
"node": ">=14"
},
"peerDependencies": {
- "@vulpemventures/secp256k1-zkp": "^3.1.0",
- "liquidjs-lib": "^6.0.2-liquid.31"
+ "liquidjs-lib": "^6.0.2-liquid.34"
}
},
"node_modules/bplist-parser": {
@@ -15519,9 +15517,9 @@
}
},
"node_modules/liquidjs-lib": {
- "version": "6.0.2-liquid.32",
- "resolved": "https://registry.npmjs.org/liquidjs-lib/-/liquidjs-lib-6.0.2-liquid.32.tgz",
- "integrity": "sha512-EHKulPNptqGyPZKbWCygdvRP6FIjHLoLRJ0YTAp58Ikm9/t9UjFgeWEwrAwORwKyc4BnWTVwJqNZEBBzjX+cAA==",
+ "version": "6.0.2-liquid.34",
+ "resolved": "https://registry.npmjs.org/liquidjs-lib/-/liquidjs-lib-6.0.2-liquid.34.tgz",
+ "integrity": "sha512-oGW7ianIcrSlK4HdKlhpShx5H4jRxzS/KZahozOb0Vfkz/3PrAXa6fIwuAxfnhOzchVKwqlXerCZvIBXzDQA5g==",
"peer": true,
"dependencies": {
"@types/randombytes": "^2.0.0",
diff --git a/package.json b/package.json
index 3492f1de..f6df38e3 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"@nestjs/throttler": "^5.0.1",
"@nestjs/websockets": "^10.2.10",
"@tanstack/react-table": "^8.10.7",
+ "@vulpemventures/secp256k1-zkp": "^3.2.1",
"apollo-server-express": "^3.13.0",
"balanceofsatoshis": "^17.5.2",
"bcryptjs": "^2.4.3",
@@ -57,7 +58,7 @@
"bip32": "^4.0.0",
"bip39": "^3.1.0",
"bitcoinjs-lib": "^6.1.5",
- "boltz-core": "^1.0.4",
+ "boltz-core": "^2.1.1",
"cookie": "^0.6.0",
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
diff --git a/schema.gql b/schema.gql
index ca380222..c79524e3 100644
--- a/schema.gql
+++ b/schema.gql
@@ -489,7 +489,7 @@ type MessageType {
type Mutation {
addPeer(isTemporary: Boolean, publicKey: String, socket: String, url: String): Boolean!
bosRebalance(avoid: [String!], in_through: String, max_fee: Float, max_fee_rate: Float, max_rebalance: Float, node: String, out_inbound: Float, out_through: String, timeout_minutes: Float): BosRebalanceResult!
- claimBoltzTransaction(destination: String!, fee: Float!, preimage: String!, privateKey: String!, redeem: String!, transaction: String!): String!
+ claimBoltzTransaction(destination: String!, fee: Float!, id: String!, preimage: String!, privateKey: String!, redeem: String!, transaction: String!): String!
claimGhostAddress(address: String): ClaimGhostAddress!
closeChannel(forceClose: Boolean, id: String!, targetConfirmations: Float, tokensPerVByte: Float): OpenOrCloseChannel!
createAddress(type: String! = "p2tr"): String!
diff --git a/src/client/src/graphql/mutations/__generated__/claimBoltzTransaction.generated.tsx b/src/client/src/graphql/mutations/__generated__/claimBoltzTransaction.generated.tsx
index 66852281..3c33fe3a 100644
--- a/src/client/src/graphql/mutations/__generated__/claimBoltzTransaction.generated.tsx
+++ b/src/client/src/graphql/mutations/__generated__/claimBoltzTransaction.generated.tsx
@@ -4,6 +4,7 @@ import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ClaimBoltzTransactionMutationVariables = Types.Exact<{
+ id: Types.Scalars['String']['input'];
redeem: Types.Scalars['String']['input'];
transaction: Types.Scalars['String']['input'];
preimage: Types.Scalars['String']['input'];
@@ -19,6 +20,7 @@ export type ClaimBoltzTransactionMutation = {
export const ClaimBoltzTransactionDocument = gql`
mutation ClaimBoltzTransaction(
+ $id: String!
$redeem: String!
$transaction: String!
$preimage: String!
@@ -27,6 +29,7 @@ export const ClaimBoltzTransactionDocument = gql`
$fee: Float!
) {
claimBoltzTransaction(
+ id: $id
redeem: $redeem
transaction: $transaction
preimage: $preimage
@@ -54,6 +57,7 @@ export type ClaimBoltzTransactionMutationFn = Apollo.MutationFunction<
* @example
* const [claimBoltzTransactionMutation, { data, loading, error }] = useClaimBoltzTransactionMutation({
* variables: {
+ * id: // value for 'id'
* redeem: // value for 'redeem'
* transaction: // value for 'transaction'
* preimage: // value for 'preimage'
diff --git a/src/client/src/graphql/mutations/claimBoltzTransaction.ts b/src/client/src/graphql/mutations/claimBoltzTransaction.ts
index bae0e680..4ed1e643 100644
--- a/src/client/src/graphql/mutations/claimBoltzTransaction.ts
+++ b/src/client/src/graphql/mutations/claimBoltzTransaction.ts
@@ -2,6 +2,7 @@ import { gql } from '@apollo/client';
export const CLAIM_BOLTZ_TRANSACTION = gql`
mutation ClaimBoltzTransaction(
+ $id: String!
$redeem: String!
$transaction: String!
$preimage: String!
@@ -10,6 +11,7 @@ export const CLAIM_BOLTZ_TRANSACTION = gql`
$fee: Float!
) {
claimBoltzTransaction(
+ id: $id
redeem: $redeem
transaction: $transaction
preimage: $preimage
diff --git a/src/client/src/graphql/types.ts b/src/client/src/graphql/types.ts
index caa0d42a..771a9925 100644
--- a/src/client/src/graphql/types.ts
+++ b/src/client/src/graphql/types.ts
@@ -628,6 +628,7 @@ export type MutationBosRebalanceArgs = {
export type MutationClaimBoltzTransactionArgs = {
destination: Scalars['String']['input'];
fee: Scalars['Float']['input'];
+ id: Scalars['String']['input'];
preimage: Scalars['String']['input'];
privateKey: Scalars['String']['input'];
redeem: Scalars['String']['input'];
diff --git a/src/client/src/views/swap/SwapClaim.tsx b/src/client/src/views/swap/SwapClaim.tsx
index 48e75140..a48488cb 100644
--- a/src/client/src/views/swap/SwapClaim.tsx
+++ b/src/client/src/views/swap/SwapClaim.tsx
@@ -77,7 +77,8 @@ export const SwapClaim = () => {
}
const claimingSwap = swaps[claim];
- const { redeemScript, preimage, receivingAddress, privateKey } = claimingSwap;
+ const { redeemScript, preimage, receivingAddress, privateKey, id } =
+ claimingSwap;
if (!preimage || !transactionHex || !privateKey) {
return ;
@@ -129,7 +130,7 @@ export const SwapClaim = () => {
)}
-
+
{type !== 'none' && (
{
onClick={() =>
claimTransaction({
variables: {
+ id,
redeem: redeemScript,
transaction: transactionHex,
preimage,
diff --git a/src/server/modules/api/boltz/boltz.helpers.ts b/src/server/modules/api/boltz/boltz.helpers.ts
index 877df42b..2ab9f216 100644
--- a/src/server/modules/api/boltz/boltz.helpers.ts
+++ b/src/server/modules/api/boltz/boltz.helpers.ts
@@ -1,5 +1,14 @@
-import { address, Network, networks } from 'bitcoinjs-lib';
-import { ECPairFactory, ECPairAPI } from 'ecpair';
+import { Secp256k1ZKP } from '@vulpemventures/secp256k1-zkp';
+import { address, Network, networks, Transaction } from 'bitcoinjs-lib';
+import {
+ detectSwap,
+ extractRefundPublicKeyFromReverseSwapTree,
+ Musig,
+ TaprootUtils,
+} from 'boltz-core';
+import { SwapTree } from 'boltz-core/dist/lib/consts/Types';
+import { randomBytes } from 'crypto';
+import { ECPairFactory, ECPairAPI, ECPairInterface } from 'ecpair';
import * as ecc from 'tiny-secp256k1';
const ECPair: ECPairAPI = ECPairFactory(ecc);
@@ -33,3 +42,39 @@ export const generateKeys = (network: Network = networks.bitcoin) => {
privateKey: getHexString(keys.privateKey),
};
};
+
+export const findTaprootOutput = (
+ zkp: Secp256k1ZKP,
+ transaction: Transaction,
+ tree: SwapTree,
+ keys: ECPairInterface
+) => {
+ const theirPublicKey = extractRefundPublicKeyFromReverseSwapTree(tree);
+
+ // "brute force" the tie breaker because it is not in the onchain script
+ // https://medium.com/blockstream/reducing-bitcoin-transaction-sizes-with-x-only-pubkeys-f86476af05d7
+ for (const tieBreaker of ['02', '03']) {
+ const compressedKey = Buffer.concat([
+ getHexBuffer(tieBreaker),
+ theirPublicKey,
+ ]);
+
+ const musig = new Musig(zkp, keys, randomBytes(32), [
+ compressedKey,
+ keys.publicKey,
+ ]);
+ const tweakedKey = TaprootUtils.tweakMusig(musig, tree.tree);
+
+ const swapOutput = detectSwap(tweakedKey, transaction);
+ if (swapOutput !== undefined) {
+ return {
+ musig,
+ tweakedKey,
+ swapOutput,
+ theirPublicKey: compressedKey,
+ };
+ }
+ }
+
+ return undefined;
+};
diff --git a/src/server/modules/api/boltz/boltz.resolver.ts b/src/server/modules/api/boltz/boltz.resolver.ts
index 0a3af23a..6188ccc3 100644
--- a/src/server/modules/api/boltz/boltz.resolver.ts
+++ b/src/server/modules/api/boltz/boltz.resolver.ts
@@ -1,3 +1,4 @@
+import zkpInit from '@vulpemventures/secp256k1-zkp';
import { Inject } from '@nestjs/common';
import {
Args,
@@ -11,10 +12,22 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import { NodeService } from '../../node/node.service';
import { BoltzService } from './boltz.service';
-import { constructClaimTransaction, detectSwap, targetFee } from 'boltz-core';
-import { generateKeys, getHexBuffer, validateAddress } from './boltz.helpers';
+import {
+ ClaimDetails,
+ SwapTreeSerializer,
+ TaprootUtils,
+ constructClaimTransaction,
+ detectSwap,
+ targetFee,
+} from 'boltz-core';
+import {
+ findTaprootOutput,
+ generateKeys,
+ getHexBuffer,
+ validateAddress,
+} from './boltz.helpers';
import { GraphQLError } from 'graphql';
-import { address, networks, Transaction } from 'bitcoinjs-lib';
+import { address, initEccLib, networks, Transaction } from 'bitcoinjs-lib';
import {
BoltzInfoType,
BoltzSwap,
@@ -106,7 +119,7 @@ export class BoltzResolver {
throw new Error(info.error);
}
- const btcPair = info?.pairs?.['BTC/BTC'];
+ const btcPair = info?.BTC?.BTC;
if (!btcPair) {
this.logger.error('No BTC > LN BTC information received from Boltz');
@@ -129,6 +142,7 @@ export class BoltzResolver {
@Mutation(() => String)
async claimBoltzTransaction(
+ @Args('id') id: string,
@Args('redeem') redeem: string,
@Args('transaction') transaction: string,
@Args('preimage') preimage: string,
@@ -141,54 +155,105 @@ export class BoltzResolver {
throw new GraphQLError('InvalidBitcoinAddress');
}
- const redeemScript = getHexBuffer(redeem);
- const lockupTransaction = Transaction.fromHex(transaction);
-
- const info = detectSwap(redeemScript, lockupTransaction);
+ initEccLib(ecc);
- if (info?.vout === undefined || info?.type === undefined) {
- this.logger.error('Cannot get vout or type from Boltz');
- this.logger.debug('Swap info', {
- redeemScript,
- lockupTransaction,
- info,
- });
- throw new Error('ErrorCreatingClaimTransaction');
- }
+ const checkOutput = (output: any | undefined) => {
+ if (output === undefined) {
+ this.logger.error('Cannot get vout or type from Boltz');
+ this.logger.debug('Swap info', {
+ lockupTransaction,
+ output,
+ });
+ throw new Error('ErrorCreatingClaimTransaction');
+ }
+ };
- const utxos = [
- {
- ...info,
- redeemScript,
- txHash: lockupTransaction.getHash(),
- preimage: getHexBuffer(preimage),
- keys: ECPair.fromPrivateKey(getHexBuffer(privateKey)),
- },
- ];
+ const lockupTransaction = Transaction.fromHex(transaction);
+ const keys = ECPair.fromPrivateKey(getHexBuffer(privateKey));
const destinationScript = address.toOutputScript(
destination,
networks.bitcoin
);
- const finalTransaction = targetFee(fee, absoluteFee =>
- constructClaimTransaction(utxos, destinationScript, absoluteFee)
- );
+ const isTaproot = redeem.startsWith('{');
- this.logger.debug('Final transaction', { finalTransaction });
+ if (isTaproot) {
+ const zkp = await zkpInit();
+ const tree = SwapTreeSerializer.deserializeSwapTree(redeem);
+ const output = findTaprootOutput(zkp, lockupTransaction, tree, keys);
+ checkOutput(output);
- const response = await this.boltzService.broadcastTransaction(
- finalTransaction.toHex()
- );
-
- this.logger.debug('Response from Boltz', { response });
+ const utxo: ClaimDetails = {
+ ...output.swapOutput,
+ keys,
+ swapTree: tree,
+ cooperative: true,
+ preimage: getHexBuffer(preimage),
+ txHash: lockupTransaction.getHash(),
+ internalKey: output.musig.getAggregatedPublicKey(),
+ };
+
+ // Try the cooperative key path spend first
+ try {
+ const claimTransaction = this.constructTransaction(
+ [utxo],
+ destinationScript,
+ fee
+ );
+ const theirPartial =
+ await this.boltzService.getReverseSwapClaimSignature(
+ id,
+ preimage,
+ claimTransaction.toHex(),
+ 0,
+ Buffer.from(output.musig.getPublicNonce()).toString('hex')
+ );
+
+ output.musig.aggregateNonces([
+ [output.theirPublicKey, getHexBuffer(theirPartial.pubNonce)],
+ ]);
+ output.musig.initializeSession(
+ TaprootUtils.hashForWitnessV1([utxo], claimTransaction, 0)
+ );
+ output.musig.addPartial(
+ output.theirPublicKey,
+ getHexBuffer(theirPartial.partialSignature)
+ );
+ output.musig.signPartial();
+ claimTransaction.ins[0].witness = [output.musig.aggregatePartials()];
+
+ return this.broadcastTransaction(claimTransaction);
+ } catch (e) {
+ this.logger.warn(`Cooperative Swap claim failed`, e);
+ }
- if (!response?.transactionId) {
- this.logger.error('Did not receive a transaction id from Boltz');
- throw new Error('NoTransactionIdFromBoltz');
+ // If cooperative fails, enforce the HTLC via a script path spend
+ utxo.cooperative = false;
+ return this.broadcastTransaction(
+ this.constructTransaction([utxo], destinationScript, fee)
+ );
+ } else {
+ const redeemScript = getHexBuffer(redeem);
+ const output = detectSwap(redeemScript, lockupTransaction);
+ checkOutput(output);
+
+ return this.broadcastTransaction(
+ this.constructTransaction(
+ [
+ {
+ ...output,
+ keys,
+ redeemScript,
+ txHash: lockupTransaction.getHash(),
+ preimage: getHexBuffer(preimage),
+ },
+ ],
+ destinationScript,
+ fee
+ )
+ );
}
-
- return response.transactionId;
}
@Mutation(() => CreateBoltzReverseSwapType)
@@ -239,6 +304,7 @@ export class BoltzResolver {
...info,
receivingAddress: btcAddress,
preimage: preimage.toString('hex'),
+ redeemScript: JSON.stringify(info.swapTree),
preimageHash: hash,
privateKey,
publicKey,
@@ -248,4 +314,30 @@ export class BoltzResolver {
return finalInfo;
}
+
+ private constructTransaction = (
+ utxos: ClaimDetails[],
+ destinationScript: Buffer,
+ fee: number
+ ) =>
+ targetFee(fee, absoluteFee =>
+ constructClaimTransaction(utxos, destinationScript, absoluteFee)
+ );
+
+ private broadcastTransaction = async (finalTransaction: Transaction) => {
+ this.logger.debug('Final transaction', { finalTransaction });
+
+ const response = await this.boltzService.broadcastTransaction(
+ finalTransaction.toHex()
+ );
+
+ this.logger.debug('Response from Boltz', { response });
+
+ if (!response?.id) {
+ this.logger.error('Did not receive a transaction id from Boltz');
+ throw new Error('NoTransactionIdFromBoltz');
+ }
+
+ return response.id;
+ };
}
diff --git a/src/server/modules/api/boltz/boltz.service.ts b/src/server/modules/api/boltz/boltz.service.ts
index e0aea122..02b98326 100644
--- a/src/server/modules/api/boltz/boltz.service.ts
+++ b/src/server/modules/api/boltz/boltz.service.ts
@@ -15,7 +15,7 @@ export class BoltzService {
async getPairs() {
try {
const response = await this.fetchService.fetchWithProxy(
- `${this.configService.get('urls.boltz')}/getpairs`
+ `${this.configService.get('urls.boltz')}/v2/swap/reverse`
);
return response.json();
} catch (error: any) {
@@ -24,10 +24,10 @@ export class BoltzService {
}
}
- async getFeeEstimations() {
+ async getFeeEstimation() {
try {
const response = await this.fetchService.fetchWithProxy(
- `${this.configService.get('urls.boltz')}/getfeeestimation`
+ `${this.configService.get('urls.boltz')}/v2/chain/BTC/fee`
);
return response.json();
} catch (error: any) {
@@ -38,14 +38,8 @@ export class BoltzService {
async getSwapStatus(id: string) {
try {
- const body = { id };
const response = await this.fetchService.fetchWithProxy(
- `${this.configService.get('urls.boltz')}/swapstatus`,
- {
- method: 'POST',
- body: JSON.stringify(body),
- headers: { 'Content-Type': 'application/json' },
- }
+ `${this.configService.get('urls.boltz')}/v2/swap/${id}`
);
return response.json();
} catch (error: any) {
@@ -61,16 +55,15 @@ export class BoltzService {
) {
try {
const body = {
- type: 'reversesubmarine',
- pairId: 'BTC/BTC',
- orderSide: 'buy',
+ from: 'BTC',
+ to: 'BTC',
referralId: 'thunderhub',
invoiceAmount,
preimageHash,
claimPublicKey,
};
const response = await this.fetchService.fetchWithProxy(
- `${this.configService.get('urls.boltz')}/createswap`,
+ `${this.configService.get('urls.boltz')}/v2/swap/reverse`,
{
method: 'POST',
body: JSON.stringify(body),
@@ -84,14 +77,48 @@ export class BoltzService {
}
}
+ async getReverseSwapClaimSignature(
+ id: string,
+ preimage: string,
+ transaction: string,
+ index: number,
+ pubNonce: string
+ ): Promise<{
+ pubNonce: string;
+ partialSignature: string;
+ }> {
+ try {
+ const body = {
+ id,
+ index,
+ preimage,
+ pubNonce,
+ transaction,
+ };
+ const response = await this.fetchService.fetchWithProxy(
+ `${this.configService.get('urls.boltz')}/v2/swap/reverse/claim`,
+ {
+ method: 'POST',
+ body: JSON.stringify(body),
+ headers: { 'Content-Type': 'application/json' },
+ }
+ );
+ return response.json();
+ } catch (error: any) {
+ this.logger.error('Error getting partial claim signature from Boltz', {
+ error,
+ });
+ throw new Error(error);
+ }
+ }
+
async broadcastTransaction(transactionHex: string) {
try {
const body = {
- currency: 'BTC',
- transactionHex,
+ hex: transactionHex,
};
const response = await this.fetchService.fetchWithProxy(
- `${this.configService.get('urls.boltz')}/broadcasttransaction`,
+ `${this.configService.get('urls.boltz')}/v2/chain/BTC/transaction`,
{
method: 'POST',
body: JSON.stringify(body),