OpenZeppelin Monitor

This software is in alpha stage. Use in production environments at your own risk.

In the rapidly evolving world of blockchain technology, effective monitoring is crucial for ensuring security and performance. OpenZeppelin Monitor is a blockchain monitoring service that watches for specific on-chain activities and triggers notifications based on configurable conditions. The service offers multi-chain support with configurable monitoring schedules, flexible trigger conditions, and an extensible architecture for adding new chains.

Features:

  • Multi-chain support

  • Configurable monitoring schedules

  • Flexible trigger conditions

  • Extensible architecture for adding new chains

Supported Networks (OOTB):

  • EVM

  • Stellar

Supported Triggers (OOTB):

  • Slack notifications

  • Email notifications

  • Discord notifications

  • Telegram notifications

  • Webhook notifications

  • Script notifications

To get started immediately, see Quickstart.

Technical Overview

%%{init: { 'theme': 'base', 'themeVariables': { 'background': '#ffffff', 'mainBkg': '#ffffff', 'primaryBorderColor': '#cccccc' } }}%% graph TD subgraph Blockchain Networks ETH[Ethereum RPC] POL[Polygon RPC] BSC[BSC RPC] end subgraph Block Processing BW[BlockWatcherService] BS[(BlockStorage)] JS[JobScheduler] end subgraph Client Layer BC[BlockchainClient] EVM[EVMClient] STL[StellarClient] end subgraph Processing Pipeline FS[FilterService] TS[TriggerService] NS[NotificationService] end subgraph Notifications Slack Email Discord Telegram Webhook Script end %% Block Processing Flow JS -->|Schedule Block Fetch| BW BW -->|Store Last Block| BS BW -->|Read Last Block| BS BW -->|Get New Blocks| BC %% Client Connections BC --> EVM BC --> STL EVM -->|RPC Calls| ETH EVM -->|RPC Calls| POL EVM -->|RPC Calls| BSC %% Processing Flow BW -->|New Block| FS FS -->|Matches| TS TS -->|Execute| NS NS --> Slack NS --> Email NS --> Discord NS --> Telegram NS --> Webhook NS --> Script style STL fill:#f0f0f0 classDef rpc fill:#e1f5fe,stroke:#01579b classDef storage fill:#fff3e0,stroke:#ef6c00 classDef service fill:#e8f5e9,stroke:#2e7d32 classDef notification fill:#f3e5f5,stroke:#7b1fa2 class ETH,POL,BSC rpc class BS storage class BW,FS,TS,NS service class Slack,Email,Discord,Telegram,Webhook,Script notification

Project Structure

The project follows a standard Rust project layout:

openzeppelin-monitor/
├── src/                # Source code
│   ├── bootstrap/      # Bootstrap functions for the application
│   ├── models/         # Data structures and types
│   ├── repositories/   # Configuration storage
│   ├── services/       # Core business logic
│   ├── utils/          # Helper functions
│
├── config/             # Configuration files
├── tests/              # Integration and property-based tests
├── data/               # Runtime data storage
├── docs/               # Documentation
├── scripts/            # Utility scripts
├── cmd/                # Metrics and monitoring
├── examples/           # Example configuration files
└── ... other root files (Cargo.toml, README.md, etc.)

For detailed information about each directory and its contents, see Project Structure Details.

Getting Started

Prerequisites

  • Rust 2021 edition

  • Docker (optional, for containerized deployment)

Installation

Run Locally

  1. Clone the repository:

    git clone https://github.com/openzeppelin/openzeppelin-monitor
    cd openzeppelin-monitor
  2. Install dependencies:

    cargo build --release
  3. Move the binary to the project root (so that it can access the ./config folder)

    mv ./target/release/openzeppelin-monitor .
  4. View available options:

    ./openzeppelin-monitor --help
    
    # Enable logging to file
    ./openzeppelin-monitor --log-file
    
    # Enable metrics server
    ./openzeppelin-monitor --metrics
    
    # Validate configuration files without starting the service
    ./openzeppelin-monitor --check

Run with Docker

Basic Setup

  1. Start the services using the make target:

    cargo make docker-compose-up

By default, Docker Compose uses Dockerfile.development. To use production settings, set: DOCKERFILE=Dockerfile.production before running the command.

Metrics Configuration

The metrics server, Prometheus, and Grafana can be enabled by setting METRICS_ENABLED=true in your .env file.

You can start services directly with Docker Compose:

# without metrics profile ( METRICS_ENABLED=false by default )
docker compose up -d

# With metrics enabled
docker compose --profile metrics up -d

You can access the monitoring interfaces through your browser:

Grafana comes pre-configured with default dashboards and metrics visualizations.

