diff --git a/migration/1772486913000-SetLightningLmRuleOptimal.js b/migration/1772486913000-SetLightningLmRuleOptimal.js new file mode 100644 index 0000000000..885423f00e --- /dev/null +++ b/migration/1772486913000-SetLightningLmRuleOptimal.js @@ -0,0 +1,45 @@ +/** + * @typedef {import('typeorm').MigrationInterface} MigrationInterface + * @typedef {import('typeorm').QueryRunner} QueryRunner + */ + +/** + * Set optimal value for Lightning BTC liquidity management rule 133. + * + * Rule 133 had optimal = NULL, causing the system to always withdraw exactly + * the deficit amount. This leads to micro-withdrawals that fall below exchange + * minimums (e.g. Binance minimum of 0.00002 BTC), triggering repeated + * pause/reactivate cycles without resolving the liquidity deficit. + * + * Setting optimal to 0.00001 adds a small buffer to prevent these cycles. + * + * See: https://github.com/DFXswiss/api/issues/3323 + * + * @class + * @implements {MigrationInterface} + */ +module.exports = class SetLightningLmRuleOptimal1772486913000 { + name = 'SetLightningLmRuleOptimal1772486913000'; + + /** + * @param {QueryRunner} queryRunner + */ + async up(queryRunner) { + await queryRunner.query(` + UPDATE "dbo"."liquidity_management_rule" + SET "optimal" = 0.00001 + WHERE "id" = 133 + `); + } + + /** + * @param {QueryRunner} queryRunner + */ + async down(queryRunner) { + await queryRunner.query(` + UPDATE "dbo"."liquidity_management_rule" + SET "optimal" = NULL + WHERE "id" = 133 + `); + } +}; diff --git a/src/subdomains/core/liquidity-management/adapters/actions/binance.adapter.ts b/src/subdomains/core/liquidity-management/adapters/actions/binance.adapter.ts index 9c314b3e17..3e43afad39 100644 --- a/src/subdomains/core/liquidity-management/adapters/actions/binance.adapter.ts +++ b/src/subdomains/core/liquidity-management/adapters/actions/binance.adapter.ts @@ -60,18 +60,14 @@ export class BinanceAdapter extends CcxtExchangeAdapter { const balance = await this.exchangeService.getAvailableBalance(asset); const withdrawalFee = await this.exchangeService.getWithdrawalFee(asset, network); - const amount = Util.floor( - Math.min( - Math.max(order.maxAmount, BINANCE_LIGHTNING_MIN_WITHDRAWAL_BTC), - balance - withdrawalFee, - BINANCE_LIGHTNING_MAX_WITHDRAWAL_BTC, - ), - 8, - ); + const desiredAmount = Math.max(order.maxAmount, BINANCE_LIGHTNING_MIN_WITHDRAWAL_BTC); + const availableAmount = balance - withdrawalFee; + + const amount = Util.floor(Math.min(desiredAmount, availableAmount, BINANCE_LIGHTNING_MAX_WITHDRAWAL_BTC), 8); if (amount < BINANCE_LIGHTNING_MIN_WITHDRAWAL_BTC) throw new OrderNotProcessableException( - `${this.exchangeService.name}: not enough balance for ${asset} (balance: ${balance}, fee: ${withdrawalFee}, min. withdrawal: ${BINANCE_LIGHTNING_MIN_WITHDRAWAL_BTC})`, + `${this.exchangeService.name}: not enough balance for ${asset} (balance: ${balance}, min. requested: ${order.minAmount}, max. requested: ${order.maxAmount})`, ); const amountSats = LightningHelper.btcToSat(amount);