diff --git a/abis/OffchainOracle.abi.json b/abis/OffchainOracle.abi.json index 57b2d2a..d43f1b5 100644 --- a/abis/OffchainOracle.abi.json +++ b/abis/OffchainOracle.abi.json @@ -1,10 +1,26 @@ [ { "inputs": [ - { "internalType": "contract MultiWrapper", "name": "_multiWrapper", "type": "address" }, - { "internalType": "contract IOracle[]", "name": "existingOracles", "type": "address[]" }, - { "internalType": "enum OffchainOracle.OracleType[]", "name": "oracleTypes", "type": "uint8[]" }, - { "internalType": "contract IERC20[]", "name": "existingConnectors", "type": "address[]" }, + { + "internalType": "contract MultiWrapper", + "name": "_multiWrapper", + "type": "address" + }, + { + "internalType": "contract IOracle[]", + "name": "existingOracles", + "type": "address[]" + }, + { + "internalType": "enum OffchainOracle.OracleType[]", + "name": "oracleTypes", + "type": "uint8[]" + }, + { + "internalType": "contract IERC20[]", + "name": "existingConnectors", + "type": "address[]" + }, { "internalType": "contract IERC20", "name": "wBase", "type": "address" } ], "stateMutability": "nonpayable", @@ -13,7 +29,12 @@ { "anonymous": false, "inputs": [ - { "indexed": false, "internalType": "contract IERC20", "name": "connector", "type": "address" } + { + "indexed": false, + "internalType": "contract IERC20", + "name": "connector", + "type": "address" + } ], "name": "ConnectorAdded", "type": "event" @@ -21,7 +42,12 @@ { "anonymous": false, "inputs": [ - { "indexed": false, "internalType": "contract IERC20", "name": "connector", "type": "address" } + { + "indexed": false, + "internalType": "contract IERC20", + "name": "connector", + "type": "address" + } ], "name": "ConnectorRemoved", "type": "event" @@ -29,7 +55,12 @@ { "anonymous": false, "inputs": [ - { "indexed": false, "internalType": "contract MultiWrapper", "name": "multiWrapper", "type": "address" } + { + "indexed": false, + "internalType": "contract MultiWrapper", + "name": "multiWrapper", + "type": "address" + } ], "name": "MultiWrapperUpdated", "type": "event" @@ -37,7 +68,12 @@ { "anonymous": false, "inputs": [ - { "indexed": false, "internalType": "contract IOracle", "name": "oracle", "type": "address" }, + { + "indexed": false, + "internalType": "contract IOracle", + "name": "oracle", + "type": "address" + }, { "indexed": false, "internalType": "enum OffchainOracle.OracleType", @@ -51,7 +87,12 @@ { "anonymous": false, "inputs": [ - { "indexed": false, "internalType": "contract IOracle", "name": "oracle", "type": "address" }, + { + "indexed": false, + "internalType": "contract IOracle", + "name": "oracle", + "type": "address" + }, { "indexed": false, "internalType": "enum OffchainOracle.OracleType", @@ -65,14 +106,30 @@ { "anonymous": false, "inputs": [ - { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } ], "name": "OwnershipTransferred", "type": "event" }, { - "inputs": [{ "internalType": "contract IERC20", "name": "connector", "type": "address" }], + "inputs": [ + { + "internalType": "contract IERC20", + "name": "connector", + "type": "address" + } + ], "name": "addConnector", "outputs": [], "stateMutability": "nonpayable", @@ -80,8 +137,16 @@ }, { "inputs": [ - { "internalType": "contract IOracle", "name": "oracle", "type": "address" }, - { "internalType": "enum OffchainOracle.OracleType", "name": "oracleKind", "type": "uint8" } + { + "internalType": "contract IOracle", + "name": "oracle", + "type": "address" + }, + { + "internalType": "enum OffchainOracle.OracleType", + "name": "oracleKind", + "type": "uint8" + } ], "name": "addOracle", "outputs": [], @@ -91,14 +156,28 @@ { "inputs": [], "name": "connectors", - "outputs": [{ "internalType": "contract IERC20[]", "name": "allConnectors", "type": "address[]" }], + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "allConnectors", + "type": "address[]" + } + ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "contract IERC20", "name": "srcToken", "type": "address" }, - { "internalType": "contract IERC20", "name": "dstToken", "type": "address" }, + { + "internalType": "contract IERC20", + "name": "srcToken", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "dstToken", + "type": "address" + }, { "internalType": "bool", "name": "useWrappers", "type": "bool" } ], "name": "getRate", @@ -108,7 +187,11 @@ }, { "inputs": [ - { "internalType": "contract IERC20", "name": "srcToken", "type": "address" }, + { + "internalType": "contract IERC20", + "name": "srcToken", + "type": "address" + }, { "internalType": "bool", "name": "useSrcWrappers", "type": "bool" } ], "name": "getRateToEth", @@ -127,8 +210,16 @@ "inputs": [], "name": "oracles", "outputs": [ - { "internalType": "contract IOracle[]", "name": "allOracles", "type": "address[]" }, - { "internalType": "enum OffchainOracle.OracleType[]", "name": "oracleTypes", "type": "uint8[]" } + { + "internalType": "contract IOracle[]", + "name": "allOracles", + "type": "address[]" + }, + { + "internalType": "enum OffchainOracle.OracleType[]", + "name": "oracleTypes", + "type": "uint8[]" + } ], "stateMutability": "view", "type": "function" @@ -141,7 +232,13 @@ "type": "function" }, { - "inputs": [{ "internalType": "contract IERC20", "name": "connector", "type": "address" }], + "inputs": [ + { + "internalType": "contract IERC20", + "name": "connector", + "type": "address" + } + ], "name": "removeConnector", "outputs": [], "stateMutability": "nonpayable", @@ -149,8 +246,16 @@ }, { "inputs": [ - { "internalType": "contract IOracle", "name": "oracle", "type": "address" }, - { "internalType": "enum OffchainOracle.OracleType", "name": "oracleKind", "type": "uint8" } + { + "internalType": "contract IOracle", + "name": "oracle", + "type": "address" + }, + { + "internalType": "enum OffchainOracle.OracleType", + "name": "oracleKind", + "type": "uint8" + } ], "name": "removeOracle", "outputs": [], @@ -165,7 +270,13 @@ "type": "function" }, { - "inputs": [{ "internalType": "contract MultiWrapper", "name": "_multiWrapper", "type": "address" }], + "inputs": [ + { + "internalType": "contract MultiWrapper", + "name": "_multiWrapper", + "type": "address" + } + ], "name": "setMultiWrapper", "outputs": [], "stateMutability": "nonpayable", diff --git a/docker-compose.yml b/docker-compose.yml index 046e810..9059b5e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: environment: REDIS_URL: redis://redis/0 nginx_proxy_read_timeout: 600 - depends_on: [ redis ] + depends_on: [redis] worker1: image: tornadocash/relayer @@ -18,7 +18,7 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] # worker2: # image: tornadocash/relayer:mining @@ -65,12 +65,10 @@ services: # TELEGRAM_NOTIFIER_BOT_TOKEN: ... # TELEGRAM_NOTIFIER_CHAT_ID: ... - - redis: image: redis restart: always - command: [ redis-server, '/usr/local/etc/redis/redis.conf', --appendonly, 'yes', ] + command: [redis-server, '/usr/local/etc/redis/redis.conf', --appendonly, 'yes'] ports: - '6379:6379' volumes: diff --git a/src/queue/health.processor.ts b/src/queue/health.processor.ts index c1a5534..bd156b6 100644 --- a/src/queue/health.processor.ts +++ b/src/queue/health.processor.ts @@ -13,4 +13,3 @@ export const healthProcessor: Processor = async () => { await healthService.setStatus({ status: false, error: e.message }); } }; - diff --git a/src/queue/index.ts b/src/queue/index.ts index 3cde24b..577b512 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -1,4 +1,5 @@ import { Processor, Queue, QueueScheduler, Worker } from 'bullmq'; +import { TransactionReceipt } from '@ethersproject/abstract-provider'; import { JobStatus, RelayerJobType, Token } from '../types'; import { WithdrawalData } from '../services/tx.service'; import { priceProcessor } from './price.processor'; @@ -8,19 +9,23 @@ import { ConfigService } from '../services/config.service'; import { relayerProcessor } from './relayer.processor'; import { healthProcessor } from './health.processor'; -type PriceJobData = Token[] -type PriceJobReturn = number +type PriceJobData = Token[]; +type PriceJobReturn = number; -type HealthJobReturn = void -type HealthJobData = null +type HealthJobReturn = void; +type HealthJobData = null; -export type RelayerJobData = - WithdrawalData - & { id: string, status: JobStatus, type: RelayerJobType, txHash?: string, confirmations?: number } -export type RelayerJobReturn = any +export type RelayerJobData = WithdrawalData & { + id: string; + status: JobStatus; + type: RelayerJobType; + txHash?: string; + confirmations?: number; +}; +export type RelayerJobReturn = TransactionReceipt; -export type RelayerProcessor = Processor -export type PriceProcessor = Processor +export type RelayerProcessor = Processor; +export type PriceProcessor = Processor; @autoInjectable() export class PriceQueueHelper { @@ -28,8 +33,7 @@ export class PriceQueueHelper { _worker: Worker; _scheduler: QueueScheduler; - constructor(private store?: RedisStore) { - } + constructor(private store?: RedisStore) {} get queue() { if (!this._queue) { @@ -56,7 +60,9 @@ export class PriceQueueHelper { get scheduler() { if (!this._scheduler) { - this._scheduler = new QueueScheduler('price', { connection: this.store.client }); + this._scheduler = new QueueScheduler('price', { + connection: this.store.client, + }); } return this._scheduler; } @@ -71,15 +77,13 @@ export class PriceQueueHelper { } } - @autoInjectable() export class RelayerQueueHelper { private _queue: Queue; private _worker: Worker; private _scheduler: QueueScheduler; - constructor(private store?: RedisStore, private config?: ConfigService) { - } + constructor(private store?: RedisStore, private config?: ConfigService) {} get queue() { if (!this._queue) { @@ -103,27 +107,27 @@ export class RelayerQueueHelper { get scheduler() { if (!this._scheduler) { - this._scheduler = new QueueScheduler(this.config.queueName, { connection: this.store.client }); + this._scheduler = new QueueScheduler(this.config.queueName, { + connection: this.store.client, + }); } return this._scheduler; } - - } @autoInjectable() export class HealthQueueHelper { - private _queue: Queue; private _worker: Worker; private _scheduler: QueueScheduler; - constructor(private store?: RedisStore, private config?: ConfigService) { - } + constructor(private store?: RedisStore, private config?: ConfigService) {} get scheduler(): QueueScheduler { if (!this._scheduler) { - this._scheduler = new QueueScheduler('health', { connection: this.store.client }); + this._scheduler = new QueueScheduler('health', { + connection: this.store.client, + }); } return this._scheduler; } @@ -156,6 +160,4 @@ export class HealthQueueHelper { }, }); } - } - diff --git a/src/queue/price.processor.ts b/src/queue/price.processor.ts index 924ee74..0dfb8fc 100644 --- a/src/queue/price.processor.ts +++ b/src/queue/price.processor.ts @@ -7,4 +7,3 @@ export const priceProcessor: PriceProcessor = async (job) => { if (result) return await priceService.savePrices(result); return null; }; - diff --git a/src/services/health.service.ts b/src/services/health.service.ts index f8aeda2..9fd94d8 100644 --- a/src/services/health.service.ts +++ b/src/services/health.service.ts @@ -15,19 +15,23 @@ class RelayerError extends Error { @autoInjectable() export class HealthService { - - constructor(private config: ConfigService, private store: RedisStore) { - } + constructor(private config: ConfigService, private store: RedisStore) {} async clearErrorCodes() { await this.store.client.del('errors:code'); } - private async _getErrors(): Promise<{ errorsLog: { message: string, score: number }[], errorsCode: Record }> { + private async _getErrors(): Promise<{ + errorsLog: { message: string; score: number }[]; + errorsCode: Record; + }> { const logSet = await this.store.client.zrevrange('errors:log', 0, -1, 'WITHSCORES'); const codeSet = await this.store.client.zrevrange('errors:code', 0, -1, 'WITHSCORES'); - return { errorsLog: HealthService._parseSet(logSet), errorsCode: HealthService._parseSet(codeSet, 'object') }; + return { + errorsLog: HealthService._parseSet(logSet), + errorsCode: HealthService._parseSet(codeSet, 'object'), + }; } private async _getStatus() { @@ -53,7 +57,7 @@ export class HealthService { return out; } - async setStatus(status: { status: boolean; error: string; }) { + async setStatus(status: { status: boolean; error: string }) { await this.store.client.hset('health:status', status); } @@ -115,8 +119,6 @@ export class HealthService { await this.config.checkNetwork(); const mainBalance = await this.config.wallet.getBalance(); const tornBalance = await this.config.tokenContract.balanceOf(this.config.wallet.address); - // const mainBalance = BigNumber.from(`${1e18}`).add(1); - // const tornBalance = BigNumber.from(`${60e18}`); const mainStatus = await this._checkBalance(mainBalance, 'MAIN'); const tornStatus = await this._checkBalance(tornBalance, 'TORN'); if (mainStatus.level === 'CRITICAL') { @@ -128,15 +130,10 @@ export class HealthService { } } -type HealthData = { - status: boolean, - error: string, - errorsLog: { message: string, score: number }[] -} type Alert = { - type: string, - message: string, - level: Levels, - time?: number, -} + type: string; + message: string; + level: Levels; + time?: number; +}; export default () => container.resolve(HealthService); diff --git a/src/services/index.ts b/src/services/index.ts index 8c1a200..486f1d5 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -4,4 +4,3 @@ export { default as getJobService } from './job.service'; export { default as getTxService } from './tx.service'; export { default as getNotifierService } from './notifier.service'; export { default as getHealthService } from './health.service'; - diff --git a/src/services/job.service.ts b/src/services/job.service.ts index 460f33d..ee35c8e 100644 --- a/src/services/job.service.ts +++ b/src/services/job.service.ts @@ -7,11 +7,12 @@ import { ConfigService } from './config.service'; @injectable() export class JobService { - constructor(private price?: PriceQueueHelper, + constructor( + private price?: PriceQueueHelper, private relayer?: RelayerQueueHelper, private health?: HealthQueueHelper, - public config?: ConfigService) { - } + public config?: ConfigService, + ) {} async postJob(type: RelayerJobType, data: WithdrawalData) { const id = v4(); @@ -39,10 +40,8 @@ export class JobService { private async _clearSchedulerJobs() { try { - - const jobs = await Promise.all([this.price.queue.getJobs(), this.health.queue.getJobs()]); - await Promise.all(jobs.flat().map(job => job?.remove())); + await Promise.all(jobs.flat().map((job) => job?.remove())); } catch (e) { console.log(e); } diff --git a/src/services/notifier.service.ts b/src/services/notifier.service.ts index ffd03e4..ceba6e9 100644 --- a/src/services/notifier.service.ts +++ b/src/services/notifier.service.ts @@ -2,21 +2,20 @@ import { Telegram } from 'telegraf'; import { autoInjectable, container } from 'tsyringe'; import { RedisStore } from '../modules/redis'; -export type Levels = keyof typeof AlertLevel +export type Levels = keyof typeof AlertLevel; export enum AlertLevel { 'INFO' = 'ℹ️️', 'WARN' = '⚠️', 'CRITICAL' = '‼️', 'ERROR' = '💩', - 'OK' = '✅' + 'OK' = '✅', } export enum AlertType { 'INSUFFICIENT_BALANCE', 'INSUFFICIENT_TORN_BALANCE', - 'RPC' - + 'RPC', } class MockTelegram { @@ -43,7 +42,6 @@ export class NotifierService { this.token = process.env.TELEGRAM_NOTIFIER_BOT_TOKEN; this.chatId = process.env.TELEGRAM_NOTIFIER_CHAT_ID; this.telegram = this.token ? new Telegram(this.token) : new MockTelegram(); - } async processAlert(message: string) { @@ -52,7 +50,7 @@ export class NotifierService { const isSent = await this.store.client.sismember('alerts:sent', `${a}_${b}_${c}`); if (!isSent) { if (alert.level === 'OK') { - this.store.client.srem('alerts:sent', ...['WARN', 'CRITICAL'].map(c => `${a}_${b}_${c}`)); + this.store.client.srem('alerts:sent', ...['WARN', 'CRITICAL'].map((c) => `${a}_${b}_${c}`)); } else { await this.send(alert.message, alert.level); this.store.client.sadd('alerts:sent', alert.type); @@ -63,27 +61,17 @@ export class NotifierService { async subscribe() { this.store.subscriber.subscribe('user-notify'); this.store.subscriber.on('message', async (channel, message) => { - await this.processAlert(message); - }, - ); - - + await this.processAlert(message); + }); } send(message: string, level: Levels) { const text = `${AlertLevel[level]} ${message}`; - return this.telegram.sendMessage( - this.chatId, - text, - { parse_mode: 'HTML' }, - ); + return this.telegram.sendMessage(this.chatId, text, { parse_mode: 'HTML' }); } sendError(e: any) { - return this.telegram.sendMessage( - this.chatId, - `Error: ${e}`, - ); + return this.telegram.sendMessage(this.chatId, `Error: ${e}`); } check() {