Feat/272 split sdk and postman (#381)

* feat: split postman and sdk

* fix: update postman client and sendMessage script

* fix: clean the sdk

* fix: update sdk dependencies

* fix: remove .env.sample file

* fix: remove testing helpers from the build

* fix: update gas provider in linea sdk and update postman client

* fix: update postman dependencies

* fix: update postman dockerfile and fix tests imports and types

* fix: remove unused code in the sdk + move typechain folder

* fix: remove unused code + fix imports in postman

* fix: pnpm lock file issue

* fix: import issue

* fix: case sensitive file issue

* fix: update sdk fees options and update exports

* fix: remove postman unused code and adjust imports and tests

* fix: update contracts abis + clean error parsing

* fix: update postman based on new SDk changes

* add readme + remove unused interface in postman

* fix: rename Base.ts file to BaseError.ts

* fix: rename Base.ts file to BaseError.ts in postman

* chore: update readme for the postman

* fix: rename maxFeePerGas to maxFeePerGasCap

* fix: update DefaultGasProvider fees check

* fix: default gas provider test issue

* fix: update main ci filter

* fix: issue in default gas provider
This commit is contained in:
Victorien Gauch
2024-12-09 12:12:45 +01:00
committed by GitHub
parent 4e93fa3a10
commit f58c12a455
217 changed files with 7157 additions and 7230 deletions

5
.dockerignore Normal file
View File

@@ -0,0 +1,5 @@
**/node_modules
**/typechain
**/tsconfig.build.tsbuildinfo
**/coverage
**/dist

View File

@@ -52,7 +52,9 @@ jobs:
- 'docker/compose-local-dev.overrides.yml'
- 'docker/compose-local-dev-traces-v2.overrides.yml'
postman:
- 'postman/**'
- 'sdk/**'
- 'ts-libs/linea-native-libs/**'
- '.github/workflows/postman-*.yml'
- '.github/workflows/build-and-publish.yml'
- '.github/workflows/main.yml'
@@ -193,7 +195,7 @@ jobs:
secrets: inherit
run-e2e-tests-geth-tracing:
needs: [ store-image-name-and-tags, docker-build, manual-docker-build-and-e2e-tests ]
needs: [ store-image-name-and-tags, docker-build, manual-docker-build-and-e2e-tests, filter-commit-changes ]
# Make this execute for has-changes-requiring-build == 'false' so that we can get to the required job @ which is in reuse-run-e2e-tests.yml
if: ${{ always() && needs.filter-commit-changes.outputs.has-changes-requiring-build == 'false' || needs.docker-build.result == 'success' }}
concurrency:

View File

@@ -93,7 +93,7 @@ jobs:
if: ${{ env.PUSH_IMAGE == 'false' }}
with:
context: ./
file: ./sdk/Dockerfile
file: ./postman/Dockerfile
platforms: linux/amd64
load: true
push: false
@@ -116,7 +116,7 @@ jobs:
if: ${{ env.PUSH_IMAGE == 'true' || github.event_name == 'workflow_dispatch' }}
with:
context: ./
file: ./sdk/Dockerfile
file: ./postman/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.TAGS }}

View File

@@ -24,4 +24,5 @@ jobs:
run: |
pnpm run -F ./ts-libs/linea-native-libs build;
pnpm run -F ./sdk build;
pnpm run -F ./postman test;
pnpm run -F ./sdk test;

632
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ packages:
- 'contracts/**'
- 'e2e/**'
- 'sdk/**'
- 'postman/**'
- 'operations/**'
- 'bridge-ui/**'
- 'ts-libs/**'

44
postman/.env.sample Normal file
View File

@@ -0,0 +1,44 @@
L1_RPC_URL=http://localhost:8445
L1_CONTRACT_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
# Only use the following PRIVATE KEY for testing
L1_SIGNER_PRIVATE_KEY=0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
L1_LISTENER_INTERVAL=2000
# L1_LISTENER_INITIAL_FROM_BLOCK=0
L1_LISTENER_BLOCK_CONFIRMATION=1
L1_MAX_BLOCKS_TO_FETCH_LOGS=1000
L1_MAX_GAS_FEE_ENFORCED=false
L2_RPC_URL=http://localhost:8845
L2_CONTRACT_ADDRESS=0xe537D669CA013d86EBeF1D64e40fC74CADC91987
# Only use the following PRIVATE KEY for testing
L2_SIGNER_PRIVATE_KEY=0xfcf854e0a0bc6fd7e97d7050e61a362c915cecd6767a32267b22e8b7af572e58
L2_LISTENER_INTERVAL=2000
# L2_LISTENER_INITIAL_FROM_BLOCK=0
L2_LISTENER_BLOCK_CONFIRMATION=0
L2_MAX_BLOCKS_TO_FETCH_LOGS=1000
L2_MESSAGE_TREE_DEPTH=5
L2_MAX_GAS_FEE_ENFORCED=false
MESSAGE_SUBMISSION_TIMEOUT=300000
MAX_FETCH_MESSAGES_FROM_DB=1000
MAX_NONCE_DIFF=10000
MAX_FEE_PER_GAS_CAP=100000000000
GAS_ESTIMATION_PERCENTILE=50
PROFIT_MARGIN=0.0
MAX_NUMBER_OF_RETRIES=100
RETRY_DELAY_IN_SECONDS=30
MAX_CLAIM_GAS_LIMIT=2560000
MAX_TX_RETRIES=20
L1_L2_EOA_ENABLED=true
L1_L2_CALLDATA_ENABLED=true
L1_L2_AUTO_CLAIM_ENABLED=true
L2_L1_EOA_ENABLED=true
L2_L1_CALLDATA_ENABLED=true
L2_L1_AUTO_CLAIM_ENABLED=true
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=postman_db
DB_CLEANER_ENABLED=false
DB_CLEANING_INTERVAL=10000
DB_DAYS_BEFORE_NOW_TO_DELETE=1
ENABLE_LINEA_ESTIMATE_GAS=false

3
postman/.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
dist
node_modules
typechain

15
postman/.eslintrc.js Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
extends: "../.eslintrc.js",
env: {
commonjs: true,
es2021: true,
node: true,
jest: true,
},
parserOptions: {
sourceType: "module",
},
rules: {
"prettier/prettier": "error",
},
};

3
postman/.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
dist
node_modules
typechain

3
postman/.prettierrc.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
...require('../.prettierrc.js'),
};

View File

@@ -18,16 +18,19 @@ ARG NATIVE_LIBS_RELEASE_TAG
ENV NATIVE_LIBS_RELEASE_TAG=${NATIVE_LIBS_RELEASE_TAG}
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.json ./
COPY ./postman/package.json ./postman/package.json
COPY ./sdk/package.json ./sdk/package.json
COPY ./ts-libs/linea-native-libs/package.json ./ts-libs/linea-native-libs/package.json
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
COPY ./postman ./postman
COPY ./sdk ./sdk
COPY ts-libs/linea-native-libs ./ts-libs/linea-native-libs
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm run build \
&& pnpm deploy --filter=./sdk --prod ./prod/sdk
&& pnpm deploy --filter=./postman --prod ./prod/postman
FROM node:lts-slim AS production
@@ -37,6 +40,6 @@ WORKDIR /usr/src/app
USER node
COPY --from=builder /usr/src/app/prod/sdk ./sdk
COPY --from=builder /usr/src/app/prod/postman ./postman
CMD [ "node", "./sdk/dist/scripts/runPostman.js" ]
CMD [ "node", "./postman/dist/scripts/runPostman.js" ]

13
postman/LICENSE Normal file
View File

@@ -0,0 +1,13 @@
Copyright 2023 Consensys Software Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

115
postman/README.md Normal file
View File

@@ -0,0 +1,115 @@
# Linea Postman Service
The Linea Postman service is a component of the Linea blockchain infrastructure that facilitates cross-chain message delivery between Layer 1 (Ethereum) and Layer 2 (Linea).
## Overview
The Postman service monitors and processes messages between L1 and L2 chains, handling message submission, verification, and claiming. It operates as a Docker container and integrates with both L1 and L2 nodes.
It offers the following key features:
- Feature 1: Listening for message sent events on Ethereum and Linea
- Feature 2: Listening for message hash anchoring events to check if a message is ready to be claimed
- Feature 3: Automatic claiming of messages with a configurable retry mechanism
- Feature 4: Checking receipt status for each transaction
All messages are stored in a configurable Postgres DB.
## Configuration
### Environment Variables
#### L1 Configuration
- `L1_RPC_URL`: Ethereum node RPC endpoint
- `L1_CONTRACT_ADDRESS`: Address of the LineaRollup contract on L1
- `L1_SIGNER_PRIVATE_KEY`: Private key for L1 transactions
- `L1_LISTENER_INTERVAL`: Block listening interval (ms)
- `L1_LISTENER_INITIAL_FROM_BLOCK`: Starting block for event listening (optional)
- `L1_LISTENER_BLOCK_CONFIRMATION`: Required block confirmations
- `L1_MAX_BLOCKS_TO_FETCH_LOGS`: Maximum blocks to fetch in one request
- `L1_MAX_GAS_FEE_ENFORCED`: Enable/disable gas fee enforcement
#### L2 Configuration
- `L2_RPC_URL`: Linea node RPC endpoint
- `L2_CONTRACT_ADDRESS`: Address of the L2MessageService contract on L2
- `L2_SIGNER_PRIVATE_KEY`: Private key for L2 transactions
- `L2_LISTENER_INTERVAL`: Block listening interval (ms)
- `L2_LISTENER_INITIAL_FROM_BLOCK`: Starting block for event listening (optional)
- `L2_LISTENER_BLOCK_CONFIRMATION`: Required block confirmations
- `L2_MAX_BLOCKS_TO_FETCH_LOGS`: Maximum blocks to fetch in one request
- `L2_MAX_GAS_FEE_ENFORCED`: Enable/disable gas fee enforcement
- `L2_MESSAGE_TREE_DEPTH`: Depth of the message Merkle tree
#### Message Processing
- `MESSAGE_SUBMISSION_TIMEOUT`: Timeout for message submission (ms)
- `MAX_FETCH_MESSAGES_FROM_DB`: Maximum messages to fetch from database
- `MAX_NONCE_DIFF`: Maximum allowed nonce difference between the DB and the chain
- `MAX_FEE_PER_GAS_CAP`: Maximum gas fee cap
- `GAS_ESTIMATION_PERCENTILE`: Gas estimation percentile
- `PROFIT_MARGIN`: Profit margin for gas fees
- `MAX_NUMBER_OF_RETRIES`: Maximum retry attempts
- `RETRY_DELAY_IN_SECONDS`: Delay between retries
- `MAX_CLAIM_GAS_LIMIT`: Maximum gas limit for claim transactions
#### Feature Flags
- `L1_L2_EOA_ENABLED`: Enable L1->L2 EOA messages
- `L1_L2_CALLDATA_ENABLED`: Enable L1->L2 calldata messages
- `L1_L2_AUTO_CLAIM_ENABLED`: Enable auto-claiming for L1->L2 messages
- `L2_L1_EOA_ENABLED`: Enable L2->L1 EOA messages
- `L2_L1_CALLDATA_ENABLED`: Enable L2->L1 calldata messages
- `L2_L1_AUTO_CLAIM_ENABLED`: Enable auto-claiming for L2->L1 messages
- `ENABLE_LINEA_ESTIMATE_GAS`: Enable `linea_estimateGas`endpoint usage for L2 chain gas fees estimation
- `DB_CLEANER_ENABLED`: Enable DB cleaning to delete old claimed messages
#### DB cleaning
- `DB_CLEANING_INTERVAL`: DB cleaning polling interval (ms)
- `DB_DAYS_BEFORE_NOW_TO_DELETE`: Number of days to retain messages in the database before deletion. Messages older than this number of days will be automatically cleaned up if they are in a final state (CLAIMED_SUCCESS, CLAIMED_REVERTED, EXCLUDED, or ZERO_FEE)
#### Database Configuration
- `POSTGRES_HOST`: PostgreSQL host
- `POSTGRES_PORT`: PostgreSQL port
- `POSTGRES_USER`: Database user
- `POSTGRES_PASSWORD`: Database password
- `POSTGRES_DB`: Database name
## Development
### Running
#### Start the docker local stack
From the root folder, run the following command:
```bash
make fresh-start-all
```
Stop the postman docker container manually.
#### Run the postman locally:
From the postman folder run the following commands:
```bash
# Create a new .env file
cp .env.sample .env
# Run the postman
ts-node scripts/runPostman.ts
```
### Building
```bash
# Build the Postman service
pnpm run build
```
### Testing
```bash
# Run unit tests
pnpm run test
```
## License
This package is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for more information.

