Getting Event Schema from Sentinel -> Autotask?

I wanted to see if I could have a Sentinel pick up the details of an event and pass them to the Autotask as variables in some fashion. I was going through documentation and noticed Autotask Event has an Event Schema that outlines details of the trigger event. I am a little unclear on how this is passed to the autotask handler. The autotask seems to take 1 parameter (in my case I am just using 'credentials' as my function input). I use it to establish a provider and a signer.

In the autotask example in the sentinel documentation, there is a function input to the handler called (params) which pulls the data using params.request.body ?

Does the autotask take multiple inputs to accomplish both having a web3 provider/signer along with getting sentinel event details?

I am a little lost on what is exactly being passed through to the handler and what I can use at my disposal within the autotask handler. A little clarity on that would be really helpful

Hey @Guceri! Apologies for the confusion, there are several moving pieces here.

An Autotask will always receive a single event parameter.

  • If there is a Relayer connected to it, the event will have a set of properties (namely credentials and relayerARN) that can be used for creating the provider and signer.
  • If the autotask is invoked by a Sentinel trigger, then event will have a request.body property with the info of the tx that was matched in that block.

If it helps, I'm attaching here the type definition for the autotask event from the defender-autotask-utils package:

/**
 * Event information injected by Defender when invoking an Autotask
 */
export interface AutotaskEvent {
  /**
   * Internal identifier of the relayer function used by the relay-client
   */
  relayerARN?: string;

  /**
   * Internal identifier of the key-value store function used by the kvstore-client
   */
  kvstoreARN?: string;

  /**
   * Internal credentials generated by Defender for communicating with other services
   */
  credentials?: string;

  /**
   * Read-only key-value secrets defined in the Autotask secrets vault
   */
  secrets?: AutotaskSecretsMap;

  /**
   * Contains a Webhook request, Sentinel match information, or Sentinel match request
   */
  request?: AutotaskRequestData;
}

/**
 * Key-value secrets defined in the Autotask secrets vault
 */
export interface AutotaskSecretsMap {
  [k: string]: string;
}

/**
 * Autotask request data injected by Defender based on the type of trigger
 */
export interface AutotaskRequestData {
  /**
   * Main payload of the request: can be a generic object for a webhook request, or a typed request from Sentinels
   */
  body?: SentinelConditionRequest | SentinelTriggerEvent | Record<string, unknown>;

  /**
   * Query parameters of the webhook request
   */
  queryParameters?: { [name: string]: string };

  /**
   * HTTP headers parameters of the webhook request (only headers starting with 'X-' are received)
   */
  headers?: { [name: string]: string };
}

/**
 * Payload injected by a Sentinel when using an Autotask as a condition.
 *
 * Note that the Autotask should return a SentinelConditionResponse corresponding to the matched txs.
 */
export interface SentinelConditionRequest {
  /**
   * All potential matches to be evaluated by the autotask
   */
  events: SentinelTriggerEvent[];
}

/**
 * To be returned by an Autotask when invoked as a Sentinel condition via a SentinelConditionRequest to refine a match
 */
export interface SentinelConditionResponse {
  /**
   * List of matches to be triggered by the Sentinel, must be included in the SentinelConditionRequest
   */
  matches: SentinelConditionMatch[];
}

/**
 * Match to be triggered by the Sentinel
 */
export interface SentinelConditionMatch {
  /**
   * Hash of the transaction to match
   */
  hash: string;

  /**
   * Optional user-defined metadata to include with the Sentinel notification
   */
  metadata?: { [k: string]: unknown };
}

/**
 * Represents an object matched by a Sentinel
 */
export interface SentinelTriggerEvent {
  hash: string;
  timestamp: number;
  blockNumber: string;
  blockHash: string;
  transaction: EthReceipt;
  matchReasons: SentinelConditionSummary[];
  sentinel: SentinelSubscriberSummary;
  metadata?: { [k: string]: unknown };
}

/**
 * Summary of a Sentinel definition
 */
export interface SentinelSubscriberSummary {
  id: string;
  name: string;
  network: string;
  address: string;
  confirmBlocks: number;
  abi: Record<string, unknown> | undefined;
}

interface SentinelBaseConditionSummary {
  condition?: string;
}

interface SentinelBaseAbiConditionSummary extends SentinelBaseConditionSummary {
  signature: string;
  args: any[];
  params: { [key: string]: any };
}

