Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ For auctioneers that were started before multi-pool functionality a db migration
| `priceSources` | (Optional) A list of assets that will have prices sourced from exchanges instead of the pool oracle. |
| `profits` | (Optional) A list of auction profits to define different profit percentages used for matching auctions.
| `slackWebhook` | (Optional) A slack webhook URL to post updates to (https://hooks.slack.com/services/). Leave undefined if no webhooks are required. |
| `discordWebhook` | (Optional) A Discord webhook URL to post updates to. Leave undefined if no webhooks are required. |


#### Fillers

Expand Down
4 changes: 2 additions & 2 deletions src/bidder_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { APP_CONFIG } from './utils/config.js';
import { AuctioneerDatabase, AuctionType } from './utils/db.js';
import { stringify } from './utils/json.js';
import { logger } from './utils/logger.js';
import { sendSlackNotification } from './utils/slack_notifier.js';
import { sendNotification } from './utils/notifier.js';
import { SorobanHelper } from './utils/soroban_helper.js';

export class BidderHandler {
Expand Down Expand Up @@ -104,7 +104,7 @@ export class BidderHandler {
`Fill: ${stringify(fill, 2)}\n` +
`Ledgers To Fill In: ${fill.block - nextLedger}\n`;
if (auctionEntry.fill_block === 0) {
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
}
logger.info(logMessage);
auctionEntry.fill_block = fill.block;
Expand Down
26 changes: 13 additions & 13 deletions src/bidder_submitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { APP_CONFIG, Filler } from './utils/config.js';
import { AuctioneerDatabase, AuctionEntry, AuctionType } from './utils/db.js';
import { serializeError, stringify } from './utils/json.js';
import { logger } from './utils/logger.js';
import { sendSlackNotification } from './utils/slack_notifier.js';
import { sendNotification } from './utils/notifier.js';
import { SorobanHelper } from './utils/soroban_helper.js';
import { SubmissionQueue } from './utils/submission_queue.js';

Expand Down Expand Up @@ -168,16 +168,16 @@ export class BidderSubmitter extends SubmissionQueue<BidderSubmission> {
`Fill Percent ${fill.percent}\n` +
`Ledger Fill Delta ${result.ledger - auctionBid.auctionEntry.start_block}\n` +
`Hash ${result.txHash}\n`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
return true;
} else {
logger.info(
`Fill ledger not reached for auction bid\n` +
`Type: ${auctionBid.auctionEntry.auction_type}\n` +
`Pool: ${auctionBid.auctionEntry.pool_id}\n` +
`User: ${auctionBid.auctionEntry.user_id}\n` +
`Fill Ledger: ${fill.block} Next Ledger: ${nextLedger}`
`Type: ${auctionBid.auctionEntry.auction_type}\n` +
`Pool: ${auctionBid.auctionEntry.pool_id}\n` +
`User: ${auctionBid.auctionEntry.user_id}\n` +
`Fill Ledger: ${fill.block} Next Ledger: ${nextLedger}`
);
}
// allow bidder handler to re-process the auction entry
Expand All @@ -190,7 +190,7 @@ export class BidderSubmitter extends SubmissionQueue<BidderSubmission> {
`User: ${auctionBid.auctionEntry.user_id}\n` +
`Filler: ${auctionBid.filler.name}\n` +
`Error: ${stringify(serializeError(e))}`;
await sendSlackNotification(`<!channel> ` + logMessage);
await sendNotification(logMessage, true);
logger.error(logMessage, e);
return false;
}
Expand Down Expand Up @@ -257,9 +257,9 @@ export class BidderSubmitter extends SubmissionQueue<BidderSubmission> {
);
logger.info(
`Successful unwind for filler: ${fillerUnwind.filler.name}\n` +
`Pool: ${fillerUnwind.poolId}\n` +
`Ledger: ${result.ledger}\n` +
`Hash: ${result.txHash}`
`Pool: ${fillerUnwind.poolId}\n` +
`Ledger: ${result.ledger}\n` +
`Hash: ${result.txHash}`
);
this.addSubmission(
{
Expand All @@ -283,7 +283,7 @@ export class BidderSubmitter extends SubmissionQueue<BidderSubmission> {
`Filler: ${fillerUnwind.filler.name}\n` +
`Backstop Token Balance: ${tokenBalanceFloat}`;
logger.info(logMessage);
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
}
}

Expand All @@ -295,7 +295,7 @@ export class BidderSubmitter extends SubmissionQueue<BidderSubmission> {
`Pool: ${fillerUnwind.poolId}\n` +
`Positions: ${stringify(filler_user.positions, 2)}`;
logger.info(logMessage);
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
return true;
}

Expand Down Expand Up @@ -373,6 +373,6 @@ export class BidderSubmitter extends SubmissionQueue<BidderSubmission> {
break;
}
logger.error(logMessage);
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
}
}
4 changes: 2 additions & 2 deletions src/liquidations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AuctioneerDatabase, AuctionType } from './utils/db.js';
import { logger } from './utils/logger.js';
import { SorobanHelper } from './utils/soroban_helper.js';
import { WorkSubmission, WorkSubmissionType } from './work_submitter.js';
import { sendSlackNotification } from './utils/slack_notifier.js';
import { sendNotification } from './utils/notifier.js';
import { stringify } from './utils/json.js';

/**
Expand Down Expand Up @@ -324,7 +324,7 @@ export async function checkUsersForLiquidationsAndBadDebt(
`User: ${user}\n` +
`Error: ${e}`;
logger.error(errorLog);
sendSlackNotification(errorLog);
sendNotification(errorLog);
}
}
return submissions;
Expand Down
12 changes: 6 additions & 6 deletions src/pool_event_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AuctioneerDatabase, AuctionEntry, AuctionType } from './utils/db.js';
import { stringify } from './utils/json.js';
import { logger } from './utils/logger.js';
import { deadletterEvent, sendEvent } from './utils/messages.js';
import { sendSlackNotification } from './utils/slack_notifier.js';
import { sendNotification } from './utils/notifier.js';
import { SorobanHelper } from './utils/soroban_helper.js';
import { WorkSubmission } from './work_submitter.js';
const MAX_RETRIES = 2;
Expand Down Expand Up @@ -113,7 +113,7 @@ export class PoolEventHandler {
`Pool: ${poolId}\n` +
`User: ${poolEvent.event.user}\n` +
`Auction Data: ${stringify(poolEvent.event.auctionData, 2)}\n`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
fillerFound = true;
break;
Expand All @@ -125,7 +125,7 @@ export class PoolEventHandler {
`Pool: ${poolId}\n` +
`User: ${poolEvent.event.user}\n` +
`Auction Data: ${stringify(poolEvent.event.auctionData, 2)}\n`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
}
break;
Expand All @@ -142,7 +142,7 @@ export class PoolEventHandler {
`Liquidation Auction Deleted\n` +
`Pool: ${poolId}\n` +
`User: ${poolEvent.event.user}\n`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
}
break;
Expand All @@ -157,7 +157,7 @@ export class PoolEventHandler {
`User: ${poolEvent.event.user}\n` +
`Fill Percent: ${poolEvent.event.fillAmount}\n` +
`Tx Hash: ${poolEvent.event.txHash}\n`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
if (poolEvent.event.fillAmount === BigInt(100)) {
// auction was fully filled, remove from ongoing auctions
Expand Down Expand Up @@ -219,7 +219,7 @@ export class PoolEventHandler {
`Type: ${AuctionType[auctionType]}\n` +
`Pool: ${poolId}\n` +
`User: ${user}`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export interface AppConfig {
priceSources: PriceSource[] | undefined;
profits: AuctionProfit[] | undefined;
slackWebhook: string | undefined;
discordWebhook: string | undefined;
highBaseFee: number | undefined;
baseFee: number | undefined;
}
Expand Down Expand Up @@ -99,6 +100,7 @@ export function validateAppConfig(config: any): boolean {
(config.priceSources !== undefined && !Array.isArray(config.priceSources)) ||
(config.profits !== undefined && !Array.isArray(config.profits)) ||
(config.slackWebhook !== undefined && typeof config.slackWebhook !== 'string') ||
(config.discordWebhook !== undefined && typeof config.discordWebhook !== 'string') ||
(config.highBaseFee !== undefined && typeof config.highBaseFee !== 'number') ||
(config.baseFee !== undefined && typeof config.baseFee !== 'number')
) {
Expand Down
73 changes: 73 additions & 0 deletions src/utils/notifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { APP_CONFIG } from './config.js';
import { logger } from './logger.js';

async function sendSlackNotification(message: string, tag: boolean = false): Promise<void> {
try {
if (APP_CONFIG.slackWebhook) {
const taggedMessage = tag ? `<!channel> ${message}` : message;
const response = await fetch(APP_CONFIG.slackWebhook, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: `*Bot Name*: ${APP_CONFIG.name}\n${taggedMessage}`,
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
} catch (e) {
logger.error(`Error sending Slack notification: ${e}`);
}
}

async function sendDiscordNotification(message: string, tag: boolean = false): Promise<void> {
try {
if (APP_CONFIG.discordWebhook) {
const taggedMessage = tag ? `@everyone ${message}` : message;
const response = await fetch(APP_CONFIG.discordWebhook, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: `**${APP_CONFIG.name}**\n${taggedMessage}`,
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
} catch (e) {
logger.error(`Error sending Discord notification: ${e}`);
}
}

export async function sendNotification(message: string, tag: boolean = false): Promise<void> {
// If no webhooks are configured, log to console as fallback
if (!APP_CONFIG.slackWebhook && !APP_CONFIG.discordWebhook) {
console.log(
`Bot Name: ${APP_CONFIG.name}\nTimestamp: ${new Date().toISOString()}\n${message}`
);
return;
}

// Send to both platforms in parallel if configured
const notifications = [];

if (APP_CONFIG.slackWebhook) {
notifications.push(sendSlackNotification(message, tag));
}

if (APP_CONFIG.discordWebhook) {
notifications.push(sendDiscordNotification(message, tag));
}

try {
await Promise.all(notifications);
} catch (error) {
logger.error(`Error sending notifications: ${error}`);
}
}
27 changes: 0 additions & 27 deletions src/utils/slack_notifier.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/work_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AuctioneerDatabase, AuctionType } from './utils/db.js';
import { logger } from './utils/logger.js';
import { deadletterEvent } from './utils/messages.js';
import { setPrices } from './utils/prices.js';
import { sendSlackNotification } from './utils/slack_notifier.js';
import { sendNotification } from './utils/notifier.js';
import { SorobanHelper } from './utils/soroban_helper.js';
import { WorkSubmissionType, WorkSubmitter } from './work_submitter.js';
import { canFillerBid, checkFillerSupport, getFillerAvailableBalances } from './filler.js';
Expand Down Expand Up @@ -154,7 +154,7 @@ export class WorkHandler {
`Pool: ${poolId}\n` +
`User: ${user.user_id}`;
logger.error(logMessage);
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
}

const { estimate: poolUserEstimate, user: poolUser } =
Expand Down
12 changes: 6 additions & 6 deletions src/work_submitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { APP_CONFIG, Filler } from './utils/config.js';
import { AuctionType } from './utils/db.js';
import { serializeError, stringify } from './utils/json.js';
import { logger } from './utils/logger.js';
import { sendSlackNotification } from './utils/slack_notifier.js';
import { sendNotification } from './utils/notifier.js';
import { SorobanHelper } from './utils/soroban_helper.js';
import { SubmissionQueue } from './utils/submission_queue.js';
import { Address, Contract, nativeToScVal } from '@stellar/stellar-sdk';
Expand Down Expand Up @@ -90,7 +90,7 @@ export class WorkSubmitter extends SubmissionQueue<WorkSubmission> {
`Lot: ${stringify(auction.lot)}\n`;

logger.info(logMessage);
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
return true;
} catch (e: any) {
const logMessage =
Expand All @@ -103,7 +103,7 @@ export class WorkSubmitter extends SubmissionQueue<WorkSubmission> {
`Lot: ${stringify(auction.lot)}\n` +
`Error: ${stringify(serializeError(e))}\n`;
logger.error(logMessage);
await sendSlackNotification(`<!channel>\n` + logMessage);
await sendNotification(logMessage, true);

// if pool throws a "LIQ_TOO_SMALL" or "LIQ_TOO_LARGE" error, adjust the fill percentage
// by 1 percentage point before retrying.
Expand Down Expand Up @@ -133,7 +133,7 @@ export class WorkSubmitter extends SubmissionQueue<WorkSubmission> {
`Successfully transferred bad debt to backstop\n` +
`Pool: ${badDebtTransfer.poolId}\n` +
`User: ${badDebtTransfer.user}`;
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
logger.info(logMessage);
return true;
} catch (e: any) {
Expand All @@ -143,7 +143,7 @@ export class WorkSubmitter extends SubmissionQueue<WorkSubmission> {
`User: ${badDebtTransfer.user}` +
`Error: ${stringify(serializeError(e))}\n`;
logger.error(logMessage);
await sendSlackNotification(`<!channel> ` + logMessage);
await sendNotification(logMessage, true);
return false;
}
}
Expand All @@ -167,6 +167,6 @@ export class WorkSubmitter extends SubmissionQueue<WorkSubmission> {
break;
}
logger.error(logMessage);
await sendSlackNotification(logMessage);
await sendNotification(logMessage);
}
}
8 changes: 4 additions & 4 deletions test/bidder_handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { AppEvent, EventType, LedgerEvent } from '../src/events';
import { APP_CONFIG, AppConfig } from '../src/utils/config';
import { AuctioneerDatabase, AuctionEntry, AuctionType } from '../src/utils/db';
import { logger } from '../src/utils/logger';
import { sendSlackNotification } from '../src/utils/slack_notifier';
import { sendNotification } from '../src/utils/notifier';
import { SorobanHelper } from '../src/utils/soroban_helper';
import { inMemoryAuctioneerDb } from './helpers/mocks';

Expand Down Expand Up @@ -84,7 +84,7 @@ jest.mock('../src/liquidations');
jest.mock('../src/utils/soroban_helper');
jest.mock('../src/bidder_submitter');
jest.mock('../src/auction');
jest.mock('../src/utils/slack_notifier');
jest.mock('../src/utils/notifier');

describe('BidderHandler', () => {
let bidderHandler: BidderHandler;
Expand All @@ -95,8 +95,8 @@ describe('BidderHandler', () => {
let mockedCalcAuctionFill = calculateAuctionFill as jest.MockedFunction<
typeof calculateAuctionFill
>;
let mockedSendSlackNotif = sendSlackNotification as jest.MockedFunction<
typeof sendSlackNotification
let mockedSendSlackNotif = sendNotification as jest.MockedFunction<
typeof sendNotification
>;

beforeEach(() => {
Expand Down
Loading