Skip to content

Commit

Permalink
feat: send alert notification when 0-conf is disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Apr 22, 2024
1 parent c375740 commit b135ce6
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 7 deletions.
9 changes: 9 additions & 0 deletions lib/notifications/NotificationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ class NotificationProvider {
});
}

this.service.lockupTransactionTracker.on(
'zeroConf.disabled',
async (symbol) =>
await this.client.sendMessage(
`${Emojis.RotatingLight} **Disabled 0-conf for ${symbol}** ${Emojis.RotatingLight}`,
true,
),
);

const check = async () => {
await Promise.all([
this.checkConnections(),
Expand Down
10 changes: 8 additions & 2 deletions lib/rates/LockupTransactionTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import { Op } from 'sequelize';
import Logger from '../Logger';
import { formatError, getChainCurrency, splitPairId } from '../Utils';
import { IChainClient } from '../chain/ChainClient';
import TypedEventEmitter from '../consts/TypedEventEmitter';
import Swap from '../db/models/Swap';
import PendingLockupTransactionRepository from '../db/repositories/PendingLockupTransactionRepository';
import SwapRepository from '../db/repositories/SwapRepository';
import { Currency } from '../wallet/WalletManager';
import Errors from './Errors';
import RateProvider from './RateProvider';

class LockupTransactionTracker {
class LockupTransactionTracker extends TypedEventEmitter<{
'zeroConf.disabled': string;
}> {
private readonly lock = new AsyncLock();
private readonly zeroConfAcceptedMap = new Map<string, boolean>();

Expand All @@ -19,6 +22,8 @@ class LockupTransactionTracker {
currencies: Map<string, Currency>,
private readonly rateProvider: RateProvider,
) {
super();

for (const currency of currencies.values()) {
if (currency.chainClient === undefined) {
continue;
Expand Down Expand Up @@ -74,11 +79,12 @@ class LockupTransactionTracker {
}
} catch (e) {
this.logger.warn(
`Could find pending lockup transaction of Submarine Swap ${swap.id} (${swap.lockupTransactionId}): ${formatError(e)}`,
`Could not find pending lockup transaction of Submarine Swap ${swap.id} (${swap.lockupTransactionId}): ${formatError(e)}`,
);
this.logger.warn(`Disabling 0-conf for ${chainClient.symbol}`);
this.zeroConfAcceptedMap.set(chainClient.symbol, false);
await this.rateProvider.setZeroConfAmount(chainClient.symbol, 0);
this.emit('zeroConf.disabled', chainClient.symbol);
}
}
};
Expand Down
13 changes: 8 additions & 5 deletions lib/service/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class Service {
public readonly eipSigner: EipSigner;
public readonly musigSigner: MusigSigner;
public readonly rateProvider: RateProvider;
public readonly lockupTransactionTracker: LockupTransactionTracker;

private prepayMinerFee: boolean;

Expand Down Expand Up @@ -171,6 +172,12 @@ class Service {
} addresses for legacy Bitcoin Submarine Swaps`,
);

this.lockupTransactionTracker = new LockupTransactionTracker(
this.logger,
this.currencies,
this.rateProvider,
);

this.swapManager = new SwapManager(
this.logger,
this.walletManager,
Expand All @@ -186,11 +193,7 @@ class Service {
config.retryInterval,
blocks,
config.swap,
new LockupTransactionTracker(
this.logger,
this.currencies,
this.rateProvider,
),
this.lockupTransactionTracker,
);

this.eventHandler = new EventHandler(
Expand Down
7 changes: 7 additions & 0 deletions test/integration/rates/LockupTransactionTracker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('LockupTransactionTracker', () => {

beforeEach(() => {
jest.clearAllMocks();
tracker.removeAllListeners();
});

afterAll(async () => {
Expand Down Expand Up @@ -203,6 +204,8 @@ describe('LockupTransactionTracker', () => {
);

test('should disable 0-conf when lockup transaction cannot be found', async () => {
expect.assertions(5);

expect(tracker.zeroConfAccepted('BTC')).toEqual(true);

const id = '123';
Expand All @@ -220,6 +223,10 @@ describe('LockupTransactionTracker', () => {
},
]);

tracker.once('zeroConf.disabled', (symbol) => {
expect(symbol).toEqual(bitcoinClient.symbol);
});

await bitcoinClient.generate(1);
await wait(100);

Expand Down
23 changes: 23 additions & 0 deletions test/unit/notifications/NotificationProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CurrencyType } from '../../../lib/consts/Enums';
import ChannelCreation from '../../../lib/db/models/ChannelCreation';
import ReverseSwap from '../../../lib/db/models/ReverseSwap';
import Swap from '../../../lib/db/models/Swap';
import { Emojis } from '../../../lib/notifications/Markup';
import NotificationProvider from '../../../lib/notifications/NotificationProvider';
import DiscordClient from '../../../lib/notifications/clients/DiscordClient';
import Service from '../../../lib/service/Service';
Expand All @@ -33,6 +34,7 @@ type failureCallback = (args: {

let emitSwapSuccess: successCallback;
let emitSwapFailure: failureCallback;
let emitZeroConfDisabled: (symbol: string) => void;

const mockGetInfo = jest.fn().mockImplementation(() =>
Promise.resolve({
Expand Down Expand Up @@ -64,6 +66,14 @@ jest.mock('../../../lib/service/Service', () => {
['USDT', { type: CurrencyType.ERC20 }],
['somethingElse', { type: CurrencyType.ERC20 }],
]),
lockupTransactionTracker: {
on: (event: string, callback: any) => {
switch (event) {
case 'zeroConf.disabled':
emitZeroConfDisabled = callback;
}
},
},
getInfo: mockGetInfo,
getBalance: mockGetBalance,
};
Expand Down Expand Up @@ -348,6 +358,19 @@ describe('NotificationProvider', () => {
);
});

test('should send notification when 0-conf is disabled', async () => {
const symbol = 'BTC';

emitZeroConfDisabled(symbol);
await wait(5);

expect(mockSendMessage).toHaveBeenCalledTimes(1);
expect(mockSendMessage).toHaveBeenCalledWith(
`${Emojis.RotatingLight} **Disabled 0-conf for ${symbol}** ${Emojis.RotatingLight}`,
true,
);
});

test('should format failed swaps with no invoice', () => {
const failureReason = 'because';
emitSwapFailure({
Expand Down

0 comments on commit b135ce6

Please sign in to comment.