Management Commands

  1. Verify container status:

    docker ps -a
  2. Stop services:

    cargo make docker-compose-down
    # or using docker compose directly:
    # without metrics profile
    docker compose down
    # or with metrics profile
    docker compose --profile metrics down
  3. View logs (stdout):

    docker compose logs -f

Configuration Guidelines

  • Network configurations: <network_type>_<network_name>.json

    • Example: ethereum_mainnet.json, stellar_testnet.json

    • Should match the slug property inside the file

  • Monitor configurations: <asset>_<action>_monitor.json

    • Example: usdc_transfer_monitor.json, dai_liquidation_monitor.json

    • Referenced by monitors using their name property

  • Trigger configurations: <type>_<purpose>.json

    • Example: slack_notifications.json, email_alerts.json

    • Individual triggers referenced by their configuration key

Configuration References

  • Monitor’s networks array must contain valid network slug values from network configuration files

  • Monitor’s triggers array must contain valid trigger configuration keys

  • Example valid references:

    // networks/ethereum_mainnet.json
    {
      "slug": "ethereum_mainnet",
      ...
    }
    
    // triggers/slack_notifications.json
    {
      "large_transfer_slack": {
        ...
      }
    }
    
    // monitors/usdc_transfer_monitor.json
    {
      "networks": ["ethereum_mainnet"],
      "triggers": ["large_transfer_slack"],
      ...
    }

Ensure all referenced slugs and trigger keys exist in their respective configuration files. The monitor will fail to start if it cannot resolve these references.

Safe Protocol Guidelines

The monitor implements protocol security validations across different components and will issue warnings when potentially insecure configurations are detected. While insecure protocols are not blocked, we strongly recommend following these security guidelines:

Network Protocols
RPC URLs
  • HTTPS Recommended: Using https:// for RPC endpoints is strongly recommended

  • WSS Recommended: For WebSocket connections, wss:// (secure WebSocket) is strongly recommended

  • Warning: Using http:// or ws:// will trigger security warnings as they transmit data unencrypted

Notification Protocols
Slack Notifications
  • HTTPS Recommended: Webhook URLs should start with https://hooks.slack.com/

  • Warning: Non-HTTPS URLs will trigger security warnings

Discord Notifications
Email Notifications
  • Secure Ports Recommended: The following ports are considered secure:

    • 465: SMTPS (SMTP over SSL)

    • 587: SMTP with STARTTLS

    • 993: IMAPS (IMAP over SSL)

  • Warning: Using other ports will trigger security warnings

  • Valid Format: Email addresses must follow RFC 5322 format

Webhook Notifications
  • HTTPS Recommended: URLs should use HTTPS protocol

  • Authentication Recommended: Including either:

    • X-API-Key header

    • Authorization header

  • Optional Secret: Can include a secret for HMAC authentication

    • When a secret is provided, the monitor will:

      • Generate a timestamp in milliseconds

      • Create an HMAC-SHA256 signature of the payload and timestamp

      • Add the signature in the X-Signature header

      • Add the timestamp in the X-Timestamp header

    • The signature is computed as: HMAC-SHA256(secret, payload + timestamp)

  • Warning: Non-HTTPS URLs or missing authentication headers will trigger security warnings

Script Security
File Permissions (Unix Systems)
  • Restricted Write Access: Script files should not have overly permissive write permissions

  • Recommended Permissions: Use 644 (rw-r—​r--) for script files

  • Warning: Files with mode 022 or more permissive will trigger security warnings

Example Setting Recommended Permissions
chmod 644 ./config/filters/my_script.sh

Secret Management

The monitor implements a secure secret management system with support for multiple secret sources and automatic memory zeroization.

Secret Sources

The monitor supports three types of secret sources:

  • Plain Text: Direct secret values (wrapped in SecretString for secure memory handling)

  • Environment Variables: Secrets stored in environment variables

  • Hashicorp Cloud Vault: Secrets stored in Hashicorp Cloud Vault

Security Features
  • Automatic Zeroization: Secrets are automatically zeroized from memory when no longer needed

  • Type-Safe Resolution: Secure handling of secret resolution with proper error handling

  • Configuration Support: Serde support for configuration files

Configuration

Secrets can be configured in the JSON files using the following format:

{
  "type": "Plain",
  "value": "my-secret-value"
}
{
  "type": "Environment",
  "value": "MY_SECRET_ENV_VAR"
}
{
  "type": "HashicorpCloudVault",
  "value": "my-secret-name"
}
Hashicorp Cloud Vault Integration

To use Hashicorp Cloud Vault, configure the following environment variables:

Environment Variable Description

HCP_CLIENT_ID

Hashicorp Cloud Vault client ID

HCP_CLIENT_SECRET

Hashicorp Cloud Vault client secret

HCP_ORG_ID

