Monitor
Monitors allow you to gain full visibility into your smart contracts' risks and behaviors. You can detect threats, get alerts on threats and anomalies, and automatically respond and resolve issues.
Use cases
-
Monitor crucial events and authorized functions like ownership transfers, pauses, or mints.
-
Alert on potentially dangerous transactions or operational issues.
-
Integrate notifications to Slack, Telegram, Discord, email, PagerDuty, Opsgenie or custom APIs.
-
Use pre-built templates to setup monitoring quickly and easily.
-
Combine with other Defender modules to execute on-chain transactions with monitor triggers.
Monitors
Monitors can be created from scratch or from templates, which are designed for common use cases. Templates automatically pre-populate a monitor, so you can easily modify and re-adapt them.
Monitors are organized into five categories, according to the type of risk they monitor:
-
Governance
-
Access Control
-
Suspicious Activity
-
Financial
-
Technical
You can also specify a severity level, which your team can use to group or filter monitors by.
-
High Severity
-
Medium Severity
-
Low Severity
Configuring a Monitor
A monitor watches all on-chain contract transactions and notifies when one matches the parameters, filters, or events.
At this time, Monitors are supported across all networks except Fantom. |
General Information
-
Name: The name assigned to the monitor.
-
Risk Category: The risk category of the monitor, useful for filtering or grouping.
-
Contracts: The smart contracts to monitor. Monitoring relies on the underlying ABI, so you may only have 1 ABI per monitor. In case of multiple smart contracts, the monitor must adhere to the same ABI/Interface.
-
Confirmation Blocks: If you want to be notified only after a certain level of confidence that the transaction is accepted, a higher confirmation block level is recommended, but if you want to be notified as soon as possible with tolerance to reorganizations, a lower confirmation block level is better. In chains where safe and finalized block tags are accepted, you can also select them as confirmation block level.
Matching Rules
For a transaction to trigger a notification, it must satisfy ALL of the following:
-
Transaction MUST have a To, From, or Address (from the log) that matches the configured address.
-
If a Transaction Filter is specified
-
The Transaction MUST match the Transaction Filter.
-
-
If Events are selected
-
The Transaction MUST emit any of the selected Events and match the Event Condition (if any)
-
-
If Functions are selected
-
The Transaction MUST directly invoke any of the selected Functions (contract calls are not detected at the moment) and match the Function Condition (if any)
-
Transaction Filters
Transaction filters allows to narrow the transactions being monitored. These are entered as property expressions or Javascript code, offering great flexibility. To accommodate comparisons for checksum and non-checksum addresses, comparisons are case-insensitive.
To receive ALL transactions that involve the selected events/functions, transaction conditions should not be specified. |
-
Conditions can use AND, OR, NOT and ()
-
Conditions can use ==, <, >, >=, <= to compare
-
Number values can be referred to by Hex (0xabc123) or Decimal (10000000000)
-
String values can only be compared via ==
-
Includes basic math operators: +, -, *, /, ^
If a transaction filter condition is specified, then a transaction MUST meet this condition in order to trigger a notification. |
Transaction Conditions can refer to the following properties
-
to is the to address for the transaction
-
from is the from address for the transaction
-
gasPrice is the price of gas sent in the transaction. In EIP1559 transactions, it’s equal to or below the
maxFeePerGas
. -
maxFeePerGas is the maximum price the transaction was willing to pay for the transaction. Only existent in EIP1559 transactions.
-
maxPriorityFeePerGas is the maximum amount of wei over the
BASE_FEE
the transaction is willing to pay to the miner for inclusion. Only existent in EIP1559 transactions. -
gasLimit is the gas limit sent in the transaction
-
gasUsed is the amount of gas used in the transaction
-
value is the value sent in the transaction
-
nonce is the nonce for the specific transaction
-
status is a derived value and can be compared with "success" or "failed"
Example Conditions
Transactions that are reverted
status == "failed"
Transactions excluding those from 0xd5180d374b6d1961ba24d0a4dbf26d696fda4cad
from != "0xd5180d374b6d1961ba24d0a4dbf26d696fda4cad"
Transactions that have BOTH a gasPrice higher than 50 gwei AND a gasUsed higher than 20000
gasPrice > 50000000000 and gasUsed > 20000
Custom Filters
Custom filters supports custom code for filtering transactions. If a custom filter is specified, it will be called with a list of matches found for a given block. This allows the monitor to use other datasources and custom logic to evaluate whether a transaction matches.
Only transactions that match other conditions (event, function, transaction) will invoke the custom filter. |
Each invocation can contain up to 25 transactions. |
Request Schema
The request body will contain the following structure. The MonitorConditionRequest
type from the defender-sdk-action-client package can be used for custom filters in Typescript.
{
"events": [
{
"hash": "0xab..123", // the transaction hash
"timestamp": "1699857792", // the timestamp of the transaction (block)
"blockNumber": 18561272, // the block number of the transaction
"blockHash": "0xab..123", // block hash from where this transaction was seen
"transaction": { // eth_getTransactionReceipt response body
... // see https://eips.ethereum.org/EIPS/eip-1474
"cumulativeGasUsed": "0xc3614",
"effectiveGasPrice": "0x6e214d78",
"gasUsed": "0x6075",
"logs": [
{ ... }
],
"logsBloom": "0x4..0",
"status": "0x1",
"from": "0xab..123",
"to": "0xab..123",
"transactionHash": "0xab..123",
"transactionIndex": "0x9",
"type": "0x2"
},
"matchReasons": [ // the reasons why monitor triggered
{
"type": "event", // event, function, or transaction
"address": "0x123..abc", // address of the event emitting contract
"signature": "...", // signature of your event/function
"condition": "value > 5", // condition expression (if any)
"args": ["5"], // parameters by index (unnamed are present)
"params": { "value": "5" } // parameters by name (unnamed are not present)
}
],
"matchedAddresses": ["0xabc..123"], // the addresses from this transaction your are monitoring
"matchedChecksumAddresses": ["0xAbC..123"], // the checksummed addresses from this transaction your are monitoring
"monitor": {
"id": "44a7d5...31df5", // internal ID of your monitor
"name": "Monitor Name", // name of your monitor
"abi": [...], // abi of your addresses (or undefined)
"addresses": ["0x000..000"], // addresses your monitor is watching
"confirmBlocks": 0, // number of blocks monitor waits (can be 'safe' or 'finalized' on PoS clients)
"network": "rinkeby" // network of your addresses
"chainId": 4 // chain Id of the network
},
"metadata": "..." // metadata (if available)
}
]
}
Response Schema
The custom filter must return a structure containing all matches. Returning an empty object indicates no match occurred. The type for this object is MonitorConditionResponse
.
Errors will be treated as a non-match. |
{
"matches": [
{
"hash": "0xabc...123", // transaction hash
"metadata": {
"foo": true // any object to be shared with notifications
}
},
{
"hash": "0xabc...123" // example with no metadata specified
}
]
}
Example Custom Filters
exports.handler = async function(payload) {
const conditionRequest = payload.request.body;
const matches = [];
const events = conditionRequest.events;
for(const evt of events) {
// add custom logic for matching here
// metadata can be any JSON-marshalable object (or undefined)
matches.push({
hash: evt.hash,
metadata: {
"id": "customId",
"timestamp": new Date().getTime(),
"numberVal": 5,
"nested": { "example": { "here": true } }
}
});
}
return { matches }
}
Events and Functions
Events and functions can be selected as filters. Selecting multiple events acts as an OR clause (triggered for ANY selected events). The same applies for functions.
Conditions for events or functions can further narrow the monitor. These can refer to arguments in the signature either by name (if the argument is named) or by index (e.g., $0, $1…). The variables must match the types shown in the interface. If left empty, the condition will be ignored.
If no events or functions are specified, then ALL transactions to or from the contracts will be in scope. |
Monitors seamlessly supports notifications for events emitted by a smart contract on all networks, regardless of whether they are triggered directly or through internal calls from a third contract. However, the capability to track and provide notifications for internal function calls within a contract is currently limited to the Ethereum mainnet. |
Example Conditions
Transactions that emit a Transfer(…)
event with a value between 1 and 100 ETH (in hex)
// Event Signature: Transfer(address to, address from, uint256 value)
value > 0xde0b6b3a7640000 and value < 0x56bc75e2d63100000
Transactions that emit a ValsEvent(…)
event with an array with a first element equal to 5
// Event Signature: ValsEvent(uint256[3] vals)
vals[0] == 5
Transactions that invoke a greet(…)
function with an unnamed string of "hello"
// Function Signature: greet(address, string)
$1 == "hello"
Configuring a Forta Monitor
A Forta Monitor watches Forta Alerts that come from a given Forta Bot or affect a given address, and notifies when one matches the parameters or conditions.
General Information
-
Name: The name assigned to the monitor.
-
Risk Category: The risk category of the monitor, useful for filtering or grouping.
-
Contracts: The smart contracts or EOAs to monitor.
-
Forta Bot IDs: The Forta Bots to monitor. Specify the Forta Bot IDs to monitor alerts from. These can be comma separated to specify multiple bots.
Forta Local Mode Monitors do not have the option to specify Bot IDs as scanner nodes running in local mode don’t support Bot IDs |
Matching Rules
For a Forta Alert to trigger a notification, it must satisfy ALL of the following:
-
Forta Alert MUST come from one of the Bot IDs specified and/or one of the Addresses specified
-
If a Alert IDs are specified
-
The Alert ID from the alert MUST match one of the Alert IDs specified.
-
-
If Minimum Severity is specified
-
The Severity MUST match or be greater severity than the one specified
-
-
If Minimum Scanner Confirmations is specified
-
The Scanner Confirmations MUST match or be of greater value than the one specified
-
Custom Filters
Custom filters supports custom code for filtering Forta Alerts. If a custom filter is specified, it will be called with a list of Alerts found. This allows the monitor to use other datasources and custom logic to evaluate whether an Alert matches. For more info see the Request Schema.
Alert Conditions
When configuring alert conditions, you can specify various criteria to ensure that only relevant alerts trigger notifications. Below are the key alert conditions you can set:
Minimum Severity
Severity indicates the impact level of an alert. By setting a minimum severity, you can filter alerts so that only those with a specified level of importance or higher will trigger notifications. Selecting a minimum severity acts as a threshold, ensuring that only alerts meeting or exceeding this level are monitored.
Minimum Scanner Confirmations
Scanner Confirmations refer to the number of scanner nodes that have verified a Forta Alert. By specifying a minimum number of confirmations, you ensure that an alert is validated by multiple nodes before triggering a notification
Alert IDs
An Alert ID is a unique string that identifies a specific class of findings. You can specify one or more Alert IDs to monitor for particular types of alerts. Multiple Alert IDs can be separated by a comma, allowing you to monitor various alert classes simultaneously
If no Severity, Scanner Confirmations, or Alert IDs are specified, the Sentinel will monitor all alerts that match your specified Bot IDs and/or Addresses. |
Forta Local Mode Monitor
Forta offers the ability to run scanner nodes locally, this can be useful for running only specific detection bots and handling alerts internally instead of publishing to the network. You can monitor scanner nodes running in Local Mode with Defender by creating a new Forta Monitor with Local Mode selected and supplying the public key of your scanner node. After creation your new Monitor will have a Webhook URI, you can add this to your scanner node config file to forward alerts to Defender. You can find more information on Local Mode scanner nodes in the Forta Docs.
Alerts
A monitor can use any supported notification channel for alerting. It’s also possible to connect an Action or a Workflow that should run with the monitor.
To prevent repeated alerts from individual monitors and control the notification rate, you can use the Alert Threshold and Minimum time between consecutive notifications fields.
-
Alert Threshold: Define the number of times a monitor must trigger per unit of time before a notification is sent or an action is fired. The unit of time is defined by the Minimum time between consecutive notifications field.
-
Minimum time between consecutive notifications: Set the the minimum wait time between sending notifications.
Notifications
You can create Notifications attached to the Monitor alerts for getting notified about the on-chain events across many channels, see more about how to create and configure them in this section.
Customizing Notification
You can also modify the message body content and formatting using the Customize notification checkbox below the notification channel selector.
Preview
*Monitor Name*
Monitor
*Network*
rinkeby
*Block Hash*
0x22407d00e953e5f8dabea57673b9109dad31acfc15d07126b9dc22c33521af52
*Transaction Hash*
0x1dc91b98249fa9f2c5c37486a2427a3a7825be240c1c84961dfb3063d9c04d50
https://rinkeby.etherscan.io/tx/0x1dc91b98249fa9f2c5c37486a2427a3a7825be240c1c84961dfb3063d9c04d50[Block Explorer]
*Match Reason 1*
_Type:_ Function
_Matched Address_:_ 0x1bb1b73c4f0bda4f67dca266ce6ef42f520fbb98
_Signature:_ greet(name)
_Condition:_ name == 'test'
_Params:_
name: test
*Match Reason 2*
_Type:_ Transaction
_Condition:_ gasPrice > 10
*Value*
0x16345785D8A0000
Message Syntax
Custom notifications support a limited set of markdown syntax:
-
Bold (**this text is bold**)
-
Italic (*this text* and _this text_ are italic)
-
Links (this is a [link](https://example.com))
There is partial support for additional markdown syntax, but rendering behavior varies by platform. Email supports full HTML and has the richest feature set, but other messaging platforms have limitations, including support for standard markdown features such as headings, block quotes, and tables. Combinations of the supported features (e.g., bold and italicized text) also have mixed support. A warning message will appear directly below the editor if the markdown contains any syntax with mixed platform support.
Monitor Event Schema
You can access the following schema when using custom notification templates. This schema is also passed to the Action if you configure your monitor to execute one.
{
"transaction": { // eth_getTransactionReceipt response body
... // see https://eips.ethereum.org/EIPS/eip-1474
"cumulativeGasUsed": "0xc3614",
"effectiveGasPrice": "0x6e214d78",
"gasUsed": "0x6075",
"logs": [
{ ... }
],
"logsBloom": "0x4..0",
"status": "0x1",
"from": "0xab..123",
"to": "0xab..123",
"transactionHash": "0xab..123",
"transactionIndex": "0x9",
"type": "0x2"
},
"blockHash": "0xab..123", // block hash from where this transaction was seen
"matchReasons": [ // the reasons why monitor triggered
{
"type": "event", // event, function, or transaction
"address": "0x123..abc", // address of the event emitting contract
"signature": "...", // signature of event/function
"condition": "value > 5", // condition expression (if any)
"args": ["5"], // parameters by index (unnamed are present)
"params": { "value": "5" } // parameters by name (unnamed are not present)
}
],
"matchedAddresses":["0x000..000"] // the addresses from this transaction monitored
"monitor": {
"id": "44a7d5...31df5", // internal ID of monitor
"name": "Monitor Name", // name of monitor
"abi": [...], // abi of address (or undefined)
"addresses": ["0x000..000"], // addresses monitored
"confirmBlocks": 0, // number of blocks monitor waits (can be 'safe' or 'finalized' on PoS clients)
"network": "rinkeby" // network of address
"chainId": 4 // chain Id of the network
},
"value": "0x16345785D8A0000" // value of the transaction
"metadata": {...} // metadata injected by action condition (if applicable)
}
Dynamic Content
Custom notification templates render dynamic content using inline templating. Any string surrounded by double curly braces will be resolved against the Event Schema. Deeply nested items (including those in arrays) can be accessed using dot notation.
In addition to the standard event schema, the following parameters are injected for usage in custom notification messages:
-
transaction.link
-
matchReasonsFormatted
Settings
In the settings tab, you can specify the default notification channel associated with the different severities: High, Medium, or Low.
Pausing and Deleting
From the monitor page, you can pause created monitors that are active. By clicking on the dotted button on the card, you can delete or save the monitor as a template.
Saving a monitor as a template stores its configuration and parameters, which can be found by clicking the template gallery tab.
We provide a quickstart tutorial to monitor a smart contract using Defender. Check it out here! |