25
postman/jest.config.js Normal file
View File

@@ -0,0 +1,25 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
rootDir: ".",
testRegex: "test.ts$",
verbose: true,
collectCoverage: true,
collectCoverageFrom: ["src/**/*.ts"],
coverageReporters: ["html", "lcov", "text"],
testPathIgnorePatterns: [
"src/clients/blockchain/typechain",
"src/application/postman/persistence/migrations/",
"src/application/postman/persistence/repositories/",
"src/index.ts",
"src/utils/WinstonLogger.ts",
],
coveragePathIgnorePatterns: [
"src/clients/blockchain/typechain",
"src/application/postman/persistence/migrations/",
"src/application/postman/persistence/repositories/",
"src/index.ts",
"src/utils/WinstonLogger.ts",
"src/utils/testing/",
],
};

44
postman/package.json Normal file
View File

@@ -0,0 +1,44 @@
{
"name": "@consensys/linea-postman",
"version": "1.0.0",
"author": "Consensys Software Inc.",
"license": "Apache-2.0",
"description": "",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"scripts": {
"lint:ts": "npx eslint '**/*.ts'",
"lint:ts:fix": "npx eslint --fix '**/*.ts'",
"prettier": "prettier -c '**/*.ts'",
"prettier:fix": "prettier -w '**/*.ts'",
"clean": "rimraf dist node_modules coverage tsconfig.build.tsbuildinfo",
"build": "tsc -p tsconfig.build.json",
"build:runSdk": "tsc ./scripts/runSdk.ts",
"test": "npx jest --bail --detectOpenHandles --forceExit",
"lint:fix": "pnpm run lint:ts:fix && pnpm run prettier:fix"
},
"dependencies": {
"@consensys/linea-native-libs": "workspace:*",
"@consensys/linea-sdk": "workspace:*",
"better-sqlite3": "11.6.0",
"class-validator": "0.14.1",
"dotenv": "16.4.5",
"ethers": "6.13.4",
"pg": "8.13.1",
"typeorm": "0.3.20",
"typeorm-naming-strategies": "4.1.0",
"winston": "3.17.0"
},
"devDependencies": {
"@jest/globals": "29.7.0",
"@types/jest": "29.5.14",
"@types/yargs": "17.0.33",
"jest": "29.7.0",
"jest-mock-extended": "3.0.5",
"ts-jest": "29.2.5",
"yargs": "17.7.2"
},
"files": [
"dist/**/*"
]
}

View File

