Skip to content

Commit

Permalink
fix: added caching for l1 blocks for l2-bridge-optimism
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeyWh1te committed Jul 8, 2024
1 parent f374408 commit 469f041
Show file tree
Hide file tree
Showing 13 changed files with 497 additions and 400 deletions.
1 change: 1 addition & 0 deletions l2-bridge-optimism/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules/
!.yarn/releases/
dist/
forta.config.json
.env
2 changes: 1 addition & 1 deletion l2-bridge-optimism/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ HTTP_PORT=3000
LOG_FORMAT=simple
LOG_LEVEL=debug
ETHEREUM_RPC_URL=https://eth.drpc.com
OPTIMSIM_RPC_URL=https://optimism.drpc.com
OPTIMSIM_RPC_URL=http://127.0.0.1:8545

USE_FORTA_RPC_URL=true

Expand Down
2 changes: 0 additions & 2 deletions l2-bridge-optimism/.eslintignore

This file was deleted.

14 changes: 0 additions & 14 deletions l2-bridge-optimism/.eslintrc.json

This file was deleted.

35 changes: 35 additions & 0 deletions l2-bridge-optimism/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import eslint from '@eslint/js'
import prettier from 'eslint-plugin-prettier'
import tseslint from 'typescript-eslint'
import tsParser from '@typescript-eslint/parser'
import globals from 'globals'

export default tseslint.config({
...eslint.configs.recommended,
...tseslint.configs.strict,
...tseslint.configs.stylistic,
files: ['** /*.ts'],
ignores: ['**/generated', '**/proto'],
plugins: {
prettier,
},
languageOptions: {
globals: {
...globals.node,
},
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
sourceType: 'module',
},
},
rules: {
'@typescript-eslint/ array-type': 'error',
'@typescript-eslint/ consistent-type-imports': 'error',
'prettier/prettier': 'error',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
curly: 'error',
semi: 'off',
},
})
6 changes: 0 additions & 6 deletions l2-bridge-optimism/jest.config.js

This file was deleted.

