mirror of
https://github.com/apotdevin/thunderhub.git
synced 2024-11-19 01:40:03 +01:00
feat: Taproot swaps (#611)
This commit is contained in:
parent
6099f178a3
commit
96b5b84dfa
48
package-lock.json
generated
48
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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!
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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'];
|
||||
|
@ -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 <Missing />;
|
||||
@ -129,7 +130,7 @@ export const SwapClaim = () => {
|
||||
</MultiButton>
|
||||
</InputWithDeco>
|
||||
)}
|
||||
<InputWithDeco title={'Fee Amount'} amount={fee * 223} noInput={true}>
|
||||
<InputWithDeco title={'Fee Amount'} amount={fee * 111} noInput={true}>
|
||||
{type !== 'none' && (
|
||||
<Input
|
||||
maxWidth={'240px'}
|
||||
@ -176,6 +177,7 @@ export const SwapClaim = () => {
|
||||
onClick={() =>
|
||||
claimTransaction({
|
||||
variables: {
|
||||
id,
|
||||
redeem: redeemScript,
|
||||
transaction: transactionHex,
|
||||
preimage,
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
initEccLib(ecc);
|
||||
|
||||
const info = detectSwap(redeemScript, lockupTransaction);
|
||||
|
||||
if (info?.vout === undefined || info?.type === undefined) {
|
||||
const checkOutput = (output: any | undefined) => {
|
||||
if (output === undefined) {
|
||||
this.logger.error('Cannot get vout or type from Boltz');
|
||||
this.logger.debug('Swap info', {
|
||||
redeemScript,
|
||||
lockupTransaction,
|
||||
info,
|
||||
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('{');
|
||||
|
||||
if (isTaproot) {
|
||||
const zkp = await zkpInit();
|
||||
const tree = SwapTreeSerializer.deserializeSwapTree(redeem);
|
||||
const output = findTaprootOutput(zkp, lockupTransaction, tree, keys);
|
||||
checkOutput(output);
|
||||
|
||||
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')
|
||||
);
|
||||
|
||||
this.logger.debug('Final transaction', { finalTransaction });
|
||||
|
||||
const response = await this.boltzService.broadcastTransaction(
|
||||
finalTransaction.toHex()
|
||||
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()];
|
||||
|
||||
this.logger.debug('Response from Boltz', { response });
|
||||
|
||||
if (!response?.transactionId) {
|
||||
this.logger.error('Did not receive a transaction id from Boltz');
|
||||
throw new Error('NoTransactionIdFromBoltz');
|
||||
return this.broadcastTransaction(claimTransaction);
|
||||
} catch (e) {
|
||||
this.logger.warn(`Cooperative Swap claim failed`, e);
|
||||
}
|
||||
|
||||
return response.transactionId;
|
||||
// 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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
};
|
||||
}
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user