@@ -32,7 +32,7 @@ async function main() {
? parseInt(process.env.MESSAGE_SUBMISSION_TIMEOUT)
: undefined,
maxNonceDiff: process.env.MAX_NONCE_DIFF ? parseInt(process.env.MAX_NONCE_DIFF) : undefined,
maxFeePerGas: process.env.MAX_FEE_PER_GAS ? BigInt(process.env.MAX_FEE_PER_GAS) : undefined,
maxFeePerGasCap: process.env.MAX_FEE_PER_GAS_CAP ? BigInt(process.env.MAX_FEE_PER_GAS_CAP) : undefined,
gasEstimationPercentile: process.env.GAS_ESTIMATION_PERCENTILE
? parseInt(process.env.GAS_ESTIMATION_PERCENTILE)
: undefined,
@@ -72,7 +72,7 @@ async function main() {
? parseInt(process.env.MESSAGE_SUBMISSION_TIMEOUT)
: undefined,
maxNonceDiff: process.env.MAX_NONCE_DIFF ? parseInt(process.env.MAX_NONCE_DIFF) : undefined,
maxFeePerGas: process.env.MAX_FEE_PER_GAS ? BigInt(process.env.MAX_FEE_PER_GAS) : undefined,
maxFeePerGasCap: process.env.MAX_FEE_PER_GAS_CAP ? BigInt(process.env.MAX_FEE_PER_GAS_CAP) : undefined,
gasEstimationPercentile: process.env.GAS_ESTIMATION_PERCENTILE
? parseInt(process.env.GAS_ESTIMATION_PERCENTILE)
: undefined,

View File

@@ -1,17 +1,10 @@
import { BytesLike, ContractTransactionReceipt, Overrides, Wallet, JsonRpcProvider } from "ethers";
import { config } from "dotenv";
import { L2MessageService, L2MessageService__factory, LineaRollup, LineaRollup__factory } from "@consensys/linea-sdk";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { SendMessageArgs } from "./types";
import { sanitizeAddress, sanitizePrivKey } from "./cli";
import {
L2MessageService,
L2MessageService__factory,
ZkEvmV2__factory,
ZkEvmV2,
LineaRollup,
LineaRollup__factory,
} from "../src/clients/blockchain/typechain";
import { encodeSendMessage } from "./helpers";
config();
@@ -86,7 +79,7 @@ const argv = yargs(hideBin(process.argv))
.parseSync();
const sendMessage = async (
contract: ZkEvmV2,
contract: LineaRollup,
args: SendMessageArgs,
overrides: Overrides = {},
): Promise<ContractTransactionReceipt | null> => {
@@ -95,7 +88,7 @@ const sendMessage = async (
};
const sendMessages = async (
contract: ZkEvmV2,
contract: LineaRollup,
signer: Wallet,
numberOfMessages: number,
args: SendMessageArgs,
@@ -118,7 +111,7 @@ const sendMessages = async (
};
const getMessageCounter = async (contractAddress: string, signer: Wallet) => {
const lineaRollup = ZkEvmV2__factory.connect(contractAddress, signer) as ZkEvmV2;
const lineaRollup = LineaRollup__factory.connect(contractAddress, signer) as LineaRollup;
return lineaRollup.nextMessageNumber();
};
@@ -158,11 +151,15 @@ const main = async (args: typeof argv) => {
calldata: args.calldata,
};
const zkEvmV2 = ZkEvmV2__factory.connect(args.l1ContractAddress, l1Signer) as ZkEvmV2;
const lineaRollup = LineaRollup__factory.connect(args.l1ContractAddress, l1Signer) as LineaRollup;
await sendMessages(zkEvmV2, l1Signer, args.numberOfMessage, functionArgs, { value: BigInt(args.value.toString()) });
await sendMessages(lineaRollup, l1Signer, args.numberOfMessage, functionArgs, {
value: BigInt(args.value.toString()),
});
// Anchor messages hash on L2
if (!args.autoAnchoring) return;
const nextMessageCounter = await getMessageCounter(args.l1ContractAddress, l1Signer);
const startCounter = nextMessageCounter - BigInt(args.numberOfMessage);
@@ -180,10 +177,7 @@ const main = async (args: typeof argv) => {
messageHashesToAnchor.push(messageHash);
}
if (!args.autoAnchoring) return;
const l2Signer = new Wallet(args.l2PrivKey!, l2Provider);
const lineaRollup = LineaRollup__factory.connect(args.l1ContractAddress, l1Signer) as LineaRollup;
const l2MessageService = L2MessageService__factory.connect(args.l2ContractAddress, l2Signer) as L2MessageService;
const startingMessageNumber = startCounter;
await anchorMessageHashesOnL2(lineaRollup, l2MessageService, messageHashesToAnchor, startingMessageNumber);

View File

@@ -2,9 +2,9 @@ import { ContractTransactionReceipt, Overrides, JsonRpcProvider, Wallet } from "
import { config } from "dotenv";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { L2MessageService, L2MessageService__factory } from "@consensys/linea-sdk";
import { SendMessageArgs } from "./types";
import { sanitizeAddress, sanitizePrivKey } from "./cli";
import { L2MessageService, L2MessageService__factory } from "../src/clients/blockchain/typechain";
config();

View File

@@ -1,38 +1,28 @@
import { Wallet, JsonRpcProvider } from "ethers";
import { DataSource } from "typeorm";
import { LineaSDK, Direction } from "@consensys/linea-sdk";
import { ILogger } from "../../../core/utils/logging/ILogger";
import { DatabaseCleaner } from "../../../services/persistence/DatabaseCleaner";
import { TypeOrmMessageRepository } from "../persistence/repositories/TypeOrmMessageRepository";
import { LineaRollupClient } from "../../../clients/blockchain/ethereum/LineaRollupClient";
import { L2MessageServiceClient } from "../../../clients/blockchain/linea/L2MessageServiceClient";
import { EthersLineaRollupLogClient } from "../../../clients/blockchain/ethereum/EthersLineaRollupLogClient";
import { ChainQuerier } from "../../../clients/blockchain/ChainQuerier";
import { WinstonLogger } from "../../../utils/WinstonLogger";
import { EthersL2MessageServiceLogClient } from "../../../clients/blockchain/linea/EthersL2MessageServiceLogClient";
import { MessageSentEventPoller } from "../../../services/pollers/MessageSentEventPoller";
import { IPoller } from "../../../core/services/pollers/IPoller";
import { MessageAnchoringPoller } from "../../../services/pollers/MessageAnchoringPoller";
import { MessageAnchoringProcessor } from "../../../services/processors/MessageAnchoringProcessor";
import {
MessageAnchoringProcessor,
MessageClaimingProcessor,
MessageClaimingPersister,
MessageSentEventProcessor,
L2ClaimMessageTransactionSizeProcessor,
} from "../../../services/processors";
import { PostmanOptions } from "./config/config";
import { DB } from "../persistence/dataSource";
import { Direction } from "../../../core/enums/MessageEnums";
import { MessageClaimingProcessor } from "../../../services/processors/MessageClaimingProcessor";
import { MessageClaimingPoller } from "../../../services/pollers/MessageClaimingPoller";
import { MessageClaimingPersister } from "../../../services/processors/MessageClaimingPersister";
import { MessagePersistingPoller } from "../../../services/pollers/MessagePersistingPoller";
import { MessageSentEventProcessor } from "../../../services/processors/MessageSentEventProcessor";
import { DatabaseCleaningPoller } from "../../../services/pollers/DatabaseCleaningPoller";
import { BaseError } from "../../../core/errors/Base";
import { LineaRollupMessageRetriever } from "../../../clients/blockchain/ethereum/LineaRollupMessageRetriever";
import { L2MessageServiceMessageRetriever } from "../../../clients/blockchain/linea/L2MessageServiceMessageRetriever";
import { MerkleTreeService } from "../../../clients/blockchain/ethereum/MerkleTreeService";
import { LineaMessageDBService } from "../../../services/persistence/LineaMessageDBService";
import { L2ChainQuerier } from "../../../clients/blockchain/linea/L2ChainQuerier";
import { EthereumMessageDBService } from "../../../services/persistence/EthereumMessageDBService";
import { L2ClaimMessageTransactionSizePoller } from "../../../services/pollers/L2ClaimMessageTransactionSizePoller";
import { L2ClaimMessageTransactionSizeProcessor } from "../../../services/processors/L2ClaimMessageTransactionSizeProcessor";
import {
MessageSentEventPoller,
MessageAnchoringPoller,
MessageClaimingPoller,
MessagePersistingPoller,
DatabaseCleaningPoller,
L2ClaimMessageTransactionSizePoller,
} from "../../../services/pollers";
import { DatabaseCleaner, LineaMessageDBService, EthereumMessageDBService } from "../../../services/persistence";
import { L2ClaimTransactionSizeCalculator } from "../../../services/L2ClaimTransactionSizeCalculator";
import { GasProvider } from "../../../clients/blockchain/gas/GasProvider";
import { LineaTransactionValidationService } from "../../../services/LineaTransactionValidationService";
import { EthereumTransactionValidationService } from "../../../services/EthereumTransactionValidationService";
import { getConfig } from "./config/utils";
@@ -72,85 +62,50 @@ export class PostmanServiceClient {
this.l1L2AutoClaimEnabled = config.l1L2AutoClaimEnabled;
this.l2L1AutoClaimEnabled = config.l2L1AutoClaimEnabled;
const l1Provider = new JsonRpcProvider(config.l1Config.rpcUrl);
const l2Provider = new JsonRpcProvider(config.l2Config.rpcUrl);
const l1Signer = this.getSigner(config.l1Config.claiming.signerPrivateKey, l1Provider);
const l2Signer = this.getSigner(config.l2Config.claiming.signerPrivateKey, l2Provider);
const l1Querier = new ChainQuerier(l1Provider, l1Signer);
const l2Querier = new L2ChainQuerier(l2Provider, l2Signer);
const lineaRollupLogClient = new EthersLineaRollupLogClient(
l1Provider,
config.l1Config.messageServiceContractAddress,
);
const l2MessageServiceLogClient = new EthersL2MessageServiceLogClient(
l2Provider,
config.l2Config.messageServiceContractAddress,
);
const l1GasProvider = new GasProvider(l1Querier, {
maxFeePerGas: config.l1Config.claiming.maxFeePerGas,
gasEstimationPercentile: config.l1Config.claiming.gasEstimationPercentile,
enforceMaxGasFee: config.l1Config.claiming.isMaxGasFeeEnforced,
enableLineaEstimateGas: false,
direction: Direction.L2_TO_L1,
const lineaSdk = new LineaSDK({
l1RpcUrlOrProvider: config.l1Config.rpcUrl,
l2RpcUrlOrProvider: config.l2Config.rpcUrl,
l1SignerPrivateKeyOrWallet: config.l1Config.claiming.signerPrivateKey,
l2SignerPrivateKeyOrWallet: config.l2Config.claiming.signerPrivateKey,
network: "custom",
mode: "read-write",
l1FeeEstimatorOptions: {
gasFeeEstimationPercentile: config.l1Config.claiming.gasEstimationPercentile,
maxFeePerGasCap: config.l1Config.claiming.maxFeePerGasCap,
enforceMaxGasFee: config.l1Config.claiming.isMaxGasFeeEnforced,
},
l2FeeEstimatorOptions: {
gasFeeEstimationPercentile: config.l2Config.claiming.gasEstimationPercentile,
maxFeePerGasCap: config.l2Config.claiming.maxFeePerGasCap,
enforceMaxGasFee: config.l2Config.claiming.isMaxGasFeeEnforced,
enableLineaEstimateGas: config.l2Config.enableLineaEstimateGas,
},
});
const l2GasProvider = new GasProvider(l2Querier, {
maxFeePerGas: config.l2Config.claiming.maxFeePerGas,
gasEstimationPercentile: config.l2Config.claiming.gasEstimationPercentile,
enforceMaxGasFee: config.l2Config.claiming.isMaxGasFeeEnforced,
enableLineaEstimateGas: config.l2Config.enableLineaEstimateGas,
direction: Direction.L1_TO_L2,
});
const l1Provider = lineaSdk.getL1Provider(config.l1Config.rpcUrl);
const l2Provider = lineaSdk.getL2Provider(config.l2Config.rpcUrl);
const lineaRollupMessageRetriever = new LineaRollupMessageRetriever(
l1Querier,
lineaRollupLogClient,
const l1Signer = lineaSdk.getL1Signer();
const l2Signer = lineaSdk.getL2Signer();
const lineaRollupClient = lineaSdk.getL1Contract(
config.l1Config.messageServiceContractAddress,
);
const l1MerkleTreeService = new MerkleTreeService(
l1Querier,
config.l1Config.messageServiceContractAddress,
lineaRollupLogClient,
l2MessageServiceLogClient,
config.l2Config.l2MessageTreeDepth,
);
const l2MessageServiceMessageRetriever = new L2MessageServiceMessageRetriever(
l2Querier,
l2MessageServiceLogClient,
config.l2Config.messageServiceContractAddress,
);
const l1MessageServiceContract = new LineaRollupClient(
l1Querier,
config.l1Config.messageServiceContractAddress,
lineaRollupLogClient,
l2MessageServiceLogClient,
l1GasProvider,
lineaRollupMessageRetriever,
l1MerkleTreeService,
"read-write",
l1Signer,
const l2MessageServiceClient = lineaSdk.getL2Contract(config.l2Config.messageServiceContractAddress);
const lineaRollupLogClient = lineaSdk.getL1ContractEventLogClient(config.l1Config.messageServiceContractAddress);
const l2MessageServiceLogClient = lineaSdk.getL2ContractEventLogClient(
config.l2Config.messageServiceContractAddress,
);
const l2MessageServiceContract = new L2MessageServiceClient(
l2Querier,
config.l2Config.messageServiceContractAddress,
l2MessageServiceMessageRetriever,
l2GasProvider,
"read-write",
l2Signer,
);
const l1GasProvider = lineaSdk.getL1GasProvider();
this.db = DB.create(config.databaseOptions);
const messageRepository = new TypeOrmMessageRepository(this.db);
const lineaMessageDBService = new LineaMessageDBService(l2Querier, messageRepository);
const lineaMessageDBService = new LineaMessageDBService(l2Provider, messageRepository);
const ethereumMessageDBService = new EthereumMessageDBService(l1GasProvider, messageRepository);
// L1 -> L2 flow
@@ -158,7 +113,7 @@ export class PostmanServiceClient {
const l1MessageSentEventProcessor = new MessageSentEventProcessor(
lineaMessageDBService,
lineaRollupLogClient,
l1Querier,
l1Provider,
{
direction: Direction.L1_TO_L2,
maxBlocksToFetchLogs: config.l1Config.listener.maxBlocksToFetchLogs,
@@ -171,7 +126,7 @@ export class PostmanServiceClient {
this.l1MessageSentEventPoller = new MessageSentEventPoller(
l1MessageSentEventProcessor,
l1Querier,
l1Provider,
lineaMessageDBService,
{
direction: Direction.L1_TO_L2,
@@ -183,8 +138,8 @@ export class PostmanServiceClient {
);
const l2MessageAnchoringProcessor = new MessageAnchoringProcessor(
l2MessageServiceContract,
l2Querier,
l2MessageServiceClient,
l2Provider,
lineaMessageDBService,
{
maxFetchMessagesFromDb: config.l1Config.listener.maxFetchMessagesFromDb,
@@ -207,13 +162,13 @@ export class PostmanServiceClient {
profitMargin: config.l2Config.claiming.profitMargin,
maxClaimGasLimit: BigInt(config.l2Config.claiming.maxClaimGasLimit),
},
l2Querier,
l2MessageServiceContract,
l2Provider,
l2MessageServiceClient,
);
const l2MessageClaimingProcessor = new MessageClaimingProcessor(
l2MessageServiceContract,
l2Querier,
l2MessageServiceClient,
l2Signer,
lineaMessageDBService,
l2TransactionValidationService,
{
@@ -240,8 +195,8 @@ export class PostmanServiceClient {
const l2MessageClaimingPersister = new MessageClaimingPersister(
lineaMessageDBService,
l2MessageServiceContract,
l2Querier,
l2MessageServiceClient,
l2Provider,
{
direction: Direction.L1_TO_L2,
messageSubmissionTimeout: config.l2Config.claiming.messageSubmissionTimeout,
@@ -259,10 +214,10 @@ export class PostmanServiceClient {
new WinstonLogger(`L2${MessagePersistingPoller.name}`, config.loggerOptions),
);
const transactionSizeCalculator = new L2ClaimTransactionSizeCalculator(l2MessageServiceContract);
const transactionSizeCalculator = new L2ClaimTransactionSizeCalculator(l2MessageServiceClient);
const transactionSizeCompressor = new L2ClaimMessageTransactionSizeProcessor(
lineaMessageDBService,
l2MessageServiceContract,
l2MessageServiceClient,
transactionSizeCalculator,
{
direction: Direction.L1_TO_L2,
@@ -283,7 +238,7 @@ export class PostmanServiceClient {
const l2MessageSentEventProcessor = new MessageSentEventProcessor(
ethereumMessageDBService,
l2MessageServiceLogClient,
l2Querier,
l2Provider,
{
direction: Direction.L2_TO_L1,
maxBlocksToFetchLogs: config.l2Config.listener.maxBlocksToFetchLogs,
@@ -296,7 +251,7 @@ export class PostmanServiceClient {
this.l2MessageSentEventPoller = new MessageSentEventPoller(
l2MessageSentEventProcessor,
l2Querier,
l2Provider,
ethereumMessageDBService,
{
direction: Direction.L2_TO_L1,
@@ -308,8 +263,8 @@ export class PostmanServiceClient {
);
const l1MessageAnchoringProcessor = new MessageAnchoringProcessor(
l1MessageServiceContract,
l1Querier,
lineaRollupClient,
l1Provider,
ethereumMessageDBService,
{
maxFetchMessagesFromDb: config.l1Config.listener.maxFetchMessagesFromDb,
@@ -327,18 +282,14 @@ export class PostmanServiceClient {
new WinstonLogger(`L1${MessageAnchoringPoller.name}`, config.loggerOptions),
);
const l1TransactionValidationService = new EthereumTransactionValidationService(
l1MessageServiceContract,
l1GasProvider,
{
profitMargin: config.l1Config.claiming.profitMargin,
maxClaimGasLimit: BigInt(config.l1Config.claiming.maxClaimGasLimit),
},
);
const l1TransactionValidationService = new EthereumTransactionValidationService(lineaRollupClient, l1GasProvider, {
profitMargin: config.l1Config.claiming.profitMargin,
maxClaimGasLimit: BigInt(config.l1Config.claiming.maxClaimGasLimit),
});
const l1MessageClaimingProcessor = new MessageClaimingProcessor(
l1MessageServiceContract,
l1Querier,
lineaRollupClient,
l1Signer,
ethereumMessageDBService,
l1TransactionValidationService,
{
@@ -365,8 +316,8 @@ export class PostmanServiceClient {
const l1MessageClaimingPersister = new MessageClaimingPersister(
ethereumMessageDBService,
l1MessageServiceContract,
l1Querier,
lineaRollupClient,
l1Provider,
{
direction: Direction.L2_TO_L1,
messageSubmissionTimeout: config.l1Config.claiming.messageSubmissionTimeout,
@@ -401,23 +352,6 @@ export class PostmanServiceClient {
);
}
/**
* Creates a Wallet instance as a signer using the provided private key and JSON RPC provider.
*
* @param {string} privateKey - The private key to use for the signer.
* @param {JsonRpcProvider} provider - The JSON RPC provider associated with the network.
* @returns {Wallet} A Wallet instance configured with the provided private key and provider.
*/
private getSigner(privateKey: string, provider: JsonRpcProvider): Wallet {
try {
return new Wallet(privateKey, provider);
} catch (e) {
throw new BaseError(
"Something went wrong when trying to generate Wallet. Please check your private key and the provider url.",
);
}
}
/**
* Initializes the database connection using the configuration provided.
*/

View File

@@ -54,7 +54,7 @@ const postmanServiceClientOptions: PostmanOptions = {
signerPrivateKey: TEST_L1_SIGNER_PRIVATE_KEY,
messageSubmissionTimeout: 300000,
maxNonceDiff: 10000,
maxFeePerGas: 100000000000n,
maxFeePerGasCap: 100000000000n,
gasEstimationPercentile: 50,
profitMargin: 1.0,
maxNumberOfRetries: 100,
@@ -75,7 +75,7 @@ const postmanServiceClientOptions: PostmanOptions = {
signerPrivateKey: TEST_L2_SIGNER_PRIVATE_KEY,
messageSubmissionTimeout: 300000,
maxNonceDiff: 10000,
maxFeePerGas: 100000000000n,
maxFeePerGasCap: 100000000000n,
gasEstimationPercentile: 50,
profitMargin: 1.0,
maxNumberOfRetries: 100,
@@ -119,15 +119,20 @@ describe("PostmanServiceClient", () => {
...postmanServiceClientOptions.l1Options,
claiming: {
...postmanServiceClientOptions.l1Options.claiming,
signerPrivateKey: "",
signerPrivateKey: "0x",
},
},
l2Options: {
...postmanServiceClientOptions.l2Options,
claiming: {
...postmanServiceClientOptions.l2Options.claiming,
signerPrivateKey: "0x",
},
},
};
expect(() => new PostmanServiceClient(postmanServiceClientOptionsWithInvalidPrivateKey)).toThrow(
new Error(
"Something went wrong when trying to generate Wallet. Please check your private key and the provider url.",
),
new Error("Something went wrong when trying to generate Wallet. Please check your private key."),
);
});
});

View File

@@ -23,7 +23,7 @@ import {
DEFAULT_LISTENER_INTERVAL,
DEFAULT_MAX_BLOCKS_TO_FETCH_LOGS,
DEFAULT_MAX_CLAIM_GAS_LIMIT,
DEFAULT_MAX_FEE_PER_GAS,
DEFAULT_MAX_FEE_PER_GAS_CAP,
DEFAULT_MAX_FETCH_MESSAGES_FROM_DB,
DEFAULT_MAX_NONCE_DIFF,
DEFAULT_MAX_NUMBER_OF_RETRIES,
@@ -74,7 +74,7 @@ describe("Config utils", () => {
gasEstimationPercentile: DEFAULT_GAS_ESTIMATION_PERCENTILE,
isMaxGasFeeEnforced: DEFAULT_ENFORCE_MAX_GAS_FEE,
maxClaimGasLimit: DEFAULT_MAX_CLAIM_GAS_LIMIT,
maxFeePerGas: DEFAULT_MAX_FEE_PER_GAS,
maxFeePerGasCap: DEFAULT_MAX_FEE_PER_GAS_CAP,
maxNonceDiff: DEFAULT_MAX_NONCE_DIFF,
maxNumberOfRetries: DEFAULT_MAX_NUMBER_OF_RETRIES,
maxTxRetries: DEFAULT_MAX_TX_RETRIES,
@@ -102,7 +102,7 @@ describe("Config utils", () => {
gasEstimationPercentile: DEFAULT_GAS_ESTIMATION_PERCENTILE,
isMaxGasFeeEnforced: DEFAULT_ENFORCE_MAX_GAS_FEE,
maxClaimGasLimit: DEFAULT_MAX_CLAIM_GAS_LIMIT,
maxFeePerGas: DEFAULT_MAX_FEE_PER_GAS,
maxFeePerGasCap: DEFAULT_MAX_FEE_PER_GAS_CAP,
maxNonceDiff: DEFAULT_MAX_NONCE_DIFF,
maxNumberOfRetries: DEFAULT_MAX_NUMBER_OF_RETRIES,
maxTxRetries: DEFAULT_MAX_TX_RETRIES,
@@ -179,7 +179,7 @@ describe("Config utils", () => {
gasEstimationPercentile: DEFAULT_GAS_ESTIMATION_PERCENTILE,
isMaxGasFeeEnforced: DEFAULT_ENFORCE_MAX_GAS_FEE,
maxClaimGasLimit: DEFAULT_MAX_CLAIM_GAS_LIMIT,
maxFeePerGas: DEFAULT_MAX_FEE_PER_GAS,
maxFeePerGasCap: DEFAULT_MAX_FEE_PER_GAS_CAP,
maxNonceDiff: DEFAULT_MAX_NONCE_DIFF,
maxNumberOfRetries: DEFAULT_MAX_NUMBER_OF_RETRIES,
maxTxRetries: DEFAULT_MAX_TX_RETRIES,
@@ -207,7 +207,7 @@ describe("Config utils", () => {
gasEstimationPercentile: DEFAULT_GAS_ESTIMATION_PERCENTILE,
isMaxGasFeeEnforced: DEFAULT_ENFORCE_MAX_GAS_FEE,
maxClaimGasLimit: DEFAULT_MAX_CLAIM_GAS_LIMIT,
maxFeePerGas: DEFAULT_MAX_FEE_PER_GAS,
maxFeePerGasCap: DEFAULT_MAX_FEE_PER_GAS_CAP,
maxNonceDiff: DEFAULT_MAX_NONCE_DIFF,
maxNumberOfRetries: DEFAULT_MAX_NUMBER_OF_RETRIES,
maxTxRetries: DEFAULT_MAX_TX_RETRIES,

View File

@@ -76,7 +76,7 @@ export type ClaimingOptions = {
messageSubmissionTimeout?: number;
feeRecipientAddress?: string;
maxNonceDiff?: number;
maxFeePerGas?: bigint;
maxFeePerGasCap?: bigint;
gasEstimationPercentile?: number;
isMaxGasFeeEnforced?: boolean;
profitMargin?: number;

View File

@@ -8,7 +8,7 @@ import {
DEFAULT_LISTENER_INTERVAL,
DEFAULT_MAX_BLOCKS_TO_FETCH_LOGS,
DEFAULT_MAX_CLAIM_GAS_LIMIT,
DEFAULT_MAX_FEE_PER_GAS,
DEFAULT_MAX_FEE_PER_GAS_CAP,
DEFAULT_MAX_FETCH_MESSAGES_FROM_DB,
DEFAULT_MAX_NONCE_DIFF,
DEFAULT_MAX_NUMBER_OF_RETRIES,
@@ -54,7 +54,7 @@ export function getConfig(postmanOptions: PostmanOptions): PostmanConfig {
messageSubmissionTimeout: l1Options.claiming.messageSubmissionTimeout ?? DEFAULT_MESSAGE_SUBMISSION_TIMEOUT,
feeRecipientAddress: l1Options.claiming.feeRecipientAddress,
maxNonceDiff: l1Options.claiming.maxNonceDiff ?? DEFAULT_MAX_NONCE_DIFF,
maxFeePerGas: l1Options.claiming.maxFeePerGas ?? DEFAULT_MAX_FEE_PER_GAS,
maxFeePerGasCap: l1Options.claiming.maxFeePerGasCap ?? DEFAULT_MAX_FEE_PER_GAS_CAP,
gasEstimationPercentile: l1Options.claiming.gasEstimationPercentile ?? DEFAULT_GAS_ESTIMATION_PERCENTILE,
isMaxGasFeeEnforced: l1Options.claiming.isMaxGasFeeEnforced ?? false,
profitMargin: l1Options.claiming.profitMargin ?? DEFAULT_PROFIT_MARGIN,
@@ -83,7 +83,7 @@ export function getConfig(postmanOptions: PostmanOptions): PostmanConfig {
messageSubmissionTimeout: l2Options.claiming.messageSubmissionTimeout ?? DEFAULT_MESSAGE_SUBMISSION_TIMEOUT,
feeRecipientAddress: l2Options.claiming.feeRecipientAddress,
maxNonceDiff: l2Options.claiming.maxNonceDiff ?? DEFAULT_MAX_NONCE_DIFF,
maxFeePerGas: l2Options.claiming.maxFeePerGas ?? DEFAULT_MAX_FEE_PER_GAS,
maxFeePerGasCap: l2Options.claiming.maxFeePerGasCap ?? DEFAULT_MAX_FEE_PER_GAS_CAP,
gasEstimationPercentile: l2Options.claiming.gasEstimationPercentile ?? DEFAULT_GAS_ESTIMATION_PERCENTILE,
isMaxGasFeeEnforced: l2Options.claiming.isMaxGasFeeEnforced ?? false,
profitMargin: l2Options.claiming.profitMargin ?? DEFAULT_PROFIT_MARGIN,

View File

@@ -1,6 +1,7 @@
import { IsDate, IsDecimal, IsEnum, IsNumber, IsString } from "class-validator";
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm";
import { Direction, MessageStatus } from "../../../../core/enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
import { MessageStatus } from "../../../../core/enums";
@Entity({ name: "message" })
export class MessageEntity {

View File

@@ -1,4 +1,5 @@
import { describe, it, expect } from "@jest/globals";
import { Direction } from "@consensys/linea-sdk";
import { generateMessage, generateMessageEntity } from "../../../../../utils/testing/helpers";
import { mapMessageEntityToMessage, mapMessageToMessageEntity } from "../messageMappers";
import {
@@ -7,7 +8,7 @@ import {
TEST_CONTRACT_ADDRESS_2,
TEST_MESSAGE_HASH,
} from "../../../../../utils/testing/constants";
import { Direction, MessageStatus } from "../../../../../core/enums/MessageEnums";
import { MessageStatus } from "../../../../../core/enums";
import { Message } from "../../../../../core/entities/Message";
describe("Message Mappers", () => {

View File

@@ -1,13 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Brackets, DataSource, Repository } from "typeorm";
import { ContractTransactionResponse } from "ethers";
import { Direction } from "@consensys/linea-sdk";
import { Message } from "../../../../core/entities/Message";
import { mapMessageEntityToMessage, mapMessageToMessageEntity } from "../mappers/messageMappers";
import { Direction, MessageStatus } from "../../../../core/enums/MessageEnums";
import { DatabaseErrorType, DatabaseRepoName } from "../../../../core/enums/DatabaseEnums";
import { DatabaseAccessError } from "../../../../core/errors/DatabaseErrors";
import { DatabaseErrorType, DatabaseRepoName, MessageStatus } from "../../../../core/enums";
import { DatabaseAccessError } from "../../../../core/errors";
import { MessageEntity } from "../entities/Message.entity";
import { subtractSeconds } from "../../../../core/utils/shared";
import { ContractTransactionResponse } from "ethers";
import { IMessageRepository } from "../../../../core/persistence/IMessageRepository";
export class TypeOrmMessageRepository<TransactionResponse extends ContractTransactionResponse>

View File

@@ -1,4 +1,4 @@
import { Direction } from "../../enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
export type GasFees = {
maxFeePerGas: bigint;
@@ -23,7 +23,7 @@ export type LineaEstimateGasResponse = {
};
type BaseGasProviderConfig = {
maxFeePerGas: bigint;
maxFeePerGasCap: bigint;
enforceMaxGasFee: boolean;
};

View File

@@ -1,16 +1,16 @@
import { GasFees } from "./IGasProvider";
export interface IChainQuerier<TransactionReceipt, Block, TransactionRequest, TransactionResponse, JsonRpcProvider> {
getCurrentNonce(accountAddress?: string): Promise<number>;
getCurrentBlockNumber(): Promise<number>;
export interface IProvider<TransactionReceipt, Block, TransactionRequest, TransactionResponse, Provider> {
getTransactionCount(address: string, blockTag: string | number | bigint): Promise<number>;
getBlockNumber(): Promise<number>;
getTransactionReceipt(txHash: string): Promise<TransactionReceipt | null>;
getBlock(blockNumber: number | bigint | string): Promise<Block | null>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendRequest(methodName: string, params: any[]): Promise<any>;
send(methodName: string, params: Array<any> | Record<string, any>): Promise<any>;
estimateGas(transactionRequest: TransactionRequest): Promise<bigint>;
getProvider(): JsonRpcProvider;
getTransaction(transactionHash: string): Promise<TransactionResponse | null>;
broadcastTransaction(signedTx: string): Promise<TransactionResponse>;
ethCall(transactionRequest: TransactionRequest): Promise<string>;
call(transactionRequest: TransactionRequest): Promise<string>;
getFees(): Promise<GasFees>;
get provider(): Provider;
}

View File

@@ -1,11 +1,21 @@
import { MessageProps } from "../../../entities/Message";
import { OnChainMessageStatus } from "../../../enums/MessageEnums";
import { MessageSent, OnChainMessageStatus } from "@consensys/linea-sdk";
import { IMessageServiceContract } from "../../../services/contracts/IMessageServiceContract";
import { MessageSent } from "../../../types/Events";
import { FinalizationMessagingInfo, Proof } from "./IMerkleTreeService";
export interface ILineaRollupClient<Overrides, TransactionReceipt, TransactionResponse, ContractTransactionResponse>
extends IMessageServiceContract<Overrides, TransactionReceipt, TransactionResponse, ContractTransactionResponse> {
export interface ILineaRollupClient<
Overrides,
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
ErrorDescription,
> extends IMessageServiceContract<
Overrides,
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
ErrorDescription
> {
getFinalizationMessagingInfo(transactionHash: string): Promise<FinalizationMessagingInfo>;
getL2MessageHashesInBlockRange(fromBlock: number, toBlock: number): Promise<string[]>;
getMessageSiblings(messageHash: string, messageHashes: string[], treeDepth: number): string[];

View File

@@ -1,4 +1,4 @@
import { L2MessagingBlockAnchored, MessageClaimed, MessageSent } from "../../../types/Events";
import { L2MessagingBlockAnchored, MessageClaimed, MessageSent } from "@consensys/linea-sdk";
export type MessageSentEventFilters = {
from?: string;

View File

@@ -1,5 +1,5 @@
import { MessageSent } from "sdk/src/core/types/Events";
import { MessageProps } from "../../../../core/entities/Message";
import { MessageSent } from "@consensys/linea-sdk";
import { MessageProps } from "../../../entities/Message";
import { IMessageServiceContract } from "../../../services/contracts/IMessageServiceContract";
import { LineaGasFees } from "../IGasProvider";
@@ -9,7 +9,14 @@ export interface IL2MessageServiceClient<
TransactionResponse,
ContractTransactionResponse,
Signer,
> extends IMessageServiceContract<Overrides, TransactionReceipt, TransactionResponse, ContractTransactionResponse> {
ErrorDescription,
> extends IMessageServiceContract<
Overrides,
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
ErrorDescription
> {
encodeClaimMessageTransactionData(message: MessageProps & { feeRecipient?: string }): string;
estimateClaimGasFees(
message: (MessageSent | MessageProps) & { feeRecipient?: string },

View File

@@ -1,4 +1,4 @@
import { MessageSent, ServiceVersionMigrated } from "../../../types/Events";
import { MessageSent, ServiceVersionMigrated } from "@consensys/linea-sdk";
export type MessageSentEventFilters = {
from?: string;

View File

@@ -0,0 +1,13 @@
import { IProvider } from "../IProvider";
export type BlockExtraData = {
version: number;
fixedCost: number;
variableCost: number;
ethGasPrice: number;
};
export interface ILineaProvider<TransactionReceipt, Block, TransactionRequest, TransactionResponse, Provider>
extends IProvider<TransactionReceipt, Block, TransactionRequest, TransactionResponse, Provider> {
getBlockExtraData(blockNumber: number | bigint | string): Promise<BlockExtraData | null>;
}

View File

@@ -0,0 +1,4 @@
export const ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
export const MINIMUM_MARGIN = 1.2;

View File

@@ -0,0 +1,25 @@
export const DEFAULT_MESSAGE_SUBMISSION_TIMEOUT = 300000;
export const DEFAULT_LISTENER_INTERVAL = 4000;
export const DEFAULT_DB_CLEANER_ENABLED = false;
export const DEFAULT_DB_CLEANING_INTERVAL = 43200000;
export const DEFAULT_DB_DAYS_BEFORE_NOW_TO_DELETE = 14;
export const DEFAULT_MAX_BLOCKS_TO_FETCH_LOGS = 1000;
export const DEFAULT_MAX_FETCH_MESSAGES_FROM_DB = 1000;
export const DEFAULT_MAX_NONCE_DIFF = 10000;
export const DEFAULT_MAX_FEE_PER_GAS_CAP = 100000000000n;
export const DEFAULT_ENFORCE_MAX_GAS_FEE = false;
export const DEFAULT_GAS_ESTIMATION_PERCENTILE = 20;
export const DEFAULT_GAS_LIMIT = 0;
export const DEFAULT_LISTENER_BLOCK_CONFIRMATIONS = 4;
export const DEFAULT_PROFIT_MARGIN = 1.0;
export const DEFAULT_MAX_NUMBER_OF_RETRIES = 100;
export const DEFAULT_RETRY_DELAY_IN_SECONDS = 30;
export const DEFAULT_EOA_ENABLED = false;
export const DEFAULT_CALLDATA_ENABLED = false;
export const DEFAULT_RATE_LIMIT_MARGIN = 0.95;
export const DEFAULT_MAX_CLAIM_GAS_LIMIT = 100_000n;
export const DEFAULT_MAX_TX_RETRIES = 20;
export const DEFAULT_L2_MESSAGE_TREE_DEPTH = 5;
export const DEFAULT_INITIAL_FROM_BLOCK = -1;
export const PROFIT_MARGIN_MULTIPLIER = 100;

View File

@@ -0,0 +1,2 @@
export * from "./common";
export * from "./blockchain";

View File

@@ -1,4 +1,5 @@
import { Direction, MessageStatus } from "../enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
import { MessageStatus } from "../enums";
export type MessageProps = {
id?: number;

View File

@@ -1,8 +1,3 @@
export enum Direction {
L1_TO_L2 = "L1_TO_L2",
L2_TO_L1 = "L2_TO_L1",
}
export enum MessageStatus {
SENT = "SENT",
TRANSACTION_SIZE_COMPUTED = "TRANSACTION_SIZE_COMPUTED",
@@ -15,9 +10,3 @@ export enum MessageStatus {
FEE_UNDERPRICED = "FEE_UNDERPRICED",
EXCLUDED = "EXCLUDED",
}
export enum OnChainMessageStatus {
UNKNOWN = "UNKNOWN",
CLAIMABLE = "CLAIMABLE",
CLAIMED = "CLAIMED",
}

View File

@@ -0,0 +1,2 @@
export { DatabaseErrorType, DatabaseRepoName } from "./DatabaseEnums";
export { MessageStatus } from "./MessageEnums";

View File

@@ -0,0 +1,11 @@
export class BaseError extends Error {
reason?: BaseError | Error | string;
override name = "PostmanCoreError";
constructor(message?: string) {
super();
this.message = message || "An error occurred.";
Error.captureStackTrace(this, this.constructor);
}
}

View File

@@ -1,6 +1,6 @@
import { BaseError } from "./BaseError";
import { MessageProps } from "../entities/Message";
import { DatabaseErrorType, DatabaseRepoName } from "../enums/DatabaseEnums";
import { BaseError } from "./Base";
import { DatabaseErrorType, DatabaseRepoName } from "../enums";
export class DatabaseAccessError<T extends MessageProps> extends BaseError {
override name = DatabaseAccessError.name;

View File

@@ -0,0 +1,14 @@
import { describe, it } from "@jest/globals";
import { serialize } from "@consensys/linea-sdk";
import { BaseError } from "../BaseError";
describe("BaseError", () => {
it("Should log error message when we only pass a short message", () => {
expect(serialize(new BaseError("An error message."))).toStrictEqual(
serialize({
name: "PostmanCoreError",
message: "An error message.",
}),
);
});
});

View File

@@ -1,9 +1,8 @@
import { describe, it } from "@jest/globals";
import { describe, it, expect } from "@jest/globals";
import { Direction, serialize } from "@consensys/linea-sdk";
import { DatabaseAccessError } from "../DatabaseErrors";
import { DatabaseErrorType, DatabaseRepoName } from "../../enums/DatabaseEnums";
import { DatabaseErrorType, DatabaseRepoName, MessageStatus } from "../../enums";
import { MessageProps } from "../../entities/Message";
import { Direction, MessageStatus } from "../../enums/MessageEnums";
import { serialize } from "../../utils/serialize";
import { ZERO_ADDRESS, ZERO_HASH } from "../../constants";
describe("DatabaseAccessError", () => {

View File

@@ -0,0 +1,2 @@
export { BaseError } from "./BaseError";
export { DatabaseAccessError } from "./DatabaseErrors";

View File

@@ -1,5 +1,6 @@
import { Direction } from "@consensys/linea-sdk";
import { Message } from "../entities/Message";
import { Direction, MessageStatus } from "../enums/MessageEnums";
import { MessageStatus } from "../enums";
export interface IMessageDBService<TransactionResponse> {
insertMessage(message: Message): Promise<void>;

View File

@@ -1,5 +1,6 @@
import { Direction } from "@consensys/linea-sdk";
import { Message } from "../entities/Message";
import { Direction, MessageStatus } from "../enums/MessageEnums";
import { MessageStatus } from "../enums";
export interface IMessageRepository<ContractTransactionResponse> {
insertMessage(message: Message): Promise<void>;

View File

@@ -1,12 +1,12 @@
import { OnChainMessageStatus } from "../../../core/enums/MessageEnums";
import { OnChainMessageStatus, MessageSent } from "@consensys/linea-sdk";
import { MessageProps } from "../../entities/Message";
import { MessageSent } from "../../types/Events";
export interface IMessageServiceContract<
Overrides,
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
ErrorDescription,
> {
getMessageStatus(messageHash: string, overrides?: Overrides): Promise<OnChainMessageStatus>;
getMessageByMessageHash(messageHash: string): Promise<MessageSent | null>;
@@ -19,4 +19,5 @@ export interface IMessageServiceContract<
retryTransactionWithHigherFee(transactionHash: string, priceBumpPercent?: number): Promise<TransactionResponse>;
isRateLimitExceeded(messageFee: bigint, messageValue: bigint): Promise<boolean>;
isRateLimitExceededError(transactionHash: string): Promise<boolean>;
parseTransactionError(transactionHash: string): Promise<ErrorDescription | string>;
}

View File

@@ -1,4 +1,4 @@
import { Direction } from "../../enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
export interface IL2ClaimMessageTransactionSizeProcessor {
process(): Promise<void>;

View File

@@ -1,4 +1,4 @@
import { Direction } from "../../enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
export interface IMessageClaimingPersister {
process(): Promise<void>;

View File

@@ -1,4 +1,4 @@
import { Direction } from "../../enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
export interface IMessageClaimingProcessor {
process(): Promise<void>;

View File

@@ -1,4 +1,4 @@
import { Direction } from "../../enums/MessageEnums";
import { Direction } from "@consensys/linea-sdk";
export interface IMessageSentEventProcessor {
process(

View File

@@ -0,0 +1,11 @@
import { describe, it, expect } from "@jest/globals";
import { subtractSeconds } from "../shared";
describe("Shared utils", () => {
describe("subtractSeconds", () => {
it("should substract X seconds to the current date", () => {
const currentDate = new Date("2024-04-08T00:12:10.000Z");
expect(subtractSeconds(currentDate, 10)).toStrictEqual(new Date("2024-04-08T00:12:00.000Z"));
});
});
});

View File

@@ -0,0 +1,12 @@
/**
* Subtracts a specified number of seconds from a given date.
*
* @param {Date} date - The original date.
* @param {number} seconds - The number of seconds to subtract from the date.
* @returns {Date} A new date object representing the time after subtracting the specified seconds.
*/
export const subtractSeconds = (date: Date, seconds: number): Date => {
const dateCopy = new Date(date);
dateCopy.setSeconds(date.getSeconds() - seconds);
return dateCopy;
};

View File

@@ -1,5 +1,6 @@
import {
ContractTransactionResponse,
ErrorDescription,
Overrides,
TransactionReceipt,
TransactionRequest,
@@ -27,7 +28,8 @@ export class EthereumTransactionValidationService implements ITransactionValidat
Overrides,
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse
ContractTransactionResponse,
ErrorDescription
>,
private readonly gasProvider: IEthereumGasProvider<TransactionRequest>,
private readonly config: TransactionValidationServiceConfig,

View File

@@ -1,5 +1,6 @@
import {
ContractTransactionResponse,
ErrorDescription,
ethers,
Overrides,
Signer,
@@ -8,11 +9,11 @@ import {
TransactionResponse,
} from "ethers";
import { GoNativeCompressor } from "@consensys/linea-native-libs";
import { serialize } from "@consensys/linea-sdk";
import { BaseError } from "../core/errors";
import { MessageProps } from "../core/entities/Message";
import { IL2MessageServiceClient } from "../core/clients/blockchain/linea/IL2MessageServiceClient";
import { BaseError } from "../core/errors/Base";
import { LineaGasFees } from "../core/clients/blockchain/IGasProvider";
import { serialize } from "../core/utils/serialize";
import { IL2ClaimTransactionSizeCalculator } from "../core/services/processors/IL2ClaimTransactionSizeCalculator";
export class L2ClaimTransactionSizeCalculator implements IL2ClaimTransactionSizeCalculator {
@@ -29,7 +30,8 @@ export class L2ClaimTransactionSizeCalculator implements IL2ClaimTransactionSize
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
Signer
Signer,
ErrorDescription
>,
) {
this.compressor = new GoNativeCompressor(800_000);

View File

@@ -1,6 +1,7 @@
import {
Block,
ContractTransactionResponse,
ErrorDescription,
JsonRpcProvider,
Overrides,
Signer,
@@ -8,6 +9,7 @@ import {
TransactionRequest,
TransactionResponse,
} from "ethers";
import { BaseError } from "../core/errors";
import { Message } from "../core/entities/Message";
import {
ITransactionValidationService,
@@ -15,20 +17,19 @@ import {
} from "../core/services/ITransactionValidationService";
import { MINIMUM_MARGIN, PROFIT_MARGIN_MULTIPLIER } from "../core/constants";
import { IL2MessageServiceClient } from "../core/clients/blockchain/linea/IL2MessageServiceClient";
import { IL2ChainQuerier } from "../core/clients/blockchain/linea/IL2ChainQuerier";
import { BaseError } from "../core/errors/Base";
import { ILineaProvider } from "../core/clients/blockchain/linea/ILineaProvider";
export class LineaTransactionValidationService implements ITransactionValidationService {
/**
* Constructs a new instance of the `LineaTransactionValidationService`.
*
* @param {TransactionValidationServiceConfig} config - Configuration settings for the transaction validation service, including profit margin and maximum gas limit.
* @param {IL2ChainQuerier} chainQuerier - An instance of a class implementing the `IL2ChainQuerier` interface, used to interact with the blockchain.
* @param {ILineaProvider} provider - An instance of a class implementing the `ILineaProvider` interface, used to interact with the blockchain.
* @param {IL2MessageServiceClient} l2MessageServiceClient - An instance of a class implementing the `IL2MessageServiceClient` interface, used to interact with the L2 message service.
*/
constructor(
private readonly config: TransactionValidationServiceConfig,
private readonly chainQuerier: IL2ChainQuerier<
private readonly provider: ILineaProvider<
TransactionReceipt,
Block,
TransactionRequest,
@@ -40,7 +41,8 @@ export class LineaTransactionValidationService implements ITransactionValidation
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
Signer
Signer,
ErrorDescription
>,
) {}
@@ -116,7 +118,7 @@ export class LineaTransactionValidationService implements ITransactionValidation
messageFee: bigint,
messageCompressedTransactionSize: number,
): Promise<boolean> {
const extraData = await this.chainQuerier.getBlockExtraData("latest");
const extraData = await this.provider.getBlockExtraData("latest");
if (!extraData) {
throw new BaseError("No extra data.");

View File

@@ -7,7 +7,7 @@ export class DatabaseCleaner implements IDatabaseCleaner {
/**
* Constructs a new instance of the `DatabaseCleaner`.
*
* @param {IMessageRepository<unknown>} messageRepository - An instance of a message repository that provides access to message storage and operations.
* @param {IMessageDBService<ContractTransactionResponse>} databaseService - An instance of a MessageDBService that provides access to message storage and operations.
* @param {ILogger} logger - An instance of a logger for logging information and errors during the cleanup process.
*/
constructor(

View File

@@ -1,6 +1,7 @@
import { ContractTransactionResponse, TransactionRequest } from "ethers";
import { Direction } from "@consensys/linea-sdk";
import { Message } from "../../core/entities/Message";
import { Direction, MessageStatus } from "../../core/enums/MessageEnums";
import { MessageStatus } from "../../core/enums";
import { IMessageRepository } from "../../core/persistence/IMessageRepository";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
import { IGasProvider } from "../../core/clients/blockchain/IGasProvider";

View File

@@ -6,11 +6,12 @@ import {
TransactionRequest,
TransactionResponse,
} from "ethers";
import { Direction } from "@consensys/linea-sdk";
import { Message } from "../../core/entities/Message";
import { Direction, MessageStatus } from "../../core/enums/MessageEnums";
import { MessageStatus } from "../../core/enums";
import { IMessageRepository } from "../../core/persistence/IMessageRepository";
import { IL2ChainQuerier } from "../../core/clients/blockchain/linea/IL2ChainQuerier";
import { BaseError } from "../../core/errors/Base";
import { ILineaProvider } from "../../core/clients/blockchain/linea/ILineaProvider";
import { BaseError } from "../../core/errors";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
import { MessageDBService } from "./MessageDBService";
import { MINIMUM_MARGIN } from "../../core/constants";
@@ -19,11 +20,11 @@ export class LineaMessageDBService extends MessageDBService implements IMessageD
/**
* Creates an instance of `LineaMessageDBService`.
*
* @param {IL2ChainQuerier} chainQuerier - The chain querier for interacting with the blockchain.
* @param {ILineaProvider} provider - The provider for interacting with the blockchain.
* @param {IMessageRepository} messageRepository - The message repository for interacting with the message database.
*/
constructor(
private readonly chainQuerier: IL2ChainQuerier<
private readonly provider: ILineaProvider<
TransactionReceipt,
Block,
TransactionRequest,
@@ -90,8 +91,8 @@ export class LineaMessageDBService extends MessageDBService implements IMessageD
extraDataFixedCost: number;
}> {
const minimumMargin = MINIMUM_MARGIN;
const blockNumber = await this.chainQuerier.getCurrentBlockNumber();
const extraData = await this.chainQuerier.getBlockExtraData(blockNumber);
const blockNumber = await this.provider.getBlockNumber();
const extraData = await this.provider.getBlockExtraData(blockNumber);
if (!extraData) {
throw new BaseError("no extra data.");

View File

@@ -1,7 +1,8 @@
import { ContractTransactionResponse } from "ethers";
import { Direction } from "@consensys/linea-sdk";
import { Message } from "../../core/entities/Message";
import { IMessageRepository } from "../../core/persistence/IMessageRepository";
import { Direction, MessageStatus } from "../../core/enums/MessageEnums";
import { MessageStatus } from "../../core/enums";
export abstract class MessageDBService {
/**

View File

@@ -4,7 +4,7 @@ import { ContractTransactionResponse } from "ethers";
import { DatabaseCleaner } from "../DatabaseCleaner";
import { ILogger } from "../../../core/utils/logging/ILogger";
import { DatabaseAccessError } from "../../../core/errors/DatabaseErrors";
import { DatabaseErrorType, DatabaseRepoName } from "../../../core/enums/DatabaseEnums";
import { DatabaseErrorType, DatabaseRepoName } from "../../../core/enums";
import { IMessageDBService } from "../../../core/persistence/IMessageDBService";
describe("TestDatabaseCleaner", () => {

View File

@@ -0,0 +1,4 @@
export { DatabaseCleaner } from "./DatabaseCleaner";
export { EthereumMessageDBService } from "./EthereumMessageDBService";
export { LineaMessageDBService } from "./LineaMessageDBService";
export { MessageDBService } from "./MessageDBService";

View File

@@ -1,8 +1,8 @@
import { wait } from "@consensys/linea-sdk";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IPoller } from "../../core/services/pollers/IPoller";
import { IDatabaseCleaner } from "../../core/persistence/IDatabaseCleaner";
import { DBCleanerConfig } from "../../application/postman/persistence/config/types";
import { wait } from "../../core/utils/shared";
export class DatabaseCleaningPoller implements IPoller {
private isPolling = false;

View File

@@ -1,7 +1,6 @@
import { Direction } from "../../core/enums/MessageEnums";
import { Direction, wait } from "@consensys/linea-sdk";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IPoller } from "../../core/services/pollers/IPoller";
import { wait } from "../../core/utils/shared";
import { L2ClaimMessageTransactionSizeProcessor } from "../processors/L2ClaimMessageTransactionSizeProcessor";
type L2ClaimMessageTransactionSizePollerConfig = {

View File

@@ -1,8 +1,7 @@
import { Direction } from "../../core/enums/MessageEnums";
import { Direction, wait } from "@consensys/linea-sdk";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IPoller } from "../../core/services/pollers/IPoller";
import { IMessageAnchoringProcessor } from "../../core/services/processors/IMessageAnchoringProcessor";
import { wait } from "../../core/utils/shared";
type MessageAnchoringPollerConfig = {
direction: Direction;

View File

@@ -1,8 +1,7 @@
import { Direction } from "../../core/enums/MessageEnums";
import { Direction, wait } from "@consensys/linea-sdk";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IPoller } from "../../core/services/pollers/IPoller";
import { IMessageClaimingProcessor } from "../../core/services/processors/IMessageClaimingProcessor";
import { wait } from "../../core/utils/shared";
type MessageClaimingPollerConfig = {
direction: Direction;

View File

@@ -1,7 +1,6 @@
import { Direction } from "../../core/enums/MessageEnums";
import { Direction, wait } from "@consensys/linea-sdk";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IPoller } from "../../core/services/pollers/IPoller";
import { wait } from "../../core/utils/shared";
import { IMessageClaimingPersister } from "../../core/services/processors/IMessageClaimingPersister";
type MessagePersistingPollerConfig = {

View File

@@ -6,15 +6,14 @@ import {
TransactionRequest,
TransactionResponse,
} from "ethers";
import { Direction } from "../../core/enums/MessageEnums";
import { Direction, wait } from "@consensys/linea-sdk";
import { ILogger } from "../../core/utils/logging/ILogger";
import { DEFAULT_INITIAL_FROM_BLOCK } from "../../core/constants";
import { IMessageSentEventProcessor } from "../../core/services/processors/IMessageSentEventProcessor";
import { Message } from "../../core/entities/Message";
import { DatabaseAccessError } from "../../core/errors/DatabaseErrors";
import { IChainQuerier } from "../../core/clients/blockchain/IChainQuerier";
import { IProvider } from "../../core/clients/blockchain/IProvider";
import { IPoller } from "../../core/services/pollers/IPoller";
import { wait } from "../../core/utils/shared";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
type MessageSentEventPollerConfig = {
@@ -31,14 +30,14 @@ export class MessageSentEventPoller implements IPoller {
* Constructs a new instance of the `MessageSentEventPoller`.
*
* @param {IMessageSentEventProcessor} eventProcessor - An instance of a class implementing the `IMessageSentEventProcessor` interface, responsible for processing message sent events.
* @param {IChainQuerier} chainQuerier - An instance of a class implementing the `IChainQuerier` interface, used to query blockchain data.
* @param {IProvider} provider - An instance of a class implementing the `IProvider` interface, used to query blockchain data.
* @param {IMessageDBService} databaseService - An instance of a class implementing the `IMessageDBService` interface, used for storing and retrieving message data.
* @param {MessageSentEventPollerConfig} config - Configuration settings for the poller, including the direction of message flow, the polling interval, and the initial block number to start listening from.
* @param {ILogger} logger - An instance of a class implementing the `ILogger` interface, used for logging messages related to the polling process.
*/
constructor(
private readonly eventProcessor: IMessageSentEventProcessor,
private readonly chainQuerier: IChainQuerier<
private readonly provider: IProvider<
TransactionReceipt,
Block,
TransactionRequest,
@@ -136,7 +135,7 @@ export class MessageSentEventPoller implements IPoller {
* @returns {Promise<{ fromBlock: number; fromBlockLogIndex: number }>} An object containing the determined starting block number and log index.
*/
private async getInitialFromBlock(): Promise<{ fromBlock: number; fromBlockLogIndex: number }> {
let fromBlock = await this.chainQuerier.getCurrentBlockNumber();
let fromBlock = await this.provider.getBlockNumber();
const fromBlockLogIndex = 0;

View File

@@ -1,7 +1,7 @@
import { describe, it, beforeEach } from "@jest/globals";
import { MockProxy, mock } from "jest-mock-extended";
import { Direction } from "@consensys/linea-sdk";
import { TestLogger } from "../../../utils/testing/helpers";
import { Direction } from "../../../core/enums/MessageEnums";
import { testL2NetworkConfig } from "../../../utils/testing/constants";
import { IPoller } from "../../../core/services/pollers/IPoller";
import { L2ClaimMessageTransactionSizePoller } from "../L2ClaimMessageTransactionSizePoller";

View File

@@ -1,9 +1,9 @@
import { describe, it, beforeEach } from "@jest/globals";
import { mock } from "jest-mock-extended";
import { Direction } from "@consensys/linea-sdk";
import { MessageAnchoringPoller } from "../MessageAnchoringPoller";
import { TestLogger } from "../../../utils/testing/helpers";
import { IMessageAnchoringProcessor } from "../../../core/services/processors/IMessageAnchoringProcessor";
import { Direction } from "../../../core/enums/MessageEnums";
import { testL2NetworkConfig } from "../../../utils/testing/constants";
import { IPoller } from "../../../core/services/pollers/IPoller";

View File

@@ -1,9 +1,9 @@
import { describe, it, beforeEach } from "@jest/globals";
import { mock } from "jest-mock-extended";
import { Direction } from "@consensys/linea-sdk";
import { MessageClaimingPoller } from "../MessageClaimingPoller";
import { TestLogger } from "../../../utils/testing/helpers";
import { IMessageClaimingProcessor } from "../../../core/services/processors/IMessageClaimingProcessor";
import { Direction } from "../../../core/enums/MessageEnums";
import { testL2NetworkConfig } from "../../../utils/testing/constants";
import { IPoller } from "../../../core/services/pollers/IPoller";

View File

@@ -1,9 +1,9 @@
import { describe, it, beforeEach } from "@jest/globals";
import { mock } from "jest-mock-extended";
import { Direction } from "@consensys/linea-sdk";
import { MessagePersistingPoller } from "../MessagePersistingPoller";
import { TestLogger } from "../../../utils/testing/helpers";
import { IMessageClaimingPersister } from "../../../core/services/processors/IMessageClaimingPersister";
import { Direction } from "../../../core/enums/MessageEnums";
import { testL2NetworkConfig } from "../../../utils/testing/constants";
import { IPoller } from "../../../core/services/pollers/IPoller";

View File

@@ -1,24 +1,22 @@
import { describe, it, beforeEach } from "@jest/globals";
import { mock } from "jest-mock-extended";
import { Block, TransactionReceipt, TransactionRequest, TransactionResponse } from "ethers";
import { Provider, DefaultGasProvider, Direction, wait } from "@consensys/linea-sdk";
import { TestLogger } from "../../../utils/testing/helpers";
import { Direction } from "../../../core/enums/MessageEnums";
import { rejectedMessageProps, testL1NetworkConfig, testMessage } from "../../../utils/testing/constants";
import { IPoller } from "../../../core/services/pollers/IPoller";
import { MessageSentEventPoller } from "../MessageSentEventPoller";
import { IMessageSentEventProcessor } from "../../../core/services/processors/IMessageSentEventProcessor";
import { IChainQuerier } from "../../../core/clients/blockchain/IChainQuerier";
import { IProvider } from "../../../core/clients/blockchain/IProvider";
import { IMessageRepository } from "../../../core/persistence/IMessageRepository";
import { wait } from "../../../core/utils/shared";
import { DatabaseAccessError } from "../../../core/errors/DatabaseErrors";
import { DatabaseErrorType, DatabaseRepoName } from "../../../core/enums/DatabaseEnums";
import { Block, JsonRpcProvider, TransactionReceipt, TransactionRequest, TransactionResponse } from "ethers";
import { DatabaseAccessError } from "../../../core/errors";
import { DatabaseErrorType, DatabaseRepoName } from "../../../core/enums";
import { EthereumMessageDBService } from "../../persistence/EthereumMessageDBService";
import { DefaultGasProvider } from "../../../clients/blockchain/gas/DefaultGasProvider";
import {
DEFAULT_GAS_ESTIMATION_PERCENTILE,
DEFAULT_INITIAL_FROM_BLOCK,
DEFAULT_LISTENER_INTERVAL,
DEFAULT_MAX_FEE_PER_GAS,
DEFAULT_MAX_FEE_PER_GAS_CAP,
} from "../../../core/constants";
describe("TestMessageSentEventPoller", () => {
@@ -26,20 +24,19 @@ describe("TestMessageSentEventPoller", () => {
let databaseService: EthereumMessageDBService;
const eventProcessorMock = mock<IMessageSentEventProcessor>();
const l1QuerierMock =
mock<IChainQuerier<TransactionReceipt, Block, TransactionRequest, TransactionResponse, JsonRpcProvider>>();
const provider = mock<IProvider<TransactionReceipt, Block, TransactionRequest, TransactionResponse, Provider>>();
const logger = new TestLogger(MessageSentEventPoller.name);
beforeEach(() => {
const gasProvider = new DefaultGasProvider(l1QuerierMock, {
maxFeePerGas: DEFAULT_MAX_FEE_PER_GAS,
const gasProvider = new DefaultGasProvider(provider, {
maxFeePerGasCap: DEFAULT_MAX_FEE_PER_GAS_CAP,
gasEstimationPercentile: DEFAULT_GAS_ESTIMATION_PERCENTILE,
enforceMaxGasFee: false,
});
databaseService = new EthereumMessageDBService(gasProvider, mock<IMessageRepository<unknown>>());
testMessageSentEventPoller = new MessageSentEventPoller(
eventProcessorMock,
l1QuerierMock,
provider,
databaseService,
{
direction: Direction.L1_TO_L2,
@@ -58,7 +55,7 @@ describe("TestMessageSentEventPoller", () => {
describe("start", () => {
it("Should return and log as warning if it has been started", async () => {
const loggerWarnSpy = jest.spyOn(logger, "warn");
jest.spyOn(l1QuerierMock, "getCurrentBlockNumber").mockResolvedValue(10);
jest.spyOn(provider, "getBlockNumber").mockResolvedValue(10);
jest.spyOn(databaseService, "getLatestMessageSent").mockResolvedValue(null);
jest.spyOn(eventProcessorMock, "process").mockResolvedValue({
nextFromBlock: 20,
@@ -76,7 +73,7 @@ describe("TestMessageSentEventPoller", () => {
});
it("Should call process and log as info if it started successfully", async () => {
const l1QuerierMockSpy = jest.spyOn(l1QuerierMock, "getCurrentBlockNumber").mockResolvedValue(10);
const l1QuerierMockSpy = jest.spyOn(provider, "getBlockNumber").mockResolvedValue(10);
const messageRepositoryMockSpy = jest
.spyOn(databaseService, "getLatestMessageSent")
.mockResolvedValue(testMessage);
@@ -105,7 +102,7 @@ describe("TestMessageSentEventPoller", () => {
it("Should log as warning if getCurrentBlockNumber throws error", async () => {
const error = new Error("Other error for testing");
const l1QuerierMockSpy = jest.spyOn(l1QuerierMock, "getCurrentBlockNumber").mockRejectedValue(error);
const l1QuerierMockSpy = jest.spyOn(provider, "getBlockNumber").mockRejectedValue(error);
const loggerErrorSpy = jest.spyOn(logger, "error");
await testMessageSentEventPoller.start();
@@ -119,7 +116,7 @@ describe("TestMessageSentEventPoller", () => {
});
it("Should log as warning if process throws DatabaseAccessError", async () => {
const l1QuerierMockSpy = jest.spyOn(l1QuerierMock, "getCurrentBlockNumber").mockResolvedValue(10);
const l1QuerierMockSpy = jest.spyOn(provider, "getBlockNumber").mockResolvedValue(10);
const messageRepositoryMockSpy = jest
.spyOn(databaseService, "getLatestMessageSent")
.mockResolvedValue(testMessage);
@@ -162,7 +159,7 @@ describe("TestMessageSentEventPoller", () => {
});
it("Should log as warning or error if process throws Error", async () => {
const l1QuerierMockSpy = jest.spyOn(l1QuerierMock, "getCurrentBlockNumber").mockResolvedValue(10);
const l1QuerierMockSpy = jest.spyOn(provider, "getBlockNumber").mockResolvedValue(10);
const messageRepositoryMockSpy = jest
.spyOn(databaseService, "getLatestMessageSent")
.mockResolvedValue(testMessage);
@@ -193,7 +190,7 @@ describe("TestMessageSentEventPoller", () => {
const loggerInfoSpy = jest.spyOn(logger, "info");
testMessageSentEventPoller = new MessageSentEventPoller(
eventProcessorMock,
l1QuerierMock,
provider,
databaseService,
{
direction: Direction.L1_TO_L2,

View File

@@ -0,0 +1,6 @@
export { DatabaseCleaningPoller } from "./DatabaseCleaningPoller";
export { L2ClaimMessageTransactionSizePoller } from "./L2ClaimMessageTransactionSizePoller";
export { MessageAnchoringPoller } from "./MessageAnchoringPoller";
export { MessageClaimingPoller } from "./MessageClaimingPoller";
export { MessagePersistingPoller } from "./MessagePersistingPoller";
export { MessageSentEventPoller } from "./MessageSentEventPoller";

View File

@@ -1,5 +1,12 @@
import { ContractTransactionResponse, Overrides, Signer, TransactionReceipt, TransactionResponse } from "ethers";
import { MessageStatus } from "../../core/enums/MessageEnums";
import {
ContractTransactionResponse,
ErrorDescription,
Overrides,
Signer,
TransactionReceipt,
TransactionResponse,
} from "ethers";
import { MessageStatus } from "../../core/enums";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
import { IL2MessageServiceClient } from "../../core/clients/blockchain/linea/IL2MessageServiceClient";
@@ -26,7 +33,8 @@ export class L2ClaimMessageTransactionSizeProcessor implements IL2ClaimMessageTr
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse,
Signer
Signer,
ErrorDescription
>,
private readonly transactionSizeCalculator: IL2ClaimTransactionSizeCalculator,
private readonly config: L2ClaimMessageTransactionSizeProcessorConfig,

View File

@@ -6,13 +6,15 @@ import {
Block,
TransactionRequest,
JsonRpcProvider,
ErrorDescription,
} from "ethers";
import { OnChainMessageStatus } from "@consensys/linea-sdk";
import {
IMessageAnchoringProcessor,
MessageAnchoringProcessorConfig,
} from "../../core/services/processors/IMessageAnchoringProcessor";
import { IChainQuerier } from "../../core/clients/blockchain/IChainQuerier";
import { MessageStatus, OnChainMessageStatus } from "../../core/enums/MessageEnums";
import { IProvider } from "../../core/clients/blockchain/IProvider";
import { MessageStatus } from "../../core/enums";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IMessageServiceContract } from "../../core/services/contracts/IMessageServiceContract";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
@@ -24,7 +26,7 @@ export class MessageAnchoringProcessor implements IMessageAnchoringProcessor {
* Constructs a new instance of the `MessageAnchoringProcessor`.
*
* @param {IMessageServiceContract<Overrides, TransactionReceipt, TransactionResponse, ContractTransactionResponse>} contractClient - An instance of a class implementing the `IMessageServiceContract` interface, used to interact with the blockchain contract.
* @param {IChainQuerier<TransactionReceipt, Block, TransactionRequest, TransactionResponse, JsonRpcProvider>} chainQuerier - An instance of a class implementing the `IChainQuerier` interface, used to query blockchain data.
* @param {IProvider<TransactionReceipt, Block, TransactionRequest, TransactionResponse, JsonRpcProvider>} provider - An instance of a class implementing the `IProvider` interface, used to query blockchain data.
* @param {IMessageDBService<ContractTransactionResponse>} databaseService - An instance of a class implementing the `IMessageDBService` interface, used for storing and retrieving message data.
* @param {MessageAnchoringProcessorConfig} config - Configuration settings for the processor, including the maximum number of messages to fetch from the database for processing.
* @param {ILogger} logger - An instance of a class implementing the `ILogger` interface, used for logging messages.
@@ -34,9 +36,10 @@ export class MessageAnchoringProcessor implements IMessageAnchoringProcessor {
Overrides,
TransactionReceipt,
TransactionResponse,
ContractTransactionResponse
ContractTransactionResponse,
ErrorDescription
>,
private readonly chainQuerier: IChainQuerier<
private readonly provider: IProvider<
TransactionReceipt,
Block,
TransactionRequest,
@@ -70,7 +73,7 @@ export class MessageAnchoringProcessor implements IMessageAnchoringProcessor {
return;
}
const latestBlockNumber = await this.chainQuerier.getCurrentBlockNumber();
const latestBlockNumber = await this.provider.getBlockNumber();
for (const message of messages) {
const messageStatus = await this.contractClient.getMessageStatus(message.messageHash, {

Some files were not shown because too many files have changed in this diff Show More