72 changes: 39 additions & 33 deletions l2-bridge-optimism/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{
"name": "lido-l2-bridge-optimism-bot",
"name": "lido-l2-bridge-optimism",
"version": "0.0.1",
"description": "Lido Detection Bot for Optimism part of L2 bridge",
"license": "MIT",
"chainIds": [
10
10,
1
],
"chainSettings": {
"default": {
Expand All @@ -24,57 +26,61 @@
"generate-proto": "make gen_ts && make gen_js",
"eslint:lint": "eslint ./src",
"eslint:format": "eslint ./src --fix",
"prettier:check": "prettier --check ./src",
"prettier:format": "prettier --write ./src README.md",
"prettier:check": "prettier --check ./src eslint.config.mjs",
"prettier:format": "prettier --write ./src README.md eslint.config.mjs",
"lint": "yarn run prettier:check && yarn run eslint:lint",
"format": "yarn run eslint:format && yarn run prettier:format",
"postinstall": "yarn generate-types && yarn generate-proto"
},
"dependencies": {
"@ethersproject/abi": "^5.0.0",
"@ethersproject/providers": "^5.0.0",
"@grpc/grpc-js": "^1.10.2",
"@types/lodash": "^4.14.202",
"@types/node": "^20.14.2",
"async-mutex": "^0.4.0",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@grpc/grpc-js": "^1.10.10",
"@typechain/ethers-v5": "^11.1.2",
"@types/node": "^20.14.10",
"async-mutex": "^0.5.0",
"bignumber.js": "^9.1.2",
"dotenv": "^16.4.5",
"ethers": "^5.5.1",
"ethers": "^5.7.2",
"express": "^4.19.2",
"forta-agent": "^0.1.48",
"fp-ts": "^2.16.1",
"lodash": "^4.17.21",
"prom-client": "^15.1.2",
"ts-retry": "^4.2.4",
"winston": "^3.11.0"
"fp-ts": "^2.16.8",
"prom-client": "^15.1.3",
"ts-retry": "^4.2.5",
"winston": "^3.13.0"
},
"devDependencies": {
"@faker-js/faker": "^8.3.1",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.6.0",
"@faker-js/faker": "^8.4.1",
"@jest/globals": "^29.7.0",
"@tsconfig/node20": "^20.1.2",
"@typechain/ethers-v5": "^11.1.2",
"@tsconfig/node20": "^20.1.4",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.11",
"@types/nodemon": "^1.19.0",
"@types/jest": "^29.5.12",
"@types/nodemon": "^1.19.6",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-jest": "^27.6.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint": "^9.6.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"globals": "^15.8.0",
"grpc-tools": "^1.12.4",
"grpc_tools_node_protoc_ts": "^5.3.3",
"husky": "^8.0.3",
"husky": "^9.0.11",
"jest": "^29.7.0",
"nodemon": "^3.0.1",
"postinstall": "^0.8.0",
"prettier": "^3.1.0",
"nodemon": "^3.1.4",
"postinstall": "^0.10.3",
"prettier": "^3.3.2",
"ts-generator": "^0.1.1",
"ts-jest": "^29.1.2",
"ts-jest": "^29.1.5",
"ts-node": "^10.9.2",
"typechain": "^8.3.2",
"typescript": "^5.3.2"
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.0-alpha.39"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"testPathIgnorePatterns": ["dist"]
},
"packageManager": "yarn@1.22.22"
}
4 changes: 2 additions & 2 deletions l2-bridge-optimism/src/clients/eth_client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ describe('ethProvider', () => {
test(
'getBlockNumber',
async () => {
const blockNumber = await l1Client.getBlockNumber()
const blockNumber = await l1Client.getBlock(new Date())
if (E.isLeft(blockNumber)) {
throw blockNumber.left
}

expect(Number.isInteger(blockNumber.right)).toBe(true)
expect(Number.isInteger(blockNumber.right.number)).toBe(true)
},
TEST_TIMEOUT,
)
Expand Down
36 changes: 29 additions & 7 deletions l2-bridge-optimism/src/clients/eth_provider_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { ERC20Bridged } from '../generated/typechain'
import { NetworkError } from '../utils/errors'
import { Metrics, StatusFail, StatusOK } from '../utils/metrics/metrics'
import { ethers } from 'ethers'
import { ETH_BLOCK_TIME_12Sec } from '../utils/constants'
import { BlockDto } from '../entity/blockDto'

const DELAY_IN_500MS = 500
const ATTEMPTS_5 = 5
Expand All @@ -16,6 +18,8 @@ export class ETHProvider implements IL1BridgeBalanceClient {
private readonly ldoRunner: ERC20Bridged
private readonly metrics: Metrics

private latestL1Block: BlockDto | null

constructor(
metric: Metrics,
wStEthRunner: ERC20Bridged,
Expand All @@ -26,6 +30,7 @@ export class ETHProvider implements IL1BridgeBalanceClient {
this.wStEthRunner = wStEthRunner
this.ldoRunner = ldoRunner
this.jsonRpcProvider = jsonRpcProvider
this.latestL1Block = null
}

public async getWstEthBalance(
Expand Down Expand Up @@ -83,22 +88,39 @@ export class ETHProvider implements IL1BridgeBalanceClient {
}
}

public async getBlockNumber(): Promise<E.Either<Error, number>> {
const end = this.metrics.etherJsDurationHistogram.labels({ method: this.getBlockNumber.name }).startTimer()
public async getBlock(latestTime: Date): Promise<E.Either<Error, BlockDto>> {
const end = this.metrics.etherJsDurationHistogram.labels({ method: this.getBlock.name }).startTimer()
try {
const latestBlockNumber = await retryAsync<number>(
async (): Promise<number> => {
return await this.jsonRpcProvider.getBlockNumber()
if (
this.latestL1Block !== null &&
new Date(this.latestL1Block.timestamp * 1000).getTime() + ETH_BLOCK_TIME_12Sec * 1000 >= latestTime.getTime()
) {
this.metrics.etherJsRequest.labels({ method: this.getBlock.name, status: StatusOK }).inc()
end({ status: StatusOK })
return E.right(this.latestL1Block)
}

const latestBlockNumber = await retryAsync<BlockDto>(
async (): Promise<BlockDto> => {
const ethBlock = await this.jsonRpcProvider.getBlock('latest')

return {
number: ethBlock.number,
timestamp: ethBlock.timestamp,
parentHash: ethBlock.parentHash,
hash: ethBlock.hash,
}
},
{ delay: DELAY_IN_500MS, maxTry: 10 },
)

this.metrics.etherJsRequest.labels({ method: this.getBlockNumber.name, status: StatusOK }).inc()
this.metrics.etherJsRequest.labels({ method: this.getBlock.name, status: StatusOK }).inc()
end({ status: StatusOK })

this.latestL1Block = latestBlockNumber
return E.right(latestBlockNumber)
} catch (e) {
this.metrics.etherJsRequest.labels({ method: this.getBlockNumber.name, status: StatusFail }).inc()
this.metrics.etherJsRequest.labels({ method: this.getBlock.name, status: StatusFail }).inc()
end({ status: StatusFail })

return E.left(new NetworkError(e, `Could not fetch latest block number`))
Expand Down
27 changes: 19 additions & 8 deletions l2-bridge-optimism/src/handlers/block.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,33 +65,44 @@ export class BlockHandler {

const findings: Finding[] = []

this.logger.info(`#Optimism block: ${l2blockDtoEvent.number}`)
this.logger.info(
`#Optimism block: ${l2blockDtoEvent.number} at ${new Date(l2blockDtoEvent.timestamp * 1000).toUTCString()}`,
)
const startTime = new Date().getTime()

if (this.onAppStartFindings.length > 0) {
findings.push(...this.onAppStartFindings)
this.onAppStartFindings = []
}

const l1BlockNumber = await this.ethProvider.getBlockNumber()
const l1BlockNumber = await this.ethProvider.getBlock(new Date())
if (E.isLeft(l1BlockNumber)) {
this.logger.warn(`#ETH block skipped`)
findings.push(
networkAlert(
l1BlockNumber.left,
`Error in ${BlockHandler.name}.${this.ethProvider.getBlockNumber.name}:80`,
`Error in ${BlockHandler.name}.${this.ethProvider.getBlock.name}:80`,
`Could not call clientL1.getBlockNumber`,
),
)
}

if (E.isRight(l1BlockNumber)) {
this.logger.info(`#ETH block: ${l1BlockNumber.right}`)
const bridgeBalanceFindings = await this.bridgeBalanceSrv.handleBlock(
l1BlockNumber.right,
l2blockDtoEvent.number,
this.logger.info(
`#ETH block: ${l1BlockNumber.right.number} at ${new Date(l1BlockNumber.right.timestamp * 1000).toUTCString()}`,
)
findings.push(...bridgeBalanceFindings)

if (l1BlockNumber.right.timestamp > l2blockDtoEvent.timestamp) {
this.logger.info(`Skipping checking bridge balances because of l1.block in the future from l2.block`)
}

if (l1BlockNumber.right.timestamp <= l2blockDtoEvent.timestamp) {
const bridgeBalanceFindings = await this.bridgeBalanceSrv.handleBlock(
l1BlockNumber.right.number,
l2blockDtoEvent.number,
)
findings.push(...bridgeBalanceFindings)
}
}

for (const proxyWatcher of this.proxyWatchers) {
Expand Down
11 changes: 5 additions & 6 deletions l2-bridge-optimism/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as Winston from 'winston'
import { Address } from './utils/constants'
import promClient from 'prom-client'
import { Metrics } from './utils/metrics/metrics'
import { getEthersProvider } from 'forta-agent/dist/sdk/utils'
import { getJsonRpcUrl } from 'forta-agent/dist/sdk/utils'
import { MonitorWithdrawals } from './services/monitor_withdrawals'
import { OptimismClient } from './clients/optimism_client'
import { BorderTime, HealthChecker, MaxNumberErrorsPerBorderTime } from './services/health-checker/health-checker.srv'
Expand All @@ -27,7 +27,7 @@ import { getProxyAdminEvents } from './utils/events/proxy_admin_events'
import { AlertHandler } from './handlers/alert.handler'
import { BridgeBalanceSrv } from './services/bridge_balance'
import { ETHProvider } from './clients/eth_provider_client'
import { ethers } from 'forta-agent'
import { ethers } from 'ethers'
import { ERC20Bridged__factory, L2ERC20TokenBridge__factory, OssifiableProxy__factory } from './generated/typechain'

const main = async () => {
Expand All @@ -50,10 +50,9 @@ const main = async () => {
const metrics = new Metrics(mergedRegistry, config.promPrefix)

const optimismProvider = new ethers.providers.JsonRpcProvider(config.optimismRpcUrl, config.chainId)
let nodeClient = getEthersProvider()
if (!config.useFortaProvider) {
nodeClient = optimismProvider
}
const nodeClient = config.useFortaProvider
? new ethers.providers.JsonRpcProvider(getJsonRpcUrl(), config.chainId)
: optimismProvider

const adr: Address = Address

Expand Down
1 change: 1 addition & 0 deletions l2-bridge-optimism/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type Address = {

export type RoleHashToNameMap = Map<string, string>
export const ETH_DECIMALS = new BigNumber(10).pow(18)
export const ETH_BLOCK_TIME_12Sec = 12

const L1_LDO_ADDRESS = '0x5a98fcbea516cf06857215779fd812ca3bef1b32'
const L1_WSTETH_ADDRESS = '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'
Expand Down
Loading

0 comments on commit 469f041

Please sign in to comment.