From 0097dcc46c6c8298cfe18494f0c76a08a075e5d7 Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 4 Nov 2025 17:52:53 +0000 Subject: [PATCH] testing swaps --- env.example | 10 +- package-lock.json | 480 ++++++++++++++++++++++++++- package.json | 7 +- src/index.ts | 8 + src/services/lnd/swaps.ts | 341 +++++++++++++++++++ src/services/main/paymentManager.ts | 3 + src/services/main/settings.ts | 17 +- src/services/main/settingsManager.ts | 7 +- 8 files changed, 858 insertions(+), 15 deletions(-) create mode 100644 src/services/lnd/swaps.ts diff --git a/env.example b/env.example index 527657ae..a853c5e8 100644 --- a/env.example +++ b/env.example @@ -14,8 +14,14 @@ # A trusted peer that will hold a node-level account until channel automation becomes affordable # The developer is used by default or you may specify your own # To disable this feature entirely overwrite the env with "null" -#LIQUIDITY_PROVIDER_PUB=null -#DISABLE_LIQUIDITY_PROVIDER=false +# LIQUIDITY_PROVIDER_PUB=null +# DISABLE_LIQUIDITY_PROVIDER=false +# USE_ONLY_LIQUIDITY_PROVIDER=false + +#SWAPS +# BOLTZ_HTTP_URL= +# BOLTZ_WEBSOCKET_URL= +# ENABLE_SWAPS=false #DB #DATABASE_FILE=db.sqlite diff --git a/package-lock.json b/package-lock.json index 4435ce90..41d1d0da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,13 @@ "@types/express": "^4.17.21", "@types/node": "^17.0.31", "@types/secp256k1": "^4.0.3", + "@vulpemventures/secp256k1-zkp": "^3.2.1", "axios": "^1.9.0", "bech32": "^2.0.0", "better-sqlite3": "^12.2.0", "bitcoin-core": "^4.2.0", + "bitcoinjs-lib": "^6.1.7", + "boltz-core": "^3.0.0", "chai": "^4.3.7", "chai-string": "^1.5.0", "copyfiles": "^2.4.1", @@ -29,6 +32,7 @@ "csv": "^6.3.8", "dotenv": "^16.4.5", "eccrypto": "^1.1.6", + "ecpair": "^3.0.0", "express": "^4.21.2", "globby": "^13.1.2", "grpc-tools": "^1.12.4", @@ -40,6 +44,7 @@ "rimraf": "^3.0.2", "rxjs": "^7.5.5", "secp256k1": "^5.0.1", + "tiny-secp256k1": "^2.2.4", "ts-node": "^10.7.0", "ts-proto": "^1.131.2", "typeorm": "^0.3.26", @@ -68,6 +73,12 @@ "typescript": "5.5.4" } }, + "node_modules/@boltz/bitcoin-ops": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@boltz/bitcoin-ops/-/bitcoin-ops-2.0.0.tgz", + "integrity": "sha512-AM7vFNwSD7B4XI6yeRKccWbbD/lvwoFr8U3pqhzryBQo4uMkYe5V3/kMVnml4SNuxzyqdIFu4ur3TId02sC33A==", + "license": "MIT" + }, "node_modules/@bufbuild/protobuf": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.7.0.tgz", @@ -441,6 +452,12 @@ "node": ">=10" } }, + "node_modules/@openzeppelin/contracts": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", + "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1012,6 +1029,16 @@ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "license": "MIT" }, + "node_modules/@types/randombytes": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-+NRgihTfuURllWCiIAhm1wsJqzsocnqXM77V/CalsdJIYSRGEHMnritxh+6EsBklshC+clo1KgnN14qgSGeQdw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -1112,6 +1139,18 @@ "uuid": "bin/uuid" } }, + "node_modules/@vulpemventures/secp256k1-zkp": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@vulpemventures/secp256k1-zkp/-/secp256k1-zkp-3.2.1.tgz", + "integrity": "sha512-2U4nuNbXuUgMmxhuoILbRMoD2DE7KND3udk5cYilIS1MHvMtje9ywUm/zsI0g7d7x8g2A57xri+wvqCC/fCnJg==", + "license": "MIT", + "dependencies": { + "long": "^5.2.3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1684,6 +1723,12 @@ "license": "Apache-2.0", "optional": true }, + "node_modules/base-x": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", + "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==", + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1764,12 +1809,54 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bip174": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.1.tgz", + "integrity": "sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bip174-liquid": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bip174-liquid/-/bip174-liquid-1.0.3.tgz", + "integrity": "sha512-e69sC0Cq2tBJuhG2+wieXv40DN13YBR/wbIjZp4Mqwpar5vQm8Ldqijdd6N33XG7LtfvQi/zKm5fSzdPY/9mgw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bip32": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-4.0.0.tgz", + "integrity": "sha512-aOGy88DDlVUhspIXJN+dVEtclhIsfAUppD43V0j40cPTld3pv/0X/MlrZSZ6jowIaQQzFwP8M6rFU2z2mVYjDQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "@scure/base": "^1.1.1", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bip65": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bip65/-/bip65-1.0.3.tgz", + "integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==", + "license": "ISC", + "engines": { + "node": ">=4.5.0" + } + }, "node_modules/bip66": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", "integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==", "license": "MIT", - "optional": true, "dependencies": { "safe-buffer": "^5.0.1" } @@ -1792,6 +1879,71 @@ "node": ">=7" } }, + "node_modules/bitcoinjs-lib": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz", + "integrity": "sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bech32": "^2.0.0", + "bip174": "^2.1.1", + "bs58check": "^3.0.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib/node_modules/base-x": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz", + "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==", + "license": "MIT" + }, + "node_modules/bitcoinjs-lib/node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "license": "MIT", + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/bitcoinjs-lib/node_modules/bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + }, + "node_modules/bitcoinjs-lib/node_modules/varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bitset": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bitset/-/bitset-5.2.3.tgz", + "integrity": "sha512-uZ7++Z60MC9cZ+7YzQ1v9yPDydcjhmcMjGx2yoGTjjSXBoVMmTr2LCRbkpI19S9P/C75hhP7Bsakj+gVzVUDbQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -1826,6 +1978,26 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/blech32": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/blech32/-/blech32-1.1.2.tgz", + "integrity": "sha512-C5qxzoF9KyX88X8Zz18cZ6BOeL0n5/Eg/cDot1frntkArRMwg1djNim5wA6QFWwu0lJ1LN8iiRMN4Lp2kZzdfA==", + "license": "MIT", + "peer": true, + "dependencies": { + "long": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/blech32/node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/bn.js": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", @@ -1871,6 +2043,42 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/boltz-core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/boltz-core/-/boltz-core-3.0.0.tgz", + "integrity": "sha512-L0jzUnTIb/vir9k6yVlCkV8I29ZeBK7LWq3rrDcp/KP7bB0VvKR5JTceua8g2KBCBM5HOMVDEuPW3c9/82/Nmg==", + "license": "AGPL-3.0", + "dependencies": { + "@boltz/bitcoin-ops": "^2.0.0", + "@openzeppelin/contracts": "^5.2.0", + "@vulpemventures/secp256k1-zkp": "^3.2.1", + "bip32": "^4.0.0", + "bip65": "^1.0.3", + "bip66": "^2.0.0", + "bitcoinjs-lib": "^6.1.7", + "bn.js": "^5.2.1", + "ecpair": "^3.0.0", + "varuint-bitcoin": "^2.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "liquidjs-lib": "^6.0.2-liquid.37" + } + }, + "node_modules/boltz-core/node_modules/bip66": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-2.0.0.tgz", + "integrity": "sha512-kBG+hSpgvZBrkIm9dt5T1Hd/7xGCPEX2npoxAWZfsK1FvjgaxySEh2WizjyIstWXriKo9K9uJ4u0OnsyLDUPXQ==", + "license": "MIT" + }, + "node_modules/boltz-core/node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1914,6 +2122,25 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", + "dependencies": { + "base-x": "^5.0.0" + } + }, + "node_modules/bs58check": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-4.0.0.tgz", + "integrity": "sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^6.0.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2204,7 +2431,6 @@ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", "license": "MIT", - "optional": true, "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" @@ -2545,7 +2771,6 @@ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "license": "MIT", - "optional": true, "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -2559,7 +2784,6 @@ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "license": "MIT", - "optional": true, "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -2980,6 +3204,52 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/ecpair": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-3.0.0.tgz", + "integrity": "sha512-kf4JxjsRQoD4EBzpYjGAcR0t9i/4oAeRPtyCpKvSwyotgkc6oA4E4M0/e+kep7cXe+mgxAvoeh/jdgH9h5+Wxw==", + "license": "MIT", + "dependencies": { + "uint8array-tools": "^0.0.8", + "valibot": "^0.37.0", + "wif": "^5.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/ecpair/node_modules/uint8array-tools": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.8.tgz", + "integrity": "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ecpair/node_modules/valibot": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.37.0.tgz", + "integrity": "sha512-FQz52I8RXgFgOHym3XHYSREbNtkgSjF9prvMFH1nBsRyfL6SfCzoT1GuSDTlbsuPubM7/6Kbw0ZMQb8A+V+VsQ==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ecpair/node_modules/wif": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/wif/-/wif-5.0.0.tgz", + "integrity": "sha512-iFzrC/9ne740qFbNjTZ2FciSRJlHIXoxqk/Y5EnE08QOXu1WjJyCCswwDTYbohAOEnlCtLaAAQBhyaLRFh2hMA==", + "license": "MIT", + "dependencies": { + "bs58check": "^4.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3775,7 +4045,6 @@ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "license": "MIT", - "optional": true, "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -3790,7 +4059,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3805,7 +4073,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", - "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -4332,6 +4599,88 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/liquidjs-lib": { + "version": "6.0.2-liquid.37", + "resolved": "https://registry.npmjs.org/liquidjs-lib/-/liquidjs-lib-6.0.2-liquid.37.tgz", + "integrity": "sha512-AOPwqg9wtLMNxfqOf8afC+bYYrs3VBsTq/Mq/iOe0F2X3CcbgiLeSAcvOY34jbnUre6U+zmuXKP9bWmxnOG08A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/randombytes": "^2.0.0", + "bech32": "^2.0.0", + "bip174-liquid": "^1.0.3", + "bip66": "^1.1.0", + "bitcoinjs-lib": "^6.0.2", + "bitset": "^5.1.1", + "blech32": "^1.0.1", + "bs58check": "^2.0.0", + "create-hash": "^1.2.0", + "ecpair": "^2.1.0", + "slip77": "^0.2.0", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/liquidjs-lib/node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/liquidjs-lib/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/liquidjs-lib/node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "license": "MIT", + "peer": true, + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/liquidjs-lib/node_modules/ecpair": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-2.1.0.tgz", + "integrity": "sha512-cL/mh3MtJutFOvFc27GPZE2pWL3a3k4YvzUWEOvilnfZVlH3Jwgx/7d6tlD7/75tNk8TG2m+7Kgtz0SI1tWcqw==", + "license": "MIT", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0", + "typeforce": "^1.18.0", + "wif": "^2.0.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/liquidjs-lib/node_modules/varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -4512,7 +4861,6 @@ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "license": "MIT", - "optional": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -5765,6 +6113,16 @@ ], "license": "MIT" }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5981,7 +6339,6 @@ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "license": "MIT", - "optional": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -6382,6 +6739,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/slip77": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/slip77/-/slip77-0.2.0.tgz", + "integrity": "sha512-LQaxb1Hef10kU36qvk71tlSt5BWph7GM0j+t2n5zs169X4QfnNbb6xqKZ38X3ETzGmCQaVdBwr925HDagHra/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "^13.9.1", + "create-hmac": "^1.1.7", + "typeforce": "^1.18.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/slip77/node_modules/@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==", + "license": "MIT", + "peer": true + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -6782,6 +7161,27 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/tiny-secp256k1": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.4.tgz", + "integrity": "sha512-FoDTcToPqZE454Q04hH9o2EhxWsm7pOSpicyHkgTwKhdKWdsTUuqfP5MLq3g+VjAtl2vSx6JpXGdwA2qpYkI0Q==", + "license": "MIT", + "dependencies": { + "uint8array-tools": "0.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tiny-secp256k1/node_modules/uint8array-tools": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz", + "integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-buffer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", @@ -7003,6 +7403,12 @@ "node": ">= 0.4" } }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==", + "license": "MIT" + }, "node_modules/typeorm": { "version": "0.3.26", "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.26.tgz", @@ -7356,6 +7762,24 @@ "devOptional": true, "license": "MIT" }, + "node_modules/varuint-bitcoin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-2.0.0.tgz", + "integrity": "sha512-6QZbU/rHO2ZQYpWFDALCDSRsXbAs1VOEmXAxtbtjLtKuMJ/FQ8YbhfxlaiKv5nklci0M6lZtlZyxo9Q+qNnyog==", + "license": "MIT", + "dependencies": { + "uint8array-tools": "^0.0.8" + } + }, + "node_modules/varuint-bitcoin/node_modules/uint8array-tools": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.8.tgz", + "integrity": "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -7458,6 +7882,44 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", + "license": "MIT", + "dependencies": { + "bs58check": "<3.0.0" + } + }, + "node_modules/wif/node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/wif/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/wif/node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "license": "MIT", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index a71062ba..9f77a7be 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,13 @@ "@types/express": "^4.17.21", "@types/node": "^17.0.31", "@types/secp256k1": "^4.0.3", + "@vulpemventures/secp256k1-zkp": "^3.2.1", "axios": "^1.9.0", "bech32": "^2.0.0", "better-sqlite3": "^12.2.0", "bitcoin-core": "^4.2.0", + "bitcoinjs-lib": "^6.1.7", + "boltz-core": "^3.0.0", "chai": "^4.3.7", "chai-string": "^1.5.0", "copyfiles": "^2.4.1", @@ -47,6 +50,7 @@ "csv": "^6.3.8", "dotenv": "^16.4.5", "eccrypto": "^1.1.6", + "ecpair": "^3.0.0", "express": "^4.21.2", "globby": "^13.1.2", "grpc-tools": "^1.12.4", @@ -58,6 +62,7 @@ "rimraf": "^3.0.2", "rxjs": "^7.5.5", "secp256k1": "^5.0.1", + "tiny-secp256k1": "^2.2.4", "ts-node": "^10.7.0", "ts-proto": "^1.131.2", "typeorm": "^0.3.26", @@ -86,4 +91,4 @@ "typescript": "5.5.4" }, "overrides": {} -} +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index aa4c3c0b..40f05ef0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { getLogger } from './services/helpers/logger.js'; import { initMainHandler, initSettings } from './services/main/init.js'; import { nip19 } from 'nostr-tools' import { LoadStorageSettingsFromEnv } from './services/storage/index.js'; +import Main from './services/main/index.js'; //@ts-ignore const { nprofileEncode } = nip19 @@ -44,6 +45,7 @@ const start = async () => { adminManager.setAppNprofile(appNprofile) const Server = NewServer(serverMethods, serverOptions(mainHandler)) Server.Listen(mainHandler.settings.getSettings().serviceSettings.servicePort) + await TMP_swapTest_TMP(mainHandler) // TMP -- remove this } start() @@ -61,4 +63,10 @@ const exitHandler = async (kill: () => void) => { kill(); process.exit(99); }); +} + +const TMP_swapTest_TMP = async (mainHandler: Main) => { + const i = await mainHandler.lnd.NewInvoice(25000, 'test', 3600, { useProvider: false, from: 'user' }) + const decoded = await mainHandler.lnd.DecodeInvoice(i.payRequest) + await mainHandler.paymentManager.swaps.SwapInvoice(i.payRequest, decoded.paymentHash) } \ No newline at end of file diff --git a/src/services/lnd/swaps.ts b/src/services/lnd/swaps.ts new file mode 100644 index 00000000..55734c93 --- /dev/null +++ b/src/services/lnd/swaps.ts @@ -0,0 +1,341 @@ +import zkpInit from '@vulpemventures/secp256k1-zkp'; +import axios from 'axios'; +import { crypto, initEccLib, Transaction, address, Network } from 'bitcoinjs-lib'; +// import bolt11 from 'bolt11'; +import { + Musig, SwapTreeSerializer, TaprootUtils, detectSwap, + constructClaimTransaction, targetFee, OutputType +} from 'boltz-core'; +import { randomBytes, createHash } from 'crypto'; +import { ECPairFactory, ECPairInterface } from 'ecpair'; +import * as ecc from 'tiny-secp256k1'; +import ws from 'ws'; +import { getLogger, PubLogger, ERROR } from '../helpers/logger.js'; +import SettingsManager from '../main/settingsManager.js'; + +type InvoiceSwapResponse = { id: string, claimPublicKey: string, swapTree: string } +type InvoiceSwapInfo = { paymentHash: string, keys: ECPairInterface } +type InvoiceSwapData = { createdResponse: InvoiceSwapResponse, info: InvoiceSwapInfo } + +type TransactionSwapResponse = { id: string, refundPublicKey: string, swapTree: string } +type TransactionSwapInfo = { destinationAddress: string, network: Network, preimage: Buffer, keys: ECPairInterface, txHex: string } +type TransactionSwapData = { createdResponse: TransactionSwapResponse, info: TransactionSwapInfo } + +export class Swaps { + settings: SettingsManager + log: PubLogger + constructor(settings: SettingsManager) { + this.settings = settings + this.log = getLogger({ component: 'SwapsService' }) + initEccLib(ecc) + } + + SwapInvoice = async (invoice: string, paymentHash: string) => { + if (!this.settings.getSettings().swapsSettings.enableSwaps) { + this.log(ERROR, 'Swaps are not enabled'); + return; + } + const keys = ECPairFactory(ecc).makeRandom() + const refundPublicKey = Buffer.from(keys.publicKey).toString('hex') + const req = { invoice, to: 'BTC', from: 'BTC', refundPublicKey } + const url = `${this.settings.getSettings().swapsSettings.boltzHttpUrl}/v2/swap/submarine` + this.log('Sending invoice swap request to', url); + const createdResponse = await loggedPost(this.log, url, req) + if (!createdResponse) { + return; + } + + this.log('Created invoice swap'); + this.log(createdResponse); + + const webSocket = new ws(`${this.settings.getSettings().swapsSettings.boltzWebSocketUrl}/v2/ws`) + const subReq = { op: 'subscribe', channel: 'swap.update', args: [createdResponse.id] } + webSocket.on('open', () => { + webSocket.send(JSON.stringify(subReq)) + }) + + webSocket.on('message', async (rawMsg) => { + try { + await this.handleSwapInvoiceMessage(rawMsg, { createdResponse, info: { paymentHash, keys } }, () => webSocket.close()) + } catch (err: any) { + this.log(ERROR, 'Error handling invoice WebSocket message', err.message) + webSocket.close() + return + } + }); + } + + handleSwapInvoiceMessage = async (rawMsg: ws.RawData, data: InvoiceSwapData, closeWebSocket: () => void) => { + const msg = JSON.parse(rawMsg.toString('utf-8')); + if (msg.event !== 'update') { + return; + } + + this.log('Got invoice WebSocket update'); + this.log(msg); + switch (msg.args[0].status) { + // "invoice.set" means Boltz is waiting for an onchain transaction to be sent + case 'invoice.set': + this.log('Waiting for onchain transaction'); + return; + // Create a partial signature to allow Boltz to do a key path spend to claim the mainchain coins + case 'transaction.claim.pending': + await this.handleInvoiceClaimPending(data) + return; + + case 'transaction.claimed': + this.log('Invoice swap successful'); + closeWebSocket() + return; + } + + } + + handleInvoiceClaimPending = async (data: InvoiceSwapData) => { + this.log('Creating cooperative claim transaction'); + const { createdResponse, info } = data + const { paymentHash, keys } = info + const { boltzHttpUrl } = this.settings.getSettings().swapsSettings + // Get the information request to create a partial signature + const url = `${boltzHttpUrl}/v2/swap/submarine/${createdResponse.id}/claim` + const claimTxDetails = await loggedGet<{ preimage: string, transactionHash: string, pubNonce: string }>(this.log, url) + if (!claimTxDetails) { + return; + } + + // Verify that Boltz actually paid the invoice by comparing the preimage hash + // of the invoice to the SHA256 hash of the preimage from the response + const claimTxPreimageHash = createHash('sha256').update(Buffer.from(claimTxDetails.preimage, 'hex')).digest() + const invoicePreimageHash = Buffer.from(paymentHash, 'hex') + + if (!claimTxPreimageHash.equals(invoicePreimageHash)) { + this.log(ERROR, 'Boltz provided invalid preimage'); + return; + } + + const boltzPublicKey = Buffer.from(createdResponse.claimPublicKey, 'hex') + + // Create a musig signing instance + const musig = new Musig(await zkpInit(), keys, randomBytes(32), [ + boltzPublicKey, + Buffer.from(keys.publicKey), + ]); + // Tweak that musig with the Taptree of the swap scripts + TaprootUtils.tweakMusig( + musig, + SwapTreeSerializer.deserializeSwapTree(createdResponse.swapTree).tree, + ); + + // Aggregate the nonces + musig.aggregateNonces([ + [boltzPublicKey, Buffer.from(claimTxDetails.pubNonce, 'hex')], + ]); + // Initialize the session to sign the transaction hash from the response + musig.initializeSession( + Buffer.from(claimTxDetails.transactionHash, 'hex'), + ); + + // Give our public nonce and the partial signature to Boltz + const claimUrl = `${boltzHttpUrl}/v2/swap/submarine/${createdResponse.id}/claim` + const claimReq = { + pubNonce: Buffer.from(musig.getPublicNonce()).toString('hex'), + partialSignature: Buffer.from(musig.signPartial()).toString('hex'), + } + const claimResponse = await loggedPost<{ pubNonce: string, partialSignature: string }>(this.log, claimUrl, claimReq) + if (!claimResponse) { + return; + } + this.log('Claim response', claimResponse) + } + + SwapTransaction = async (destinationAddress: string, invoiceAmount: number, network: Network) => { + if (!this.settings.getSettings().swapsSettings.enableSwaps) { + this.log(ERROR, 'Swaps are not enabled'); + return; + } + const preimage = randomBytes(32); + const keys = ECPairFactory(ecc).makeRandom() + const url = `${this.settings.getSettings().swapsSettings.boltzHttpUrl}/v2/swap/reverse` + const req = { + invoiceAmount, + to: 'BTC', + from: 'BTC', + claimPublicKey: Buffer.from(keys.publicKey).toString('hex'), + preimageHash: createHash('sha256').update(preimage).digest('hex'), + } + const createdResponse = await loggedPost(this.log, url, req) + if (!createdResponse) { + return; + } + this.log('Created transaction swap'); + this.log(createdResponse); + + const webSocket = new ws(`${this.settings.getSettings().swapsSettings.boltzWebSocketUrl}/v2/ws`) + const subReq = { op: 'subscribe', channel: 'swap.update', args: [createdResponse.id] } + webSocket.on('open', () => { + webSocket.send(JSON.stringify(subReq)) + }) + + webSocket.on('message', async (rawMsg) => { + try { + await this.handleSwapTransactionMessage(rawMsg, { createdResponse, info: { destinationAddress, network, preimage, keys, txHex: '' } }, () => webSocket.close()) + } catch (err: any) { + this.log(ERROR, 'Error handling transaction WebSocket message', err.message) + webSocket.close() + return + } + }) + } + + handleSwapTransactionMessage = async (rawMsg: ws.RawData, data: TransactionSwapData, closeWebSocket: () => void) => { + const msg = JSON.parse(rawMsg.toString('utf-8')); + if (msg.event !== 'update') { + return; + } + + this.log('Got WebSocket update'); + this.log(msg); + + switch (msg.args[0].status) { + // "swap.created" means Boltz is waiting for the invoice to be paid + case 'swap.created': + this.log('Waiting invoice to be paid'); + return; + + // "transaction.mempool" means that Boltz sent an onchain transaction + case 'transaction.mempool': + data.info.txHex = msg.args[0].transaction.hex + await this.handleTransactionMempool(data) + return + case 'invoice.settled': + this.log('Transaction swap successful'); + closeWebSocket() + return; + } + } + + handleTransactionMempool = async (data: TransactionSwapData) => { + this.log('Creating claim transaction'); + const { createdResponse, info } = data + const { destinationAddress, network, keys, preimage, txHex } = info + const boltzPublicKey = Buffer.from( + createdResponse.refundPublicKey, + 'hex', + ); + + // Create a musig signing session and tweak it with the Taptree of the swap scripts + const musig = new Musig(await zkpInit(), keys, randomBytes(32), [ + boltzPublicKey, + Buffer.from(keys.publicKey), + ]); + const tweakedKey = TaprootUtils.tweakMusig( + musig, + SwapTreeSerializer.deserializeSwapTree(createdResponse.swapTree).tree, + ); + + // Parse the lockup transaction and find the output relevant for the swap + const lockupTx = Transaction.fromHex(txHex); + const swapOutput = detectSwap(tweakedKey, lockupTx); + if (swapOutput === undefined) { + this.log(ERROR, 'No swap output found in lockup transaction'); + return; + } + + // Create a claim transaction to be signed cooperatively via a key path spend + const claimTx = targetFee(2, (fee) => + constructClaimTransaction( + [ + { + ...swapOutput, + keys, + preimage, + cooperative: true, + type: OutputType.Taproot, + txHash: lockupTx.getHash(), + }, + ], + address.toOutputScript(destinationAddress, network), + fee, + ), + ); + const { boltzHttpUrl } = this.settings.getSettings().swapsSettings + // Get the partial signature from Boltz + const claimUrl = `${boltzHttpUrl}/v2/swap/reverse/${createdResponse.id}/claim` + const claimReq = { + index: 0, + transaction: claimTx.toHex(), + preimage: preimage.toString('hex'), + pubNonce: Buffer.from(musig.getPublicNonce()).toString('hex'), + } + const boltzSig = await loggedPost<{ pubNonce: string, partialSignature: string }>(this.log, claimUrl, claimReq) + if (!boltzSig) { + return; + } + + // Aggregate the nonces + musig.aggregateNonces([ + [boltzPublicKey, Buffer.from(boltzSig.pubNonce, 'hex')], + ]); + + // Initialize the session to sign the claim transaction + musig.initializeSession( + claimTx.hashForWitnessV1( + 0, + [swapOutput.script], + [swapOutput.value], + Transaction.SIGHASH_DEFAULT, + ), + ); + + // Add the partial signature from Boltz + musig.addPartial( + boltzPublicKey, + Buffer.from(boltzSig.partialSignature, 'hex'), + ); + + // Create our partial signature + musig.signPartial(); + + // Witness of the input to the aggregated signature + claimTx.ins[0].witness = [musig.aggregatePartials()]; + + // Broadcast the finalized transaction + const broadcastUrl = `${boltzHttpUrl}/v2/chain/BTC/transaction` + const broadcastReq = { + hex: claimTx.toHex(), + } + const broadcastResponse = await loggedPost(this.log, broadcastUrl, broadcastReq) + if (!broadcastResponse) { + return; + } + this.log('Transaction broadcasted', broadcastResponse) + } +} + +const loggedPost = async (log: PubLogger, url: string, req: any): Promise => { + try { + const { data } = await axios.post(url, req) + return data as T + } catch (err: any) { + if (err.response?.data) { + log(ERROR, 'Error sending request', err.response.data) + return null + } + log(ERROR, 'Error sending request', err.message) + return null + } +} + +const loggedGet = async (log: PubLogger, url: string): Promise => { + try { + const { data } = await axios.get(url) + return data as T + } catch (err: any) { + if (err.response?.data) { + log(ERROR, 'Error getting request', err.response.data) + return null + } + log(ERROR, 'Error getting request', err.message) + return null + } +} \ No newline at end of file diff --git a/src/services/main/paymentManager.ts b/src/services/main/paymentManager.ts index 4dcd0b6e..6a40facc 100644 --- a/src/services/main/paymentManager.ts +++ b/src/services/main/paymentManager.ts @@ -17,6 +17,7 @@ import { LiquidityManager } from './liquidityManager.js' import { Utils } from '../helpers/utilsWrapper.js' import { UserInvoicePayment } from '../storage/entity/UserInvoicePayment.js' import SettingsManager from './settingsManager.js' +import { Swaps } from '../lnd/swaps.js' interface UserOperationInfo { serial_id: number paid_amount: number @@ -51,6 +52,7 @@ export default class { watchDog: Watchdog liquidityManager: LiquidityManager utils: Utils + swaps: Swaps constructor(storage: Storage, lnd: LND, settings: SettingsManager, liquidityManager: LiquidityManager, utils: Utils, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb) { this.storage = storage this.settings = settings @@ -58,6 +60,7 @@ export default class { this.liquidityManager = liquidityManager this.utils = utils this.watchDog = new Watchdog(settings, this.liquidityManager, this.lnd, this.storage, this.utils, this.liquidityManager.rugPullTracker) + this.swaps = new Swaps(settings) this.addressPaidCb = addressPaidCb this.invoicePaidCb = invoicePaidCb } diff --git a/src/services/main/settings.ts b/src/services/main/settings.ts index 91717fcf..2e557b81 100644 --- a/src/services/main/settings.ts +++ b/src/services/main/settings.ts @@ -165,7 +165,22 @@ export const LoadLiquiditySettingsFromEnv = (dbEnv: Record, addToDb?: EnvCacher): SwapsSettings => { + return { + boltzHttpUrl: chooseEnv("BOLTZ_HTTP_URL", dbEnv, "http://127.0.0.1:9001", addToDb), + boltzWebSocketUrl: chooseEnv("BOLTZ_WEBSOCKET_URL", dbEnv, "ws://127.0.0.1:9004", addToDb), + enableSwaps: chooseEnvBool("ENABLE_SWAPS", dbEnv, false, addToDb) + } } diff --git a/src/services/main/settingsManager.ts b/src/services/main/settingsManager.ts index e5d51e6c..bf30bc63 100644 --- a/src/services/main/settingsManager.ts +++ b/src/services/main/settingsManager.ts @@ -5,7 +5,8 @@ import { LiquiditySettings, LndNodeSettings, LndSettings, LoadLiquiditySettingsFromEnv, LoadLSPSettingsFromEnv, LSPSettings, ServiceFeeSettings, ServiceSettings, LoadServiceFeeSettingsFromEnv, LoadNostrRelaySettingsFromEnv, LoadServiceSettingsFromEnv, LoadWatchdogSettingsFromEnv, - LoadLndNodeSettingsFromEnv, LoadLndSettingsFromEnv, NostrRelaySettings, WatchdogSettings + LoadLndNodeSettingsFromEnv, LoadLndSettingsFromEnv, NostrRelaySettings, WatchdogSettings, SwapsSettings, LoadSwapsSettingsFromEnv + } from "./settings.js" export default class SettingsManager { storage: Storage @@ -27,6 +28,7 @@ export default class SettingsManager { serviceFeeSettings: LoadServiceFeeSettingsFromEnv(dbEnv, addToDb), serviceSettings: LoadServiceSettingsFromEnv(dbEnv, addToDb), watchDogSettings: LoadWatchdogSettingsFromEnv(dbEnv, addToDb), + swapsSettings: LoadSwapsSettingsFromEnv(dbEnv, addToDb), } } @@ -148,5 +150,6 @@ type FullSettings = { nostrRelaySettings: NostrRelaySettings, serviceFeeSettings: ServiceFeeSettings, serviceSettings: ServiceSettings, - lspSettings: LSPSettings + lspSettings: LSPSettings, + swapsSettings: SwapsSettings } \ No newline at end of file