Monitoring Large Deposits with Zephyr and Sending Web Alerts

In this tutorial, we'll create a Zephyr program that monitors deposit events for a specific contract and sends a web request when a deposit exceeds a certain threshold.

Prerequisites:

  • A Mercury account

  • Zephyr SDK and Mercury CLI installed

  • Basic knowledge of Rust and Soroban

Step 1: Set up the project

Create a new Zephyr project using the Mercury CLI:

mercury-cli --key "" new-project --name large-deposit-monitor

the --key "" flag is temporarily needed as we're updating the Mercury tooling.

Step 2: Configure the project

Open the src/lib.rs file and add the following imports that we will use and constants at the top:

use stellar_strkey::Contract;
use zephyr_sdk::{soroban_sdk::Symbol, AgnosticRequest, EnvClient, PrettyContractEvent};

const CONTRACT_ID: &str = "YOUR_CONTRACT_ID_HERE";
const DEPOSIT_THRESHOLD: i128 = 1_000_000_000; // 100 XLM in stroops
const WEBHOOK_URL: &str = "https://your-webhook-url.com/endpoint";
const STROOP: i128 = 10_000_000;

Replace YOUR_CONTRACT_ID_HERE with the actual contract ID you want to monitor, and update WEBHOOK_URL with your desired endpoint for receiving alerts.

Step 3: Implement the main logic

Add the following on_close function to your lib.rs file:

#[no_mangle]
pub extern "C" fn on_close() {
    let env = EnvClient::new();
    let contract_id: [u8; 32] = Contract::from_string(CONTRACT_ID).unwrap().0;

    env.log().debug(
        format!("Monitoring events for contract: {}", CONTRACT_ID),
        None,
    );

    let events: Vec<PrettyContractEvent> = env
        .reader()
        .pretty()
        .soroban_events()
        .into_iter()
        .filter(|x| x.contract == contract_id)
        .collect();

    for event in events {
        let action = env.from_scval::<Symbol>(&event.topics[0]);
        let amount = env.from_scval::<i128>(&event.data);

        if action == Symbol::new(&env.soroban(), "deposit") && amount > DEPOSIT_THRESHOLD {
            let formatted_amount = format_amount(amount);
            let source = Contract(event.contract).to_string();
            send_large_deposit_alert(&env, &formatted_amount, &source);
        }
    }
}

This function does the following:

  1. Initializes the Zephyr environment.

  2. Converts the contract ID to the required format, in order to compare it to the PrettyContractEvent.contract filed.

  3. Filters events for the specific contract we're monitoring. Notice that we're using env.reader().pretty() to directly access the event fields.

  4. Checks each event to see if it's a "deposit" event and if the amount exceeds our threshold.

  5. If conditions are met, it calls a function to send an alert.

Step 4: Implement helper functions

Add these helper functions to your lib.rs file:

fn send_large_deposit_alert(env: &EnvClient, amount: &str, source: &str) {
    let message = format!("Large deposit of {} XLM detected from {}", amount, source);

    env.send_web_request(AgnosticRequest {
        body: Some(message.clone()),
        url: WEBHOOK_URL.into(),
        method: zephyr_sdk::Method::Post,
        headers: vec![
            ("Content-Type".into(), "application/json".into()),
            ("X-Alert-Type".into(), "LargeDeposit".into()),
        ],
    });

    // Log the attempt
    env.log().debug(format!("Attempted to send alert for deposit: {}", message), None);
    
    // If the request is successful it will be showed in your program logs on the Mercury app
}

fn format_amount(amount: i128) -> String {
    format!("{}.{:07}", amount / STROOP, amount % STROOP)
}

The send_large_deposit_alert function sends a web request with the alert information, while format_amount helps format the XLM amount properly.

Step 5: Deploy and test

Deploy your Zephyr program using the Mercury CLI (testnet deploy):

mercury-cli --jwt $MERCURY_JWT --local false --mainnet false deploy

To test your program:

  1. Set up a webhook receiver (e.g., using a service like RequestBin or a simple server you control).

  2. Make sure your contract is emitting "deposit" events.

  3. Trigger a deposit larger than the threshold (100 XLM in this case) to the monitored contract.

  4. Check your webhook receiver for incoming alerts.

  5. Review the Zephyr program logs in the Mercury dashboard for any debug messages or errors.

Conclusion

This tutorial demonstrated how to use Zephyr to:

  1. Monitor specific events from a Stellar smart contract.

  2. Filter and process these events efficiently.

  3. Send web alerts for large deposits.

  4. Use Zephyr's logging capabilities for debugging and monitoring.

You can extend this concept to monitor various types of events or implement more complex logic based on your specific needs. Remember to handle potential network issues and implement retries if necessary in a production environment.

Last updated