Hashicorp Cloud Vault organization ID

HCP_PROJECT_ID

Hashicorp Cloud Vault project ID

HCP_APP_NAME

Hashicorp Cloud Vault application name

Best Practices
  • Use environment variables or vault for production secrets

  • Avoid storing plain text secrets in configuration files

  • Use appropriate access controls for vault secrets

  • Monitor vault access patterns for suspicious activity

Basic Configuration

  • Set up environment variables:

Copy the example environment file and update values according to your needs

cp .env.example .env

This table lists the environment variables and their default values.

Environment Variable Default Value Accepted Values Description

RUST_LOG

info

info, debug, warn, error, trace

Log level.

LOG_MODE

stdout

stdout, file

Write logs either to console or to file.

LOG_DATA_DIR

logs/

<any file path>

Directory to write log files on host.

MONITOR_DATA_DIR

null

<any file path>

Persist monitor data between container restarts.

LOG_MAX_SIZE

1073741824

<size in bytes or human-readable format (e.g., "1GB", "500MB")>

Size after which logs needs to be rolled. Accepts both raw bytes (e.g., "1073741824") or human-readable formats (e.g., "1GB", "500MB").

METRICS_ENABLED

false

true, false

Enable metrics server for external tools to scrape metrics.

METRICS_PORT

8081

<any tcp port (preferably choose non-privileged ports i.e. (1024-65535))>

Port to use for metrics server.

HCP_CLIENT_ID

-

<string>

Hashicorp Cloud Vault client ID for secret management.

HCP_CLIENT_SECRET

-

<string>

Hashicorp Cloud Vault client secret for secret management.

HCP_ORG_ID

-

<string>

Hashicorp Cloud Vault organization ID for secret management.

HCP_PROJECT_ID

-

<string>

Hashicorp Cloud Vault project ID for secret management.

HCP_APP_NAME

-

<string>

Hashicorp Cloud Vault application name for secret management.

  • Copy and configure some example files:

# EVM Configuration
cp examples/config/monitors/evm_transfer_usdc.json config/monitors/evm_transfer_usdc.json
cp examples/config/networks/ethereum_mainnet.json config/networks/ethereum_mainnet.json

# Stellar Configuration
cp examples/config/monitors/stellar_swap_dex.json config/monitors/stellar_swap_dex.json
cp examples/config/networks/stellar_mainnet.json config/networks/stellar_mainnet.json

# Notification Configuration
cp examples/config/triggers/slack_notifications.json config/triggers/slack_notifications.json
cp examples/config/triggers/email_notifications.json config/triggers/email_notifications.json

# Filter Configuration
cp examples/config/filters/evm_filter_block_number.sh config/filters/evm_filter_block_number.sh
cp examples/config/filters/stellar_filter_block_number.sh config/filters/stellar_filter_block_number.sh

Command Line Options

The monitor supports several command-line options for configuration and control:

Option Default Description

--log-file

false

Write logs to file instead of stdout

--log-level

info

Set log level (trace, debug, info, warn, error)

--log-path

logs/

Path to store log files

--log-max-size

1GB

Maximum log file size before rolling

--metrics-address

127.0.0.1:8081

Address to start the metrics server on

--metrics

false

Enable metrics server

--monitor-path

-

Path to the monitor to execute (for testing)

--network

-

Network to execute the monitor for (for testing)

--block

-

Block number to execute the monitor for (for testing)

--check

false

Validate configuration files without starting the service

Data Storage Configuration

The monitor uses file-based storage by default, with an extensible interface (BlockStorage) for custom storage implementations.

File Storage

When store_blocks is enabled in the network configuration, the monitor stores:

  • Processed blocks: ./data/<network_slug>_blocks_<timestamp>.json

  • Missed blocks: ./data/<network_slug>_missed_blocks.txt (used to store missed blocks)

The content of the missed_blocks.txt file may help to determine the right max_past_blocks value based on the network’s block time and the monitor’s cron schedule.

Additionally, the monitor will always store:

  • Last processed block: ./data/<network_slug>_last_block.txt (enables resuming from last checkpoint)

Configuration Files

Network Configuration

A Network configuration defines connection details and operational parameters for a specific blockchain network, supporting both EVM and Stellar-based chains.

Example Network Configuration
{
  "network_type": "Stellar",
  "slug": "stellar_mainnet",
  "name": "Stellar Mainnet",
  "rpc_urls": [
    {
      "type_": "rpc",
      "url": {
        "type": "plain",
        "value": "https://soroban.stellar.org"
      },
      "weight": 100
    }
  ],
  "network_passphrase": "Public Global Stellar Network ; September 2015",
  "block_time_ms": 5000,
  "confirmation_blocks": 2,
  "cron_schedule": "0 */1 * * * *",
  "max_past_blocks": 20,
  "store_blocks": true
}

