Writing Rhai Scripts
Argus is using the Rhai scripting language for its filtering logic. Each monitor you define has a filter_script that determines whether a given transaction or log should trigger a notification.
Why Rhai?
The choice of Rhai as the scripting language for Argus was deliberate, focusing on providing a powerful yet safe filtering experience. Here are the key reasons:
- Performance: Rhai is a lightweight and fast scripting engine. Scripts are compiled to an efficient bytecode format before execution, making it highly suitable for the repetitive, high-throughput task of evaluating thousands of blockchain events.
- Safety: Rhai is designed to be sandboxed. User-provided scripts run in a controlled environment and cannot access the file system, network, or other system resources. This ensures that a poorly written or malicious script cannot compromise the stability or security of the Argus service.
- Ease of Use: The syntax is relatively simple and should feel familiar to anyone with experience in languages like JavaScript, Python, or Rust, lowering the learning curve for writing effective filters.
- Seamless Rust Integration: As a Rust-native scripting language, Rhai integrates cleanly and efficiently into the Argus codebase, allowing for tight and performant interaction between the core application and the filtering logic.
The filter_script
The filter_script is a short piece of Rhai code that must evaluate to a boolean (true or false).
- If the script evaluates to
true, the monitor is considered a "match," and a notification is sent to its configured actions. - If the script evaluates to
false, no action is taken.
Example: Simple ETH Transfer
This script triggers if a transaction's value is greater than 10 ETH.
tx.value > ether(10)
Example: Specific ERC20 Transfer Event
This script triggers if a log is a Transfer event and the value parameter of that event is greater than 1,000,000 USDC.
log.name == "Transfer" && log.params.value > usdc(1_000_000)
Data Types, Conversions, and Custom Filters in Rhai
When working with blockchain data in Rhai scripts, it's crucial to understand how data types are handled, especially for large numerical values, and how to use conversion functions for accurate comparisons and calculations.
Handling Large Numbers (BigInts)
EVM-compatible blockchains often deal with very large numbers (e.g., token amounts in Wei, Gwei, or other base units) that exceed the capacity of standard 64-bit integers. In Argus's Rhai environment, these large numbers are typically represented as BigInt strings.
To perform mathematical comparisons or operations on these BigInt strings, you must use the provided conversion helper functions. These functions convert the BigInt string into a comparable decimal representation.
Conversion Helper Functions
Argus provides several built-in helper functions to facilitate these conversions:
-
ether(amount): Converts a decimalamount(e.g.,10.5) into its equivalentBigIntstring in Wei (18 decimal places).tx.value > ether(10) // Checks if tx.value (BigInt string) is greater than 10 ETH -
gwei(amount): Converts a decimalamountinto its equivalentBigIntstring in Gwei (9 decimal places).tx.gas_price > gwei(50) // Checks if gas_price (BigInt string) is greater than 50 Gwei -
usdc(amount): Converts a decimalamountinto its equivalentBigIntstring for USDC (6 decimal places).log.params.value > usdc(1_000_000) // Checks if log.params.value (BigInt string) is greater than 1,000,000 USDC -
wbtc(amount): Converts a decimalamountinto its equivalentBigIntstring for WBTC (8 decimal places).log.params.value > wbtc(0.5) // Checks if log.params.value (BigInt string) is greater than 0.5 WBTC -
decimals(amount, num_decimals): A generic function to convert a decimalamountinto aBigIntstring with a specified number of decimal places.log.params.tokenAmount > decimals(100, 0) // Checks if tokenAmount (BigInt string) is greater than 100 with 0 decimals
Important: Always use these conversion functions when comparing or performing arithmetic with tx.value, log.params.value, or other BigInt string representations of token amounts to ensure correct logic. If you are unsure when to apply a conversion, use the dry-run feature to verify your script works as expected.
The Data Context
Within your script, you have access to a rich set of data about the on-chain event. The data available depends on the type of monitor you have configured.
- For all monitors, you have access to the
txobject, which contains details about the transaction. - For monitors with an
addressandabi, you also have access to thelogobject, which contains the decoded event log data.
For a detailed breakdown of the available data, see the following pages:
Scripting Best Practices
- Keep it Simple: Your script is executed for every relevant transaction or log. Keep it as simple and efficient as possible.
- Be Specific: The more specific your filter, the fewer false positives you'll get.
- Use Helpers: Use the provided helper functions (
ether,usdc,gwei,decimals) to handle token amounts. This makes your scripts more readable and less error-prone. - Test with
dry-run: Before deploying a new or modified monitor, use thedry-runCLI command to test your script against historical data. This will help you verify that it's working as expected.
For more practical examples of Rhai scripts, refer to the Example Gallery.