interface EventConditionSummary extends SentinelBaseAbiConditionSummary {
  type: 'event';
}

interface FunctionConditionSummary extends SentinelBaseAbiConditionSummary {
  type: 'function';
}

interface InternalFunctionConditionSummary extends SentinelBaseAbiConditionSummary {
  type: 'internal-function';
}

interface TransactionConditionSummary extends SentinelBaseConditionSummary {
  type: 'transaction';
}

/**
 * Summary of a user-defined Sentinel condition
 */
export type SentinelConditionSummary =
  | TransactionConditionSummary
  | InternalFunctionConditionSummary
  | FunctionConditionSummary
  | EventConditionSummary;

/**
 * Ethereum transaction receipt
 */
export interface EthReceipt {
  transactionHash: string;
  transactionIndex: string;
  contractAddress: string | null;
  blockHash: string;
  blockNumber: string;
  from: string;
  to: string;
  cumulativeGasUsed: string;
  gasUsed: string;
  logs: EthLog[];
  logsBloom: string;
  status: string;
}

/**
 * Ethereum transaction event log
 */
export interface EthLog {
  address?: string;
  blockHash: string;
  blockNumber: string;
  data: string;
  logIndex: string;
  removed: boolean;
  topics: string[];
  transactionHash: string;
  transactionIndex: string;
}

The goal here is just to get the event logs from an event that was emitted to the blockchain (which is the event the sentinel is looking for). Using the sentinel to invoke the autotask, I passed 'credentials' into the autotask handler function as my function input, I logged the output as follows:

exports.handler = async function(credentials) {
  console.log(credentials.request.body)
  //logic below
}

And the return output of this was:

AUTOTASK START
2021-07-29T14:40:03.770Z	INFO	{
  events: [
    {
      hash: '0x8ebd920fe5e23211fe4b3f5b899d6d321e7efd106dc89e205e05369f9bc7c016',
      transaction: [Object],
      blockHash: '0xe2b932c3aaf0b70acd47e782876d482e844a9ce68b6044bd690690212f756943',
      blockNumber: '0x102dd40',
      timestamp: 1627569596,
      matchReasons: [Array],
      sentinel: [Object]
    }
  ]
}

I was expecting that the transaction object would return the receipt of the transaction in which I could get the emitted event logs, but I am just getting an empty object.

Note that the transaction: [Object] is just related to how console.log works in node: it won't expand objects/arrays that are nested too deep. Try console.log(JSON.stringify(redentials.request.body, null, 2)) instead and you'll see the contents of the transaction object!

@spalladino It works, thank you! This is very useful so we can use event details to tailor the autotask accordingly.

console.log(JSON.stringify(credentials.request.body, null, 2))
1 Like

@spalladino I am able to see the data I want using the solution you presented. Thank you. I tried to drill down further into the data and I was not able to actually extract the event parameters I want.

I believe the path for what I am looking for is within :

credentials.request.body.matchReasons

But when I console log that to doublecheck the values, I get 'undefined'

console.log(JSON.stringify(credentials.request.body.matchReasons, null, 2))

Ultimately, I just want to be able to store the "params" object that is within matchReasons so I can reference it in my autotask (which are the event parameters). I am just trying to console log it to see the values first before assigning it to a variable.

  "matchReasons": [               // the reasons why sentinel triggered
    {
      "type": "event",            // event, function, or transaction
      "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)
      "metadata": {...}           // metadata injected by Autotask Condition (if applicable)
    }
  ]

I was using the path that is specified in the docs:

exports.handler = async function(params) {
  const payload = params.request.body;
  const matchReasons = payload.matchReasons;
  //additional logic
}

From the console.log output you shared, the full path should be event.request.body.events[0].matchReasons. Note that the path differs depending on whether you have set up your Autotask as a condition or a notification: in the former (which seems to be your case), you have a collection of event.request.body.events with potentially multiple txs to evaluate, while in the latter the autotask will be called with the tx info in event.request.body directly.

Thank you for bearing with me here. This makes more sense and I was able to come out with a solution. In the case that anyone is looking for something similar, I was able to extract the emitted event parameters as follows when using an autotask as a condition in the sentinel:

exports.handler = async function(credentials) {
  const params = credentials.request.body.events[0].matchReasons[1].params
  const myValue = params.myValue
}