Available Fields

Field Type Description

network_type

String

Type of blockchain ("EVM" or "Stellar")

slug

String

Unique identifier for the network

name

String

Human-readable network name

rpc_urls

Array[Object]

List of RPC endpoints with weights for load balancing

chain_id

Number

Network chain ID (EVM only)

network_passphrase

String

Network identifier (Stellar only)

block_time_ms

Number

Average block time in milliseconds

confirmation_blocks

Number

Number of blocks to wait for confirmation

cron_schedule

String

Monitor scheduling in cron format

max_past_blocks

Number

Maximum number of past blocks to process

store_blocks

Boolean

Whether to store processed blocks (defaults output to ./data/ directory)

Important Considerations

  • We strongly recommend using private RPC providers for improved reliability.

Trigger Configuration

A Trigger defines actions to take when monitored conditions are met. Triggers can send notifications, make HTTP requests, or execute scripts.

Example Trigger Configuration
{
  "evm_large_transfer_usdc_slack": {
    "name": "Large Transfer Slack Notification",
    "trigger_type": "slack",
    "config": {
      "slack_url": {
        "type": "plain",
        "value": "https://hooks.slack.com/services/A/B/C"
      },
      "message": {
        "title": "large_transfer_slack triggered",
        "body": "Large transfer of ${events.0.args.value} USDC from ${events.0.args.from} to ${events.0.args.to} | https://etherscan.io/tx/${transaction.hash}#eventlog"
      }
    }
  },
  "stellar_large_transfer_usdc_slack": {
    "name": "Large Transfer Slack Notification",
    "trigger_type": "slack",
    "config": {
      "slack_url": {
        "type": "environment",
        "value": "SLACK_WEBHOOK_URL"
      },
      "message": {
        "title": "large_transfer_usdc_slack triggered",
        "body": "${monitor.name} triggered because of a large transfer of ${functions.0.args.amount} USDC to ${functions.0.args.to} | https://stellar.expert/explorer/testnet/tx/${transaction.hash}"
      }
    }
  }
}

Trigger Types

