diff --git a/l2-bridge-ethereum/src/constants.ts b/l2-bridge-ethereum/src/constants.ts index 01f658a5..bd3c7e32 100644 --- a/l2-bridge-ethereum/src/constants.ts +++ b/l2-bridge-ethereum/src/constants.ts @@ -517,8 +517,8 @@ const SCROLL_L1_MESSENGER_EVENTS = [ severity: FindingSeverity.High, type: FindingType.Info, }, - // There is also `event UpdateFeeVault(address _oldFeeVault, address _newFeeVault)` - // for skipping it as not significant enough + // There is also `event UpdateFeeVault(address _oldFeeVault, address _newFeeVault)`, + // but it is skipped as not significant enough ]; const SCROLL_L1_GATEWAY_ROUTER_PROXY_EVENTS = [ @@ -597,7 +597,8 @@ const SCROLL_L1_GATEWAY_ROUTER_EVENTS = [ `\non Gateway Router ${SCROLL_L1_GATEWAY_ROUTER}`, severity: FindingSeverity.Critical, type: FindingType.Info, - // Occurred in block 18318378 but than there were no Lido custom gateway contracts + // Occurred in block 18318378 but than there were no Lido custom gateway contracts, + // thus it cannot be used in tests }, ]; diff --git a/l2-bridge-scroll/src/abi/L2LidoGateway.json b/l2-bridge-scroll/src/abi/L2LidoGateway.json index 7503f932..e392396d 100644 --- a/l2-bridge-scroll/src/abi/L2LidoGateway.json +++ b/l2-bridge-scroll/src/abi/L2LidoGateway.json @@ -858,4 +858,4 @@ "stateMutability": "payable", "type": "function" } -] \ No newline at end of file +] diff --git a/l2-bridge-scroll/src/app.ts b/l2-bridge-scroll/src/app.ts index a35a2c57..60c78619 100644 --- a/l2-bridge-scroll/src/app.ts +++ b/l2-bridge-scroll/src/app.ts @@ -1,4 +1,3 @@ -import { FortaGuardClient } from './clients/forta_guard_client' import { ethers, Finding } from 'forta-agent' import { ScrollClient } from './clients/scroll_client' import { EventWatcher } from './services/event_watcher' @@ -19,7 +18,6 @@ import { BridgeBalanceSrv } from './services/bridge_balance' import { getJsonRpcUrl } from 'forta-agent/dist/sdk/utils' import { BorderTime, HealthChecker, MaxNumberErrorsPerBorderTime } from './services/health-checker/health-checker.srv' - export type Container = { scrollClient: ScrollClient proxyWatcher: ProxyWatcher @@ -47,9 +45,9 @@ export class App { }) const adr = Constants - const scrollRpcURL = FortaGuardClient.getSecret() + const scrollRpcURL = adr.SCROLL_NETWORK_RPC - const nodeClient = new ethers.providers.JsonRpcProvider(scrollRpcURL, adr.L2_NETWORK_ID) + const nodeClient = new ethers.providers.JsonRpcProvider(scrollRpcURL, adr.SCROLL_NETWORK_ID) const l2Bridge = L2LidoGateway__factory.connect(adr.L2_ERC20_TOKEN_GATEWAY.address, nodeClient) const bridgedWSthEthRunner = ERC20Short__factory.connect(adr.SCROLL_WSTETH_BRIDGED.address, nodeClient) diff --git a/l2-bridge-scroll/src/clients/forta_guard_client.ts b/l2-bridge-scroll/src/clients/forta_guard_client.ts deleted file mode 100644 index 633f6f2a..00000000 --- a/l2-bridge-scroll/src/clients/forta_guard_client.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { fetchJwt } from 'forta-agent' -import { verifyJwt } from 'forta-agent/dist/sdk/jwt' -import * as E from 'fp-ts/Either' -import { Constants } from '../utils/constants' - -export class FortaGuardClient { - private fortaGuardURL: string = 'http://127.0.0.1/secret' - private readonly verifyJwt: boolean - - constructor(verifyJwt: boolean) { - this.verifyJwt = verifyJwt - } - - public static getSecret(): string { - return Constants.L2_NETWORK_RPC - } - - public async getSecret(key: string): Promise> { - let token: string - try { - token = await fetchJwt({}) - } catch (e) { - return E.left(new Error(`Could not fetch jwt. cause ${e}`)) - } - - if (this.verifyJwt) { - try { - const isTokenOk = await verifyJwt(token) - if (!isTokenOk) { - return E.left(new Error(`Token verification failed`)) - } - } catch (e) { - return E.left(new Error(`Token verification failed`)) - } - } - - try { - const response = await fetch(this.fortaGuardURL + '/' + key, { - method: 'GET', - headers: { - Authorization: 'Bearer ' + token, - }, - }) - - if (!response.ok) { - return E.left(new Error(`Could not fetch secret, status: ${response.status}`)) - } - const out = await response.text() - - return E.right(out) - } catch (e) { - return E.left(new Error(`Could not fetch secret, cause: ${e}`)) - } - } -} diff --git a/l2-bridge-scroll/src/clients/scroll_client.spec.ts b/l2-bridge-scroll/src/clients/scroll_client.spec.ts index 07833e28..3c9482aa 100644 --- a/l2-bridge-scroll/src/clients/scroll_client.spec.ts +++ b/l2-bridge-scroll/src/clients/scroll_client.spec.ts @@ -11,8 +11,8 @@ describe('ScrollProvider', () => { async () => { const app = await App.getInstance() - const startL2BlockNumber = 31_653_550 - const endL2BlockNumber = 31_653_600 + const startL2BlockNumber = 5_927_000 + const endL2BlockNumber = 5_927_050 const l2Blocks = await app.scrollClient.fetchL2Blocks(startL2BlockNumber, endL2BlockNumber) expect(l2Blocks.length).toEqual(endL2BlockNumber - startL2BlockNumber + 1) @@ -21,17 +21,17 @@ describe('ScrollProvider', () => { ) test( - 'getWstEthTotalSupply is 1177.342778779487684996 wstEth', + 'getWstEthTotalSupply is 7620.760541243359204164 wstEth', async () => { const app = await App.getInstance() - const baseBlockNumber = 31_653_550 + const baseBlockNumber = 5_927_366 const balance = await app.scrollClient.getWstEthTotalSupply(baseBlockNumber) if (E.isLeft(balance)) { throw balance.left } - expect(balance.right.dividedBy(ETH_DECIMALS)).toEqual(new BigNumber('1177.342778779487684996')) + expect(balance.right.dividedBy(ETH_DECIMALS)).toEqual(new BigNumber('7620.760541243359204164')) }, TEST_TIMEOUT, ) diff --git a/l2-bridge-scroll/src/services/monitor_withdrawals.ts b/l2-bridge-scroll/src/services/monitor_withdrawals.ts index fb6a125e..d2eb6923 100644 --- a/l2-bridge-scroll/src/services/monitor_withdrawals.ts +++ b/l2-bridge-scroll/src/services/monitor_withdrawals.ts @@ -8,17 +8,16 @@ import { IMonitorWithdrawalsClient } from '../clients/scroll_client' import { NetworkError } from '../utils/error' import { elapsedTime } from '../utils/time' import { getUniqueKey } from '../utils/finding.helpers' -import { Constants } from '../utils/constants' +import { Constants, ETH_DECIMALS } from '../utils/constants' +import { formatAddress } from 'forta-agent/dist/cli/utils' -const ETH_DECIMALS = new BigNumber(10).pow(18) // 10k wstETH const MAX_WITHDRAWALS_SUM = 10_000 export type MonitorWithdrawalsInitResp = { currentWithdrawals: string } -export const TWO_DAYS = 60 * 60 * 24 * 2 - +export const HOURS_48 = 60 * 60 * 24 * 2 export class MonitorWithdrawals { private readonly name: string = 'WithdrawalsMonitor' @@ -30,7 +29,7 @@ export class MonitorWithdrawals { private readonly l2Erc20TokenGatewayAddress: string private readonly withdrawalsClient: IMonitorWithdrawalsClient - private withdrawalsCache: WithdrawalRecord[] = [] + private withdrawalsStore: WithdrawalRecord[] = [] private lastReportedTooManyWithdrawalsTimestamp = 0 constructor(withdrawalsClient: IMonitorWithdrawalsClient, l2Erc20TokenGatewayAddress: string, logger: Logger) { @@ -44,7 +43,7 @@ export class MonitorWithdrawals { } public async initialize(currentBlock: number): Promise> { - const pastBlock = currentBlock - Math.ceil(TWO_DAYS / Constants.SCROLL_APPROX_BLOCK_TIME_3_SECONDS) + const pastBlock = currentBlock - Math.ceil(HOURS_48 / Constants.SCROLL_APPROX_BLOCK_TIME_3_SECONDS) const withdrawalEvents = await this.withdrawalsClient.getWithdrawalEvents(pastBlock, currentBlock - 1) if (E.isLeft(withdrawalEvents)) { @@ -59,7 +58,7 @@ export class MonitorWithdrawals { const withdrawalsSum = new BigNumber(0) for (const wc of withdrawalRecords.right) { withdrawalsSum.plus(wc.amount) - this.withdrawalsCache.push(wc) + this.withdrawalsStore.push(wc) } this.logger.info(`${MonitorWithdrawals.name} started on block ${currentBlock}`) @@ -68,37 +67,40 @@ export class MonitorWithdrawals { }) } - public handleBlocks(logs: Log[], blocksDto: BlockDto[]): Finding[] { + public handleBlocks(l2Logs: Log[], l2BlocksDto: BlockDto[]): Finding[] { const start = new Date().getTime() // adds records into withdrawalsCache - const withdrawalRecords = this.getWithdrawalRecords(logs, blocksDto) - this.withdrawalsCache.push(...withdrawalRecords) + const withdrawalRecords = this.getWithdrawalRecords(l2Logs, l2BlocksDto) + if (withdrawalRecords.length !== 0) { + this.logger.info(`Withdrawals count = ${withdrawalRecords.length}`) + } + this.withdrawalsStore.push(...withdrawalRecords) const out: Finding[] = [] - for (const block of blocksDto) { + for (const l2Block of l2BlocksDto) { // remove withdrawals records older than MAX_WITHDRAWALS_WINDOW const withdrawalsCache: WithdrawalRecord[] = [] - for (const wc of this.withdrawalsCache) { - if (wc.time > block.timestamp - TWO_DAYS) { + for (const wc of this.withdrawalsStore) { + if (wc.time > l2Block.timestamp - HOURS_48) { withdrawalsCache.push(wc) } } - this.withdrawalsCache = withdrawalsCache + this.withdrawalsStore = withdrawalsCache const withdrawalsSum = new BigNumber(0) - for (const wc of this.withdrawalsCache) { + for (const wc of this.withdrawalsStore) { withdrawalsSum.plus(wc.amount) } // block number condition is meant to "sync" agents alerts - if (withdrawalsSum.div(ETH_DECIMALS).isGreaterThanOrEqualTo(MAX_WITHDRAWALS_SUM) && block.number % 10 === 0) { + if (withdrawalsSum.div(ETH_DECIMALS).isGreaterThanOrEqualTo(MAX_WITHDRAWALS_SUM) && l2Block.number % 10 === 0) { const period = - block.timestamp - this.lastReportedTooManyWithdrawalsTimestamp < TWO_DAYS - ? block.timestamp - this.lastReportedTooManyWithdrawalsTimestamp - : TWO_DAYS + l2Block.timestamp - this.lastReportedTooManyWithdrawalsTimestamp < HOURS_48 + ? l2Block.timestamp - this.lastReportedTooManyWithdrawalsTimestamp + : HOURS_48 const uniqueKey = `C167F276-D519-4906-90CB-C4455E9ABBD4` @@ -110,21 +112,21 @@ export class MonitorWithdrawals { alertId: 'HUGE-WITHDRAWALS-FROM-L2', severity: FindingSeverity.Medium, type: FindingType.Suspicious, - uniqueKey: getUniqueKey(uniqueKey, block.number), + uniqueKey: getUniqueKey(uniqueKey, l2Block.number), }) out.push(finding) - this.lastReportedTooManyWithdrawalsTimestamp = block.timestamp + this.lastReportedTooManyWithdrawalsTimestamp = l2Block.timestamp const tmp: WithdrawalRecord[] = [] - for (const wc of this.withdrawalsCache) { - if (wc.time > block.timestamp - this.lastReportedTooManyWithdrawalsTimestamp) { + for (const wc of this.withdrawalsStore) { + if (wc.time > l2Block.timestamp - this.lastReportedTooManyWithdrawalsTimestamp) { tmp.push(wc) } } - this.withdrawalsCache = tmp + this.withdrawalsStore = tmp } } @@ -132,23 +134,23 @@ export class MonitorWithdrawals { return out } - private getWithdrawalRecords(logs: Log[], blocksDto: BlockDto[]): WithdrawalRecord[] { + private getWithdrawalRecords(l2Logs: Log[], l2BlocksDto: BlockDto[]): WithdrawalRecord[] { const blockNumberToBlock = new Map() const logIndexToLogs = new Map() - const addresses: string[] = [] + const addresses = new Set() - for (const log of logs) { - logIndexToLogs.set(log.logIndex, log) - addresses.push(log.address) + for (const l2Log of l2Logs) { + logIndexToLogs.set(l2Log.logIndex, l2Log) + addresses.add(l2Log.address.toLowerCase()) } - for (const blockDto of blocksDto) { - blockNumberToBlock.set(blockDto.number, blockDto) + for (const l2BlockDto of l2BlocksDto) { + blockNumberToBlock.set(l2BlockDto.number, l2BlockDto) } const out: WithdrawalRecord[] = [] - if (this.l2Erc20TokenGatewayAddress in addresses) { - const events = filterLog(logs, this.withdrawalInitiatedEvent, this.l2Erc20TokenGatewayAddress) + if (formatAddress(this.l2Erc20TokenGatewayAddress) in addresses) { + const events = filterLog(l2Logs, this.withdrawalInitiatedEvent, formatAddress(this.l2Erc20TokenGatewayAddress)) for (const event of events) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/l2-bridge-scroll/src/services/proxy_watcher.ts b/l2-bridge-scroll/src/services/proxy_watcher.ts index f7c8de78..3b7c4b3c 100644 --- a/l2-bridge-scroll/src/services/proxy_watcher.ts +++ b/l2-bridge-scroll/src/services/proxy_watcher.ts @@ -2,7 +2,6 @@ import { Finding, FindingSeverity, FindingType } from 'forta-agent' import { IProxyContractClient } from '../clients/proxy_contract_client' import * as E from 'fp-ts/Either' import { retry } from 'ts-retry' -import { DataRW } from '../utils/mutex' import { getUniqueKey, networkAlert } from '../utils/finding.helpers' import { elapsedTime } from '../utils/time' import { Logger } from 'winston' @@ -63,29 +62,21 @@ export class ProxyWatcher { async handleBlocks(blockNumbers: number[]): Promise { const start = new Date().getTime() + const findings: Finding[] = [] const BLOCK_INTERVAL = 25 - const batchPromises: Promise[] = [] - const out = new DataRW([]) - - for (const blockNumber of blockNumbers) { - if (blockNumber % BLOCK_INTERVAL === 0) { - const promiseProxyImpl = this.handleProxyImplementationChanges(blockNumber).then((findings: Finding[]) => { - out.write(findings) - }) - - const promiseAdminChanges = this.handleProxyAdminChanges(blockNumber).then((findings: Finding[]) => { - out.write(findings) - }) - - batchPromises.push(promiseProxyImpl, promiseAdminChanges) + for (const l2BlockNumber of blockNumbers) { + if (l2BlockNumber % BLOCK_INTERVAL === 0) { + const [implFindings, adminFindings] = await Promise.all([ + this.handleProxyImplementationChanges(l2BlockNumber), + this.handleProxyAdminChanges(l2BlockNumber), + ]) + findings.push(...implFindings, ...adminFindings) } } - await Promise.all(batchPromises) this.logger.info(elapsedTime(ProxyWatcher.name + '.' + this.handleBlocks.name, start)) - - return await out.read() + return findings } private async handleProxyImplementationChanges(blockNumber: number): Promise { diff --git a/l2-bridge-scroll/src/utils/constants.ts b/l2-bridge-scroll/src/utils/constants.ts index af7c721d..468e82fc 100644 --- a/l2-bridge-scroll/src/utils/constants.ts +++ b/l2-bridge-scroll/src/utils/constants.ts @@ -9,8 +9,8 @@ export type RoleHashToName = Map export const ETH_DECIMALS = new BigNumber(10).pow(18) export const Constants = { - L2_NETWORK_RPC: 'https://rpc.scroll.io', - L2_NETWORK_ID: 534352, + SCROLL_NETWORK_RPC: 'https://rpc.scroll.io', + SCROLL_NETWORK_ID: 534352, SCROLL_APPROX_BLOCK_TIME_3_SECONDS: 3, L2_PROXY_ADMIN_CONTRACT_ADDRESS: '0x8e34d07eb348716a1f0a48a507a9de8a3a6dce45', GOV_BRIDGE_ADDRESS: '0x0c67d8d067e349669dfeab132a7c03a90594ee09',