Slack Notifications
{
  "slack_url": {
    "type": "HashicorpCloudVault",
    "value": "slack-webhook-url"
  },
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Slack Notification Fields
Field Type Description

name

String

Human-readable name for the notification

trigger_type

String

Must be "slack" for Slack notifications

config.slack_url.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.slack_url.value

String

Secret value (URL, environment variable name, or vault secret name)

config.message.title

String

Title that appears in the Slack message

config.message.body

String

Message template with variable substitution

Email Notifications
{
  "host": "smtp.gmail.com",
  "port": 465,
  "username": {
    "type": "plain",
    "value": "[email protected]"
  },
  "password": {
    "type": "environment",
    "value": "SMTP_PASSWORD"
  },
  "message": {
    "title": "Alert Subject",
    "body": "Alert message for ${transaction.hash}",
  },
  "sender": "[email protected]",
  "recipients": ["[email protected]"]
}
Email Notification Fields
Field Type Description

name

String

Human-readable name for the notification

trigger_type

String

Must be "email" for email notifications

config.host

String

SMTP server hostname

config.port

Number

SMTP port (defaults to 465)

config.username.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.username.value

String

Secret value (username, environment variable name, or vault secret name)

config.password.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.password.value

String

Secret value (password, environment variable name, or vault secret name)

config.message.title

String

Email subject line

config.message.body

String

Email body template with variable substitution

config.sender

String

Sender email address

config.recipients

Array[String]

List of recipient email addresses

Webhook Notifications
{
  "url": {
    "type": "HashicorpCloudVault",
    "value": "webhook-url"
  },
  "method": "POST",
  "secret": {
    "type": "environment",
    "value": "WEBHOOK_SECRET"
  },
  "headers": {
    "Content-Type": "application/json"
  },
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Webhook Notification Fields
Field Type Description

name

String

Human-readable name for the notification

trigger_type

String

Must be "webhook" for webhook notifications

config.url.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.url.value

String

Secret value (URL, environment variable name, or vault secret name)

config.method

String

HTTP method (POST, GET, etc.) defaults to POST

config.secret.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.secret.value

String

Secret value (HMAC secret, environment variable name, or vault secret name)

config.headers

Object

Headers to include in the webhook request

config.message.title

String

Title that appears in the webhook message

config.message.body

String

Message template with variable substitution

Discord Notifications
{
  "discord_url": {
    "type": "plain",
    "value": "https://discord.com/api/webhooks/123-456-789"
  },
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Discord Notification Fields
Field Type Description

name

String

Human-readable name for the notification

trigger_type

String

Must be "discord" for Discord notifications

config.discord_url.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.discord_url.value

String

Secret value (URL, environment variable name, or vault secret name)

config.message.title

String

Title that appears in the Discord message

config.message.body

String

Message template with variable substitution

Telegram Notifications
{
  "token": {
    "type": "HashicorpCloudVault",
    "value": "telegram-bot-token"
  },
  "chat_id": "9876543210",
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Telegram Notification Fields
Field Type Description

name

String

Human-readable name for the notification

trigger_type

String

Must be "telegram" for Telegram notifications

config.token.type

String

Secret type ("Plain", "Environment", or "HashicorpCloudVault")

config.token.value

String

Secret value (bot token, environment variable name, or vault secret name)

config.chat_id

String

Telegram chat ID

config.disable_web_preview

Boolean

Whether to disable web preview in Telegram messages (defaults to false)

config.message.title

String

Title that appears in the Telegram message

config.message.body

String

Message template with variable substitution

Custom Script Notifications
{
  "language": "Bash",
  "script_path": "./config/triggers/scripts/custom_notification.sh",
  "arguments": ["--verbose"],
  "timeout_ms": 1000
}
Script Notification Fields
Field Type Description

name

String

Human-readable name for the notification

trigger_type

String

Must be "script" for Custom Script notifications

language

String

The language of the script

script_path

String

The path to the script

arguments

Array[String]

The arguments of the script (optional).

timeout_ms

Number

The timeout of the script is important to avoid infinite loops during the execution. If the script takes longer than the timeout, it will be killed.

For more information about custom scripts, see Custom Scripts Section.

Security Risk: Only run scripts that you trust and fully understand. Malicious scripts can harm your system or expose sensitive data. Always review script contents and verify their source before execution.

Available Template Variables

The monitor uses a structured JSON format with nested objects for template variables. The data is flattened into dot notation for template use.

Common Variables
Variable Description

monitor.name

Name of the triggered monitor

transaction.hash

Hash of the transaction

functions.[index].signature

Function signature

events.[index].signature

Event signature

Network-Specific Variables
EVM Variables
Variable Description

transaction.from

Sender address

transaction.to

Recipient address

transaction.value

Transaction value

events.[index].args.[param]

Event parameters by name

functions.[index].args.[param]

Function parameters by name

Stellar Variables
Variable Description

events.[index].args.[position]

Event parameters by position

functions.[index].args.[param]

Function parameters by name

Transaction-related variables (transaction.from, transaction.to, transaction.value) are not available for Stellar networks.

Message Formatting

Slack, Discord, Telegram, Email and Webhook support Markdown formatting in their message bodies. You can use Markdown syntax to enhance your notifications.

Example Email Notification with Markdown
{
  "email_notification": {
    "name": "Formatted Alert",
    "trigger_type": "email",
    "config": {
      "host": "smtp.example.com",
      "port": 465,
      "username": {"type": "plain", "value": "[email protected]"},
      "password": {"type": "plain", "value": "password"},
      "message": {
        "title": "**High Value Transfer Alert**",
        "body": "### Transaction Details\n\n* **Amount:** ${events.0.args.value} USDC\n* **From:** `${events.0.args.from}`\n* **To:** `${events.0.args.to}`\n\n> Transaction Hash: ${transaction.hash}\n\n[View on Explorer](https://etherscan.io/tx/${transaction.hash})"
      },
      "sender": "[email protected]",
      "recipients": ["[email protected]"]
    }
  }
}
Example Slack Notification with Markdown
{
  "slack_notification": {
    "name": "Formatted Alert",
    "trigger_type": "slack",
    "config": {
      "slack_url": {"type": "plain", "value": "https://hooks.slack.com/services/XXX/YYY/ZZZ"},
      "message": {
        "title": "*🚨 High Value Transfer Alert*",
        "body": "*Transaction Details*\n\n• *Amount:* `${events.0.args.value}` USDC\n• *From:* `${events.0.args.from}`\n• *To:* `${events.0.args.to}`\n\n>Transaction Hash: `${transaction.hash}`\n\n<https://etherscan.io/tx/${transaction.hash}|View on Explorer>"
      }
    }
  }
}
Example Discord Notification with Markdown
{
  "discord_notification": {
    "name": "Formatted Alert",
    "trigger_type": "discord",
    "config": {
      "discord_url": {"type": "plain", "value": "https://discord.com/api/webhooks/XXX/YYY"},
      "message": {
        "title": "**🚨 High Value Transfer Alert**",
        "body": "# Transaction Details\n\n* **Amount:** `${events.0.args.value}` USDC\n* **From:** `${events.0.args.from}`\n* **To:** `${events.0.args.to}`\n\n>>> Transaction Hash: `${transaction.hash}`\n\n**[View on Explorer](https://etherscan.io/tx/${transaction.hash})"
      }
    }
  }
}
Example Telegram Notification with Markdown
{
  "telegram_notification": {
    "name": "Formatted Alert",
    "trigger_type": "telegram",
    "config": {
      "token": {"type": "plain", "value": "1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ"},
      "chat_id": "9876543210",
      "message": {
        "title": "*🚨 High Value Transfer Alert*",
        "body": "*Transaction Details*\n\n• *Amount:* `${events.0.args.value}` USDC\n• *From:* `${events.0.args.from}`\n• *To:* `${events.0.args.to}`\n\n`Transaction Hash: ${transaction.hash}`\n\n[View on Explorer](https://etherscan.io/tx/${transaction.hash})"
      }
    }
  }
}

Important Considerations:

  • Email notification port defaults to 465 if not specified.

  • Template variables are context-dependent:

    • Event-triggered notifications only populate event variables.

    • Function-triggered notifications only populate function variables.

    • Mixing contexts results in empty values.

  • Credentials in configuration files should be properly secured.

  • Consider using environment variables for sensitive information.

Monitor Configuration

A Monitor defines what blockchain activity to watch and what actions to take when conditions are met. Each monitor combines:

  • Network targets (which chains to monitor)

  • Contract addresses to watch

  • Conditions to match (functions, events, transactions)

  • Trigger conditions (custom scripts that act as filters for each monitor match to determine whether a trigger should be activated).

  • Triggers to execute when conditions are met

Example Monitor Configuration
{
  "name": "Large USDC Transfers",
  "networks": ["ethereum_mainnet"],
  "paused": false,
  "addresses": [
    {
      "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "contract_spec": [ ... ]
    }
  ],
  "match_conditions": {
    "functions": [
      {
        "signature": "transfer(address,uint256)",
        "expression": "value > 1000000"
      }
    ],
    "events": [
      {
        "signature": "Transfer(address,address,uint256)",
        "expression": "amount > 1000000"
      }
    ],
    "transactions": [
      {
        "status": "Success",
        "expression": "value > 1500000000000000000"
      }
    ]
  },
  "trigger_conditions": [
    {
      "script_path": "./config/filters/evm_filter_block_number.sh",
      "language": "bash",
      "arguments": "--verbose",
      "timeout_ms": 1000
    }
  ],
  "triggers": ["evm_large_transfer_usdc_slack", "evm_large_transfer_usdc_email"]
}

Match Conditions

Monitors support three types of match conditions that can be combined:

Function Conditions

Match specific function calls to monitored contracts:

{
  "functions": [
    {
      "signature": "transfer(address,uint256)",
      "expression": "value > 1000"
    }
  ]
}
Event Conditions

Match events emitted by monitored contracts:

{
  "events": [
    {
      "signature": "Transfer(address,address,uint256)",
      "expression": "amount > 1000000"
    }
  ]
}
Transaction Conditions

Match transaction properties. The available fields and expression syntax depend on the network type (EVM/Stellar)

{
  "transactions": [
    {
      "status": "Success", // Only match successful transactions
      "expression": "value > 1500000000000000000" // Match transactions with value greater than 1.5 ETH
    }
  ]
}

Available Transaction Fields (EVM)

Field Type Description

value

uint256

Transaction value in wei

from

address

Sender address (case-insensitive comparison)

to

address

Recipient address (case-insensitive comparison)

hash

string

Transaction hash

gas_price

uint256

Gas price in wei (legacy transactions)

max_fee_per_gas

uint256

EIP-1559 maximum fee per gas

max_priority_fee_per_gas

uint256

EIP-1559 priority fee

gas_limit

uint256

Gas limit for transaction

nonce

uint256

Sender nonce

input

string

Hex-encoded input data (e.g., "0xa9059cbb…​")

gas_used

uint256

Actual gas used (from receipt)

transaction_index

uint64

Position in block (from receipt)

Available Transaction Fields (Stellar)

Field Type Description

hash

string

Transaction hash

ledger

i64

Ledger sequence number where the transaction was included

value

i64

Value associated with the first relevant operation (e.g., payment amount). Defaults to 0 if no relevant operation or value is found.

from

address

Source account address of the first relevant operation (e.g., payment sender). Case-insensitive comparison.

to

address

Destination account address of the first relevant operation (e.g., payment recipient or invoked contract). Case-insensitive comparison.

Expression Syntax Enhancements

Supported Value Types:

  • Numeric (uint/int types): Supports standard comparison operators: >, >=, <, , ==, !=.

  • Address: Supports equality checks ==, !=. Comparisons are case-insensitive.

  • String: Supports equality (==, !=). Comparisons are case-insensitive.

  • EVM Specific: The input field also supports starts_with, ends_with, contains.

  • Boolean (bool): Supports equality checks ==, !=. Values are true or false.

Logical Operators:

  • AND - All conditions must be true

  • OR - At least one condition must be true

  • () - Parentheses for grouping

Examples (EVM):

// Match transfers where the amount is between 1000 and 5000
"amount >= 1000 AND amount <= 5000"

// Match transactions with high value OR sent from a specific address
"(value > 10000000000000000000) OR (from == 0xDeAdBeef...)"

// Match specific function calls based on input data prefix (case-insensitive)
"input starts_with 0xa9059cbb"

// Monitor high gas fees (EIP-1559)
"max_fee_per_gas > 50000000000 AND max_priority_fee_per_gas > 2000000000" // >50 Gwei fee, >2 Gwei tip

// Match transactions at the start of a block with high gas usage
"transaction_index == 0 AND gas_used > 100000"

Available Fields

Field Type Description

name

String

Unique identifier for this monitor

networks

Array[String]

List of network slugs this monitor should watch

paused

Boolean

Whether this monitor is currently paused

addresses

Array[Object]

Contract addresses to monitor with optional ABIs

match_conditions

Object

Collection of conditions that can trigger the monitor

trigger_conditions

Array[Object]

Collection of filters to apply to monitor matches before executing triggers

triggers

Array[String]

IDs of triggers to execute when conditions match

Matching Rules

  • If no conditions are specified, all transactions match

  • For multiple condition types:

    • Transaction conditions are checked first

    • Then either function OR event conditions must match

    • Both transaction AND (function OR event) must match if both specified

Expressions

Network Type Access Method

Stellar

Arguments accessed by parameter names for functions and numeric index for events (e.g., [0, 1, 2])

EVM

Arguments accessed by parameter names from ABI

Examples

For EVM event Transfer(address from, address to, uint256 value):

{
  "expression": "value > 10000000000"
}

For Stellar event Transfer(address, address, uint256):

{
  "expression": "2 > 10000000000"
}

For EVM or Stellar function transfer(Address from, Address to, I128 amount):

{
  "expression": "amount > 1000"
}

With SEP-48 support, Stellar functions can now reference parameters by name (e.g., amount > 1000) instead of position (e.g., 2 > 1000). Events still use indexed parameters until SEP-48 support is added for events.

You can find the contract specification through Stellar contract explorer tool. For example: Stellar DEX Contract Interface

Trigger Conditions (Custom filters)

Custom filters allow you to create sophisticated filtering logic for processing monitor matches. These filters act as additional validation layers that determine whether a match should trigger the execution of a trigger or not.

For more information about custom scripts, see Custom Scripts Section.

Security Risk: Only run scripts that you trust and fully understand. Malicious scripts can harm your system or expose sensitive data. Always review script contents and verify their source before execution.

Example Trigger Conditions Configuration
{
  "script_path": "./config/filters/evm_filter_block_number.sh",
  "language": "Bash",
  "arguments": ["--verbose"],
  "timeout_ms": 1000
}

Available Fields

Trigger Conditions Fields
Field Type Description

script_path

String

The path to the script

language

String

The language of the script

arguments

Array[String]

The arguments of the script (optional).

timeout_ms

Number

The timeout of the script is important to avoid infinite loops during the execution. If the script takes longer than the timeout, it will be killed and the match will be included by default.

Important Considerations:

  • Network slugs in the monitor must match valid network configurations.

  • Trigger IDs must match configured triggers.

  • Expression syntax and available variables differ between EVM and Stellar networks.

  • ABIs can be provided in two ways:

    • For EVM networks: Through the monitor configuration using standard Ethereum ABI format

    • For Stellar networks: Through the monitor configuration using SEP-48 format, or automatically fetched from the chain if not provided

  • The monitoring frequency is controlled by the network’s cron_schedule.

  • Each monitor can watch multiple networks and addresses simultaneously.

  • Monitors can be paused without removing their configuration.

Testing your configuration

Network Configuration

The validate_network_config.sh script helps ensure your network configuration is properly set up and operational. The script:

  • Tests the health of all configured RPC endpoints

  • Validates connectivity using network-specific methods

  • Provides clear visual feedback for each endpoint

# Test default networks directory (/config/networks/)
./scripts/validate_network_config.sh

# Test a specific configuration directory
./scripts/validate_network_config.sh -f /path/to/configs
Run this script when setting up new networks, before deploying configuration changes, or when troubleshooting connectivity issues.

Validating Configuration Files

Before starting the monitor service, you can validate your configuration files using the --check option:

./openzeppelin-monitor --check

This command will:

  • Parse and validate all configuration files

  • Check for syntax errors

  • Verify references between monitors, networks, and triggers

  • Report any issues without starting the service

It’s recommended to run this check after making changes to any configuration files.

Monitor Configuration

The monitor can be tested in two modes:

1. Latest Block Mode

This mode processes the most recent blocks across all configured networks.

./openzeppelin-monitor --monitor-path="config/monitors/evm_transfer_usdc.json"

What this does:

  • Runs the "Large Transfer of USDC Token" monitor

  • Targets all networks specified in the configuration

  • Processes only the latest block for each network

  • Sends a notification to all associated channels for every match that is found

2. Specific Block Mode

This mode allows you to analyze a particular block on a specific network, which is useful for debugging specific transactions, verifying monitor behavior on known events, and testing monitor performance on historical data.

./openzeppelin-monitor \
    --monitor-path="config/monitors/evm_transfer_usdc.json" \
    --network=ethereum_mainnet \
    --block=12345678

What this does:

  • Runs the "Large Transfer of USDC Token" monitor

  • Targets only the specified network (ethereum_mainnet)

  • Processes only the specified block (12345678)

  • Sends a notification to all associated channels for every match that is found

Specific Block Mode requires both parameters:

  • --network: The network to analyze

  • --block: The block number to process

Running the Monitor

Local Execution

./openzeppelin-monitor

Docker Deployment

The monitor can be run as either a development or production container using the corresponding Dockerfile (Dockerfile.development or Dockerfile.production).

Environment Configuration

  • Copy .env.example to .env and change values according to your needs.

Build Image and Run Containers

You can build and run containers using single command:

cargo make docker-compose-up

By default, Docker Compose uses Dockerfile.development. To use production settings, set: DOCKERFILE=Dockerfile.production before running the command.

Metrics Configuration

The metrics server, Prometheus, and Grafana can be enabled by setting METRICS_ENABLED=true in your .env file.

You can start services directly with Docker Compose:

# without metrics profile ( METRICS_ENABLED=false by default )
docker compose up -d

# With metrics enabled
docker compose --profile metrics up -d

Management Commands

  1. Verify container status:

    docker ps -a
  2. Stop services:

    cargo make docker-compose-down
    # or using docker compose directly:
    # without metrics profile
    docker compose down
    # or with metrics profile
    docker compose --profile metrics down

The build process will include:

  • The appropriate .env file

  • Configurations from the ./config folder

To modify configurations without rebuilding:

  • Restart the container

docker compose restart monitor

Data Persistence (Optional)

  • Set LOG_MODE as file will persist the log data in logs/ on host. To change it to a different directory use LOG_DATA_DIR.

  • Set MONITOR_DATA_DIR to specific dir on your host system which will persist data between container restarts.

Error Handling

The monitor implements a comprehensive error handling system with rich context and tracing capabilities. For detailed information about error handling, see Error Handling Guide.

Important Considerations:

Performance Considerations:

  • Monitor performance depends on network congestion and RPC endpoint reliability.

  • The max_past_blocks configuration is critical:

    • Calculate as: (cron_interval_ms/block_time_ms) + confirmation_blocks + 1 (defaults to this calculation if not specified).

    • Example for 1-minute Ethereum cron: (60000/12000) + 12 + 1 = 18 blocks.

    • Too low settings may result in missed blocks.

  • Trigger conditions are executed sequentially based on their position in the trigger conditions array. Proper execution also depends on the number of available file descriptors on your system. To ensure optimal performance, it is recommended to increase the limit for open file descriptors to at least 2048 or higher. On Unix-based systems you can check the current limit by running ulimit -n and temporarily increase it with ulimit -n 2048.

  • Since scripts are loaded at startup, any modifications to script files require restarting the monitor to take effect.

  • See performance considerations about custom scripts here.

Notification Considerations

  • Template variables are context-dependent:

    • Event-triggered notifications only populate event variables.

    • Function-triggered notifications only populate function variables.

    • Mixing contexts results in empty values.

  • Custom script notifications have additional considerations:

    • Scripts receive monitor match data and arguments as JSON input

    • Scripts must complete within their configured timeout_ms or they will be terminated

    • Script modifications require monitor restart to take effect

    • Supported languages are limited to Python, JavaScript, and Bash

Testing

Running Tests

RUST_TEST_THREADS=1 cargo test
RUST_TEST_THREADS=1 cargo test properties
RUST_TEST_THREADS=1 cargo test integration

Coverage Reports

Generate an HTML coverage report:

RUST_TEST_THREADS=1 cargo +stable llvm-cov --html --open

Generate a coverage report in the terminal:

RUST_TEST_THREADS=1 cargo +stable llvm-cov

Support

For support or inquiries, contact us on Telegram.

License

This project is licensed under the GNU Affero General Public License v3.0 - see the LICENSE file for details.

Security

For security concerns, please refer to our Security Policy.