# Overview

LitListenerSDK.

<figure><img src="/files/UO7rNH4jyFnxTqPx0yzu" alt=""><figcaption></figcaption></figure>

The **LitListenerSDK** is designed around the principle of conditionally pre-approved chain reactions with account abstraction.&#x20;

**What are chain reactions?**

We call them **circuits**. Because logically, that's what they are. You have a series of things you want to do in an encrypted and decentralized context. But you shouldn't need to be hovering over those actions to make sure they happen, when and how they are supposed to.

This series of actions encrypted, conditional, often on-chain, can be chained together to trigger complex strategies. Circuits are a simple way to represent them.

***

**Here's a few examples of what you can do with them:**

* **Supply Chain Management**

  You use the SDK to listen for a smart contract event, signaling when a shipped item has reached its destination (verified by a trusted third-party oracle).

  If the item ID matches the expected value, the SDK triggers an on-chain function. It releases payment to the supplier, and updates the product's status in the supply chain smart contract.

  \
  Off-chain, it sends a notification to the end customer informing them that their product has arrived.
* **Gaming / Collectibles**

  AAA web3 games are on the horizon. You can use the SDK to listen for events which herald fully autonomous game worlds. Signaling when a player has achieved a milestone, or found a rare item, is just the start.<br>

  When events are detected, it can trigger an on-chain function that mints unique NFTs, and assigns them to pre-approved addresses for players or spectators.<br>

  Off-chain, you can use the SDK to send a notification to players, and update their leaderboard status.
* **Web3 Fashion**\
  When a new fashion line, or streetwear drop, is released, you can use the SDK to be on the lookout for it ahead of time. Helping you be first in line.<br>

  It can trigger an on-chain function to place a bid, or make a direct purchase on your behalf, if items in the collection match the pre-approved preferences set by the collector.<br>

  Off-chain, the SDK can update a collector's virtual wardrobe, or send a notification about the successful score.
* **DeFi**\
  The circuit you’ve set through the SDK listens for a specific webhook event related to market prices (from a trusted API).<br>

  When the price of a chosen token satisfies a pre-approved threshold, the SDK fires an on-chain function. It triggers a trade, or a series of them, on a decentralized exchange.<br>

  The SDK can also be used to set an off-chain function to send notifications to the user, with information about the completed trade.
* **Web3 Social Media**\
  You can also use the SDK to improve your social media experience. By listening for events related to new posts, new likes, and other activity, from accounts you set as worth following more closely than a default friend or follow button can offer on its own.<br>

  When a new event is detected, it can trigger an on-chain function to tip a creator with tokens, if the content matches the user's pre-approved interests, or set off more elaborate chain reactions.<br>

  Off-chain, it can mark a post as 'interacted with' or 'liked' in the user's personalized feed.

  Bringing programmability to like buttons everywhere, makes the difference between decentralized vs archaic social media as clear and simple as a status update.

<figure><img src="/files/M8O3tCmAHstayZtMyWrb" alt=""><figcaption></figcaption></figure>

**And wtf is account abstraction?**

Account abstraction is about eliminating confusion. Instead of asking you to juggle terms like "wallets" and "accounts", it presents a simple platform where you can manage your assets, communicate with others, and maintain privacy.

You don't have to learn a new language, just to go online, or touch the grass outside.

**Programmable Key Pairs (PKPs)**&#x20;

The LitListenerSDK lets you mint [PKPs on Lit Protocol's Chronicle network](https://developer.litprotocol.com/pkp/intro/) and verifiably assign these PKPs to Lit Actions to set and run on-chain functions (with Javascript).

You can create your own PKPs on Lit Protocol's Chronicle network. PKPs are self-contained tools combining programmable logic (hence the name) with conditional signatures that you can use to approve transactions, and other messages, online. And since almost everything is connected online today, that means IRL too.&#x20;

What makes PKPs special is that they aren't made or maintained by just one entity. They're the result of teamwork in the Lit network, with multiple nodes contributing a piece of the keys, but no one having the whole thing. It's like everyone has a part of the secret recipe, but no one can make the dish on their own unless they meet the conditions spelled out by your pre-approval.

PKPs are managed by a unique token on the Chronicle network. If your wallet holds this token, you can ask the Lit network to combine the key pieces and sign any transaction or message on your behalf.

**And Lit Actions?**

Lit Actions are like a task list for your PKPs. Written in JavaScript, they tell your PKPs what transactions need to be confirmed. They're designed to work on any network where Lit Protocol operates, allowing you to perform tasks across different systems with the same set of instructions.

In our case, we pair Lit Actions with PKPs to manage our decentralized interactions. This allows us to operate smoothly and securely, without the need to know the nitty-gritty of each task.

***

## A Closer Look&#x20;

Your circuit remains dynamic and responsive through three core condition functions: **webhooks, on-chain events, and intervals.**

### 1. Webhooks

**Query and Monitor APIs and Webhooks on the Web:** Within the LitListenerSDK, webhooks play a role in connecting your circuit (series of actions) with external web services. The SDK is designed to query (request) and monitor information from specific APIs. In a blockchain context, this might include tracking price changes of a specific cryptocurrency or updates from a decentralized app.

**Match Returned Data Against Pre-Defined Operators and Values:** The data retrieved through the webhooks is analyzed to see if it meets the predetermined conditions you've set up in your circuit. If the conditions are met, the associated chain reaction is triggered within the LitListenerSDK. For example, if you've set up a condition to listen for a particular token's price reaching a specific threshold, the SDK will trigger the corresponding on-chain action once that condition is met.

### 2. On-Chain Events

**Subscribe to On-Chain Events Across Various Blockchain Networks:** The LitListenerSDK allows you to set up listeners for specific events occurring on the blockchain, like a contract being executed or a new block being added. By subscribing to these events, you're telling the SDK to keep an eye on particular occurrences within the blockchain networks that are relevant to your circuit.

**Match Returned Event Log Data Against Pre-Defined Operators and Values:** When one of the subscribed on-chain events occurs, the SDK analyzes the event's data, matching it against your predefined conditions. If there's a match, it will trigger the subsequent actions in the circuit. For instance, if your circuit is set to react to a successful transaction involving a specific NFT, once that transaction occurs, the next steps in the circuit are activated.

### 3. Intervals

**Specify the Monitor and Check Frequency for Webhooks and On-Chain Events:** Intervals within the LitListenerSDK act as the timing mechanism that governs how frequently the SDK checks the webhooks and on-chain events. By setting intervals, you determine how often the SDK will check for updates in the specified webhooks or on-chain events. If you need real-time reaction, you might set a short interval, whereas a less time-sensitive circuit might have longer intervals.


# Quick Start

The Quickest Route to Getting Started.

## Install the SDK

{% tabs %}
{% tab title="npm" %}

```
# Install via NPM
npm i lit-listener-sdk ethers
```

{% endtab %}

{% tab title="yarn" %}

```
# Install via yarn
yarn add lit-listener-sdk ethers
```

{% endtab %}
{% endtabs %}

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript">import { ethers, BigNumber } from "ethers";
import { Circuit } from "lit-listener-sdk";

const chronicleProvider = new ethers.providers.JsonRpcProvider("https://chain-rpc.litprotocol.com/http", 175177);
const chronicleSigner = new ethers.Wallet(YOUR_PRIVATE_KEY, chronicleProvider);

const quickStartCircuit = new Circuit(chronicleSigner);
<strong>
</strong><strong>quickStartCircuit.setConditions([
</strong> new ContractCondition(
    "0x6968105460f67c3bf751be7c15f92f5286fd0ce5", // contract address
    [
     {
      "anonymous": false,
      "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
       },
       {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
        }
          ],
          "name": "Transfer",
          "type": "event"
        },
      ], // abi
      "Transfer", // event name
      CHAIN_NAME.polygon, // chainId
      "https://your_provider_url_for_this_network", // provider URL
      ["to", "value"], // event name args
      ["0x6968105460f67c3bf751be7c15f92f5286fd0ce5",   
<strong>      BigNumber.from("500000")], // expected value
</strong>      "===", // match operator
      async () => { console.log("Matched!"); }, // onMatched function
      async () => { console.log("Unmatched!"); }, // onUnMatched function
      (error: Error) => { console.log("Error:", error); } // onError function,
), ]);
        
const {unsignedTransactionDataObject, litActionCode} = await quickStartCircuit.setActions([{
  type: "contract", 
  priority: 2, 
  contractAddress: "0x6968105460f67c3bf751be7c15f92f5286fd0ce5", 
  abi: [
     {
      constant: true,
      inputs: [{ name: "numberValue", type: "uint256" }],
      name: "your_function_name",
      outputs: [{ name: "", type: "uint256" }],
      payable: false,
      stateMutability: "external",
      type: "function",
      },
  ], 
  functionName: "your_function_name", 
  chainId: "polygon", 
  nonce: 1, 
  gasLimit: 100000,
  value: 0, 
  maxPriorityFeePerGas: 1000, 
  maxFeePerGas: 10000, 
  args: [20], 
};
]);

// Assuming you have already uploaded the Lit Action Code to IPFS and just need to retrieve the hash
const ipfsCID = await quickStartCircuit.getIPFSHash(litActionCode);
const { publicKey, tokenId, address } = await quickStartCircuit.mintGrantBurnPKP(ipfsCID);

await quickStartCircuit.start({publicKey, ipfsCID});
</code></pre>


# Instantiate Circuit

Create an Instance of your Circuit.

### Instantiate SDK:

{% hint style="info" %}
Minting a PKP requires an ethers signer with LIT Tokens on the <mark style="background-color:yellow;">Chronicle Lit Rollup network</mark>. If you only need to generate Lit Action code then an ethers signer object is not necessary.
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
import { ethers } from "ethers";
import { Circuit } from "lit-listener-sdk";

const chronicleProvider = new ethers.providers.JsonRpcProvider("https://chain-rpc.litprotocol.com/http", 175177);
const chronicleSigner = new ethers.Wallet(YOUR_PRIVATE_KEY, chronicleProvider);

const newCircuit = new Circuit(chronicleSigner);
```

{% endcode %}

The `signer` is an optional constructor parameter. If you are minting a PKP then you must set a `signer` with a Provider compatible with the Lit Chronicle Network, you can use the standard RPC URL found [here](https://developer.litprotocol.com/intro/rollup/#connecting-to-chronicle). You can also optionally pass in the PKP Contract to mint from if it is not `0x8F75a53F65e31DD0D2e40d0827becAaE2299D111`.


# Set Conditions

Set Contract Event and Webhook Conditions.

The `setConditions` method let's you specify and assign either webhook or contract event conditions to the circuit. When the specified conditions are met, the Lit Action code will be executed.

Each condition has a maximum retry limit of **3** for encountered errors before logging an **unmatched** condition and continuing the Circuit. See [Error Strict Mode](/errors-and-logs/error-strict-mode) and [Logs & Error Handling](/errors-and-logs/logs-and-error-handling) for a more in depth view of how errors are handled in the SDK.

When invoking this method, you provide an array of conditions that you want to set. These conditions are called either at the specified interval set in [`ConditionLogic`](/sdk-reference/conditional-logic) or monitored in real-time by the specified API endpoint and emitted contract events.

{% hint style="info" %}
SetConditions is optional. You can run the Circuit without specifying conditions.
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
import { CHAIN_NAME } from "lit-listener-sdk";
import { BigNumber } from "ethers";

newCircuit.setConditions(
    [
        new ContractCondition(
              "0x6968105460f67c3bf751be7c15f92f5286fd0ce5", // contract address
              [
                {
                  "anonymous": false,
                  "inputs": [
                    {
                      "indexed": true,
                      "internalType": "address",
                      "name": "to",
                      "type": "address"
                    },
                    {
                      "indexed": false,
                      "internalType": "uint256",
                      "name": "value",
                      "type": "uint256"
                    }
                  ],
                  "name": "Transfer",
                  "type": "event"
                },
              ], // abi
              CHAIN_NAME.polygon, // chainId
              "https://your_provider_url_for_this_network", // provider URL
              "Transfer", // event name
               ["to", "value"], // event name args
               ["0x6968105460f67c3bf751be7c15f92f5286fd0ce5", 
               BigNumber.from("500000")], // expected value
              "===", // match operator
              async (emittedValue) => { console.log("Value Emmited by the contract event",         emittedValue); }, // onMatched function
              async (emittedValue) => { console.log("Value Emmited by the contract event",         emittedValue); }, // onUnMatched function
              (error: Error) => { console.log("Error:", error); } // onError function,
        ), 
        new WebhookCondition(
          "https://api.example.com", // baseUrl
          "/endpoint", // endpoint
          "path.to.value", // responsePath
          20, // expected value
          "===", // match operator
          "my-api-key", // apiKey
              async (emittedValue) => { console.log("Value Emmited by the webhook event",         emittedValue); }, // onMatched function
              async (emittedValue) => { console.log("Value Emmited by the webhook event",         emittedValue); }, // onUnMatched function
          (error: Error) => { console.log("Error:", error); } // onError function,
        )  
    ]
)
```

{% endcode %}

**Webhook Condition Parameters:**

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript">/* The base URL of the webhook endpoint.*/
<strong>baseUrl: string;
</strong><strong>
</strong>/* The specific endpoint for the webhook.*/
endpoint: string;

/* The path to access the expected value in the response body.*/
responsePath: string;

/* The value to match against the emitted value.*/
expectedValue:  number | string | number[] | string[] | bigint | bigint[] | object | object[] | (string | number | bigint | object)[];

/* The operator used for the comparison. It must be one of the following: "&#x3C;", ">", "==", "===", "!==", "!=", ">=", "&#x3C;=".*/
matchOperator: "&#x3C;" | ">" | "==" | "===" | "!==" | "!=" | ">=" | "&#x3C;=";

/* Optional API key for authorization.*/
apiKey?: string;

/* A callback function to execute when the emitted value matches 
the expected value.*/
onMatched: (emittedValue:  number | string | number[] | string[] | bigint | bigint[] | object | object[] | (string | number | bigint | object)[]) => Promise&#x3C;void>;

/* A callback function to execute when the emitted value does not 
match the expected value.*/
onUnMatched: (emittedValue:  number | string | number[] | string[] | bigint | bigint[] | object | object[] | (string | number | bigint | object)[]) => Promise&#x3C;void>;

/* A callback function to execute when an error occurs during monitoring.*/
onError: (error: Error) => void;
</code></pre>

**Contract Event Condition Parameters:**

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript">import { CHAIN_NAME } from "lit-listener-sdk";
import { InterfaceAbi } from "ethers";
<strong>
</strong><strong>/* The address of the contract to monitor.*/
</strong><strong>contractAddress: `0x${string}`;
</strong>
/* The ABI (Application Binary Interface) of the contract.*/
abi: InterfaceAbi;

/* The Lit supported blockchain network chainId. Import 
    CHAIN_NAME from lit-listener-sdk.*/
chainId: string;

/* The provider URL that is used to create the ethers contract object and monitor the on-chain event. Make sure that your provider URL is compatible with the same network indicated in chain_id for this contract condition.*/
chainId: string;

/* The address of the contract to monitor.*/
eventName: string;

/* The name of the event arg/s that the expectedValue will be matched against.*/
eventArgName: string[]

/* The value that will be matched against the emitted value. This will be compared against the arguments specified in the eventArgName */
expectedValue: number[] | string[] | bigint[] | object[] | (string | number | bigint | object)[];

/* The operator used for the comparison. It must be one of the following: "&#x3C;", ">", "==", "===", "!==", "!=", ">=", "&#x3C;=".*/
matchOperator: "&#x3C;" | ">" | "==" | "===" | "!==" | "!=" | ">=" | "&#x3C;=";

/* A callback function to execute when the emitted value matches 
the expected value.*/
onMatched: (emittedValue: number[] | string[] | bigint[] | object[] | (string | number | bigint | object)[]) => Promise&#x3C;void>;

/* A callback function to execute when the emitted value does not 
match the expected value.*/
onUnMatched: (emittedValue: number[] | string[] | bigint[] | object[] | (string | number | bigint | object)[]) => Promise&#x3C;void>;

/* A callback function to execute when an error occurs during monitoring.*/
onError: (error: Error) => void;
</code></pre>


# Conditional Logic

Set Conditional Logic.

The conditional logic provides an additional layer of granular control over the combined conditions criteria that should be met for the execution of the Lit Action. The Lit Action execution can be tailored to respond to varying situations, such as when a certain threshold has been exceeded or a specific condition has been satisfied, and the conditions can be checked according to a specified time interval.

{% hint style="info" %}
The default condition logic is set with type `EVERY` and no interval (i.e. continuous monitoring).
{% endhint %}

{% hint style="warning" %}
Keep in mind, if you've configured a `WebhookCondition` or `ContractCondition` with a very low interval, there's a possibility that your requests will get rejected due to rate limiting constraints from your provider or the endpoint that you're calling.
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
newCircuit.setConditionalLogic({
    type: "TARGET",
    targetCondition: "1",
    interval: 120000 // milliseconds, Circuit loop called every two minutes
})
```

{% endcode %}

**Conditional Logic Parameters:**

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript"><strong>/* The type of the conditional logic. It can be "THRESHOLD", "TARGET", or "EVERY.*/
</strong><strong>type: "THRESHOLD" | "TARGET" | "EVERY".
</strong><strong>
</strong><strong>/* Used when the type is "THRESHOLD". It's the threshold number of conditions that 
</strong><strong>    must have passed in order for the Lit Action to run.*/
</strong><strong>value?: number;
</strong>
/* Used when the type is "TARGET". It's the specific Condition Id (In order of Conditions Added to Array starting from id "1") that must be met in order for the Lit Action to run.*/
targetCondition?: string;

/* Optional. It's the frequency of condition checks. If omitted, the condition is checked 
    every 30 minutes (1,800,000 ms). Resolves in milliseconds.*/
interval?: number;
</code></pre>


# Set Actions

Set Lit Action Code.

The `setActions` method let's you add custom, contract and fetch actions that are executed by the nodes on the Lit Network. These actions are only invoked if the conditions set in [`setConditions`](/sdk-reference/set-conditions) are met.

When invoking this method, you provide an array of actions that you want to set. The `setActions` returns the Lit Action code as a `string` that will be executed on the Lit Nodes and an `unsignedTransactionDataObject` that is only populated when instantiating `ContractActions`.

This is an asynchronous method, make sure to use `await`.&#x20;

{% hint style="info" %}
If undefined is passed to either of the `gasLimit`, `maxPriorityFeePerGas` or `maxFeePerGas` fields they will be calculated internally as the transaction is simulated. If you'd like more control over these fields please pass in correctly calculated values.&#x20;

If you're transaction requires **approval** or **funding** of the PKP Wallet before it's broadcast, these values must be passed in manually. This is necessary due to the transaction simulation process, to obtain the expected gas price, which occurs prior to the minting of the PKP.&#x20;
{% endhint %}

{% hint style="info" %}
Before Lit.Action.executeJS is called with each run of the Circuit the nonce value for ContractActions will be recalculated by invoking `getTransactionCount()` on the provider.&#x20;
{% endhint %}

{% hint style="info" %}
If you are sending any `value` along with the Contract Action, make sure that it is specified correctly in **wei**.&#x20;
{% endhint %}

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript">import { FetchAction, ContractAction } from "lit-listener-sdk"

const fetchAction: FetchAction = {
  type: "fetch", // type
  priority: 1, // execution priority 
  baseUrl: "https://api.example.com", // baseUrl
  endpoint: "/data", // endPoint
  responsePath: "data.value", // responsePath
  apiKey: "your_api_key", // apiKey
  toSign: [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100], // toSign
  signCondition: [
    {
      type: "&#x26;&#x26;",
      operator: "==",
      value: "expected_value",
    },
  ], // signCondition
};

const contractAction: ContractAction = {
  type: "contract", // type
  priority: 2, // execution priority
  contractAddress: "0x6968105460f67c3bf751be7c15f92f5286fd0ce5", // contract address
  abi: [
     {
      constant: true,
      inputs: [{ name: "numberValue", type: "uint256" }],
      name: "your_function_name",
      outputs: [{ name: "", type: "uint256" }],
      payable: false,
      stateMutability: "external",
      type: "function",
      },
  ], // abi
  functionName: "your_function_name", // function name
  chainId: "polygon", // chainId
  providerURL: "https://polygon-provider-url.com" // provider URL
  nonce: 1, // nonce
  gasLimit: 100000, // gas Limit
  value: 0, // value
  maxPriorityFeePerGas: 1000, // max priority gas fee
  maxFeePerGas: 10000, // max fee per gas
  args: [20], // function arguments
};
<strong>
</strong>const {unsignedTransactionDataObject, litActionCode} = await newCircuit.setActions(
    [
        fetchAction,
        contractAction
    ]
)
</code></pre>

To easily generate the unsigned transaction data that is passed to executeJS (signed by your PKP) for Contract Actions, you can invoke the asynchronous LitListenerSDK helper function `generateUnsignedTransactionData`. &#x20;

{% code overflow="wrap" fullWidth="true" %}

```typescript
import { generateUnsignedTransactionData, CustomAction, CHAIN_NAME, LitUnsignedTransaction } from "lit-listener-sdk";

const unsignedTransactionArgs = await newCircuit.generateUnsignedTransactionData({
    chainId: CHAIN_NAME.polygon,
    abi: [
      {
        constant: false,
        inputs: [
          {
            name: "_value",
            type: "uint256",
          },
        ],
        name: "setValue",
        outputs: [],
        payable: true,
        stateMutability: "nonpayable",
        type: "function",
      },
    ],
    contractAddress: "0x6968105460f67c3bf751be7c15f92f5286fd0ce5",
    nonce: 2, // optional param
    gasLimit: "21000", // optional param
    maxFeePerGas: "10000000000", // optional param
    maxPriorityFeePerGas: "1", // optional param
    from: "{{publicKey}}",
    functionName: "setValue",
    args: [5],
    value: "1000000000000000000", // optional param
}, "https://the-provider-url.com")

const customFunction = `async () => {
   try {
   
    const hashTransaction = (tx) => {
        return ethers.utils.arrayify(
          ethers.utils.keccak256(
            ethers.utils.arrayify(ethers.utils.serializeTransaction(tx)),
            ),
          );
        };
        
    await LitActions.signEcdsa({
        toSign: hashTransaction(unsignedTransactionArgs),
        publicKey: publicKey,
        sigName: sigName,
        });
        
    Lit.Actions.SetResponse({response: "Transaction Signed Successfully."});
        
    } catch (err) {
    console.log('Error thrown on signing transaction.', err)
  }
}`;

const customAction: CustomAction = {
  type: "custom",
  priority: 0,
  code: customFunction
  args: {
   unsignedTransactionArgs
  }
}

const { litActionCode } = newCircuit.setActions([customAction]);
```

{% endcode %}

**Contract Action Parameters:**

{% hint style="info" %}
If the from address passed to a `ContractAction` is left blank it will be populated with the minted PKP Address during execution of the Lit Action.
{% endhint %}

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript">import { CHAIN_NAME,  } from "lit-listener-sdk";
import { InterfaceAbi } from "ethers";

/* The type of the action, always "contract" for this interface.*/
<strong>type: "contract";
</strong>
/*  A numerical value representing the priority of the action. The lower the 
    value, the higher the priority.*/
priority: number;

/* The Ethereum address of the smart contract with which to interact.*/
contractAddress: `0x${string}`;

/* The ABI (Application Binary Interface) of the smart contract, which 
    describes its functions and events.*/
abi: InterfaceAbi;

/* The name of the smart contract function to call.*/
functionName: string;

/* The compatible blockchain network chainId. Import 
    CHAIN_NAME from lit-listener-sdk*/
chainId: CHAIN_NAME;

/* The provider URL for the indicated network.*/
providerURL: string;

/* The transaction nonce.*/
nonce?: number;

/* The transaction gas limit.*/
gasLimit?: BigNumberish;

/* Any value to be passed with the transaction.*/
value?: BigNumberish;

/* The address from which the transaction as called. This will usually be the PKP address.*/
from?: `0x${string}` | {{publicKey}};

/* The max priority fee per gas for the transaction.*/
maxPriorityFeePerGas?: BigNumberish;

/* The max fee per gas for the transaction.*/
maxFeePerGas?: BigNumberish;

/* An array of arguments to pass to the function call.*/
args?: any[];
</code></pre>

**Fetch Action Parameters:**

{% code overflow="wrap" fullWidth="true" %}

```typescript
/* The type of the action, always "fetch" for this interface.*/
type: "fetch";

/* A numerical value representing the priority of the action. The lower the value, the higher the priority. */
priority: number;

/* The base URL of the API endpoint. */
baseUrl: string;

/* The specific endpoint to fetch. */
endpoint: string;

/* The path to access the expected value in the response body.*/
responsePath: string;

/* Optional API key for authorization.*/
apiKey?: string;

/* The data to sign. If left blank the response returned from the API will be signed. */
toSign?: Uint8Array;

/* The condition under which to sign the data.*/
signCondition?: {
    type: "&&" | "||";
    operator: "<" | ">" | "==" | "===" | "!==" | "!=" | ">=" | "<=";
    value:
      | number
      | string
      | bigint
      | string[]
      | number[]
      | bigint[]
      | undefined
      | (string | number | bigint)[];
 }[];
```

{% endcode %}

**Custom Action Parameters:**

{% hint style="info" %}
When creating a `CustomAction` and invoking a `Lit Action` to sign unsigned transaction data you will need to pass in the unsigned transaction data as an argument, however you do not need need to include the `PKP PublicKey`, `PKP Address` or `Auth Signature` as arguments as these parameters are passed to `Circuit.start()`
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
/* The type of the action, always "custom" for this interface.*/
type: "custom";

/* A numerical value representing the priority of the action. The 
    lower the value, the higher the priority.*/
priority: number;

/* A function string representing the custom action to be performed. This 
    function is defined by the user.*/
code: string;

/* The type of the action, always "custom" for this interface.*/
args?: Object;

```

{% endcode %}


# Execution Constraints

Set Execution Constraints.

The method defines the conditions that govern the execution of the circuit, allowing developers to limit runs based on criteria such as time frame or number of successful completions.

{% hint style="info" %}
If no `executionConstraints` are added then the Lit Action Code will run according to the `setConditions` and `conditionalLogic`.
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
const executionOptions = {
    conditionMonitorExecutions: 10,
    startDate: new Date("2023-07-01T00:00:00Z"),
    maxLitActionCompletions: 3
}

newCircuit.executionConstraints(executionOptions);
```

{% endcode %}

**Execution Constraints Parameters:**

{% code overflow="wrap" fullWidth="true" %}

```typescript
/* Optional. The maximum amount of times that the circuit will run before 
    stopping, inclusive of conditions on matched, on unmatched and conditional logic failures.*/
conditionMonitorExecutions?: number;

/* Optional. The circuit will not run before this date.*/
startDate?: Date;

/* Optional. The circuit will stop running once this date has passed.*/
endDate?: Date;

/* Optional. The maximum amount of times that the Lit Action code will be 
    executed before the circuit stops running. This is a full run of the circuit.*/
maxLitActionCompletions?: number;
```

{% endcode %}


# IPFS Hash

Obtain the CID.

{% hint style="info" %}
For a reliable IPFS upload use a dedicated client and pass the returned CID to `mintGrantBurnPKP`&#x20;
{% endhint %}

Upload your Lit Action Code to IPFS through your preferred client. If you have already uploaded your Lit Action to IPFS and only need retrieve the CID, invoke `getIPFSHash`.&#x20;

{% code overflow="wrap" fullWidth="true" %}

```typescript
const ipfsCID = await newCircuit.getIPFSHash(litActionCode);
```

{% endcode %}


# MintGrantBurn PKP

Mint, Grant and Burn the PKP to the associated Lit Action.

By invoking `mintGrantBurnPKP`, you obtain the `publicKey`, `tokenId`, and `address` of the PKP. The publicKey is the non-compressed public key that the ECDSA algorithm uses to compute the address.

{% hint style="info" %}
Get some Testnet LIT tokens from [the official Chronicle faucet](https://faucet.litprotocol.com/) to mint your PKP on Chronicle.
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
const { publicKey, tokenId, address } = await newCircuit.mintGrantBurnPKP(ipfsCID);
```

{% endcode %}


# Start Circuit

Run your Circuit.

To monitor the conditions and execute the Lit Action code, start the circuit and pass in your PKP public key, IPFS hash of the Lit Action code and Auth Signature.

{% hint style="info" %}
The `ipfsCID` is an optional parameter. If it is not passed then the LitActionCode generated and returned directly through `setActions` will be passed instead. `authSig` can also be passed as an optional parameter to `start()`.
{% endhint %}

{% hint style="info" %}
Please note that for any actions where the signed transaction or message is required to be **broadcast** to a blockchain network this is an additional step. It is supported by the SDK, see [broadcast](/sdk-reference/broadcast-transactions).&#x20;
{% endhint %}

{% code overflow="wrap" fullWidth="true" %}

```typescript
const authSig = await newCircuit.generateAuthSignature();

await newCircuit.start({publicKey, ipfsCID, authSig});
```

{% endcode %}

To get the logs of returned responses and handle errors see [**Errors and Logs**](/errors-and-logs/logs-and-error-handling) and [**Error Strict Mode**](/errors-and-logs/error-strict-mode)**.**

If you need to abort the execution forcefully call `interrupt()` . To ensure that the interrupt effectively stops the execution, it should **not** be called directly `after` an await on `start()` or the execution of `interrupt()` will be blocked.&#x20;

The circuit will stop running after the current iteration is complete.

{% code overflow="wrap" fullWidth="true" %}

```typescript
newCircuit.interrupt();
```

{% endcode %}


# Broadcast Transactions

Broadcast to the Network.

If you'd like your transactions to also be broadcast to the associated blockchain network for each `ContractAction` created, you can pass in the optional broadcast parameter as `true` to the `start` method.&#x20;

{% code overflow="wrap" fullWidth="true" %}

```typescript
await newCircuit.start({publicKey, ipfsCID, authSig, broadcast: true});
```

{% endcode %}

{% hint style="warning" %}
Please note that broadcast is only supported for `ContractActions`. If any of your transaction data is under funded or unapproved the broadcast will be unsuccessful and an error logged and thrown.&#x20;

Make sure your PKP address or other wallets are correctly funded and approved on the network specified in each `ContractAction`.
{% endhint %}

For a more **reliable** and **controlled** experience it's suggested that you broadcast your transactions outside of the SDK, which is necessary anyway if you plan to broadcast any `FetchAction` or `CustomAction` signed data. **Broadcasts are non blocking to each run of the circuit.**

For a quick start to broadcasting your signed transactions, obtain the correct unsigned transaction data parameters returned in the SetActions object. You can also create this manually yourself by invoking `generateUnsignedTransactionData` .

{% code overflow="wrap" fullWidth="true" %}

```typescript
// Generate or obtain the constructed unsigned transaction data.
const {unsignedTransactionDataObject, LitActionCode} = await newCircuit.setActions([contractAction]);
```

{% endcode %}

The `unsignedTransactionDataObject` is populated according to the number of `ContractActions` added in `SetActions`. Each field within this object is named in the following format: `generatedUnsignedDataContract${priorityNumberOfTheAction}`.

{% code overflow="wrap" fullWidth="true" %}

```typescript
unsignedTransactionDataObject = {
      generatedUnsignedDataContract1: {
      "to": "0x123abc...",
      "nonce": 0,
      "chainId": 137,
      "gasLimit": { "_hex": "0x186a0", "_isBigNumber": true },
      "maxFeePerGas": { "_hex": "0x2dc6c0", "_isBigNumber": true },
      "maxPriorityFeePerGas": { "_hex": "0x16e360", "_isBigNumber": true },
      "from": "0xabc123...",
      "data": "0xa9059cbb0000000000000000000000005beeb...",
      "value": { "_hex": "0x0", "_isBigNumber": true },
      "type": 2
  },
      generatedUnsignedDataContract2: {
        "to": "0xdef456...",
        "nonce": 2,
        "chainId": 137,
        "gasLimit": { "_hex": "0x186a0", "_isBigNumber": true },
        "maxFeePerGas": { "_hex": "0x2dc6c0", "_isBigNumber": true },
        "maxPriorityFeePerGas": { "_hex": "0x16e360", "_isBigNumber": true },
        "from": "0x456def...",
        "data": "0xa9059cbb0000000000000000000000004abcf...",
        "value": { "_hex": "0x0", "_isBigNumber": true },
        "type": 2
  }
}
```

{% endcode %}

This transaction data can then be serialized alongside the returned `s,r,recid` values from the signed transaction response object in the `LogCategory.RESPONSE` field.

{% code overflow="wrap" fullWidth="true" %}

```typescript
import { joinSignature } from "@ethersproject/bytes";

// The latestBroadcastLog will contain the broadcast information of the last signed transaction i.e. generatedUnsignedDataContract2. To access generatedUnsignedDataContract1 take newCircuit.getLogs(LogCategory.Broadcast)[1]. Assuming the circuit has done one execution run. 
const latestBroadcastLog = newCircuit.getLogs(LogCategory.Broadcast)[0];
const sig = JSON.parse(latestBroadcastLog.responseObject).signatures.contract1;

const encodedSignature = joinSignature({r: "0x" + sig.r, s: "0x" + sig.s, recoveryParam: sig.recid});

// Make sure your provider url and chainId matches the chain that was specified for the added contract action.
const provider = new ethers.providers.JsonRpcProvider("https://provider-url.com", 137);

// Serialize the correct tx data with the associated encoded signature.
const serialized = serialize(generatedUnsignedDataContract2,encodedSignature);

const transactionHash = await provider.sendTransaction(serialized);
await transactionHash.wait();
```

{% endcode %}


# Add Secure Key

Additional security layer.

To enhance the security and control over who can execute the assigned `LitAction` granted to your PKP, you have the option to implement a Secure Key. This mechanism is analogous to using an API key, serving as an additional layer of authorization.

**Generating the Secure Key**

This secure key is generated when running `setActions()` where you will receive a 32-byte hash key. This key is generated randomly and returned.

{% hint style="warning" %}
Store this key in a secure location, your LitAction will not run without it.
{% endhint %}

**How It Works**

1. **Hash Generation**: The SHA-256 hash of the Secure Key is computed and permanently associated with the `LitAction`.
2. **Execution**: When you attempt to run the `LitAction`, you must provide the correct Secure Key as a parameter.
3. **Validation**: A function within the `LitAction` will then compare the SHA-256 hash of the provided key against the original hash stored in the`LitAction`.&#x20;
4. **Outcome**:
   * **Success**: If the hashes match, the `LitAction` will execute.
   * **Failure**: If the hashes do not match, the `LitAction` will not execute.

{% code overflow="wrap" fullWidth="true" %}

```typescript
// Pass true after the actions array to create and apply the Secure Key to your LitAction. Retrieve the secureKey value from the returned object array.
const {unsignedTransactionDataObject, litActionCode, secureKey} = await newCircuit.setActions([fetchAction, contractAction], true);

// Pass in the correct secure key to start
await newCircuit.start({publicKey, ipfsCID, authSig, secureKey});
```

{% endcode %}


# Server-SDK Integration

Long-Range and Long-Running Operations

<figure><img src="/files/EhErVIPyWG7poEUTWQzH" alt=""><figcaption></figcaption></figure>

For applications where continuous webhook and contract event monitoring is required, a dedicated stable server setup provides uninterrupted connection.&#x20;

A full code example of this set up, using an Node.js backend (hosted on [render.com](https://render.com/)), is available [here](https://github.com/DIGITALAX/ListenerNoCode) (frontend) and [here](https://github.com/DIGITALAX/nocode_listener_server) (backend server), with the architecture running live at [listener.irrevocable.dev](https://listener.irrevocable.dev).

The SDK also includes dedicated long-running functions that support the minting of PKPs directly from the server.&#x20;

Further, the Listener Contract Factory can be deployed on Polygon as a persistant on-chain database for storing all logs generated by the running circuits on the server. These logs are stored with IPFS and can be accessed via a dedicated subgraph, making data retrieval efficient and streamlined for frontend queries. With this architecture, all logs are batched and signed to the Listener Database contract through an assigned PKP.

**See** [**here**](/database-and-server-interactions/persistant-circuit-architecture) **for a step by step walkthrough for getting started with a long range setup.**&#x20;


# Persistant Circuit Architecture

Quick Start for Server-SDK Integration.

<figure><img src="/files/rBPeHRz7pIrEAaDZpwSN" alt=""><figcaption></figcaption></figure>

1. MintGrantAndBurn a PKP on Chronicle using the [Lit Explorer](https://lit-protocol.calderaexplorer.xyz/address/0x8F75a53F65e31DD0D2e40d0827becAaE2299D111/write-contract#address-tabs).
   * Invoke `mintGrantAndBurnNext` using a keyType `2`, the **bytes hash** of the `IPFS CID` found [**here**](https://chromadin.infura-ipfs.io/ipfs/QmSrk1TqfTPSiqEPyfbReZPAwfnQQw7Ai9jfPqbQQ8sndR) which generates the unsigned transaction data for logging all instantiated circuits and circuit responses to the ListenerDB Contract and a value of `0.000000000000000001` LIT.
2. Deploy your ListenerDB and ListenerAccessControl from the [**ListenerFactory**](https://polygonscan.com/address/0x13091758Cf341818C14b070bf237d42913fDCEbc) on Polygon by invoking `deployFromFactory` with your minted PKP address, public key and tokenId.
3. Set up an Node.js VM or similar and deploy your server architecture. For a full code example of the server <> sdk integration and set up see [**here**](https://github.com/DIGITALAX/nocode_listener_server).&#x20;
   * If you use the same architecture and endpoints, make sure to call `/connect` once at the start when the server is live to correctly instantiate the Lit client that is used to save logs on-chain.
4. Create and connect your frontend using NextJS or similar to the server. See a full code example [**here**](https://github.com/DIGITALAX/LitListenerSDK).
   * Queries can be made to the deployed [**Lit Listener Graph**](https://api.thegraph.com/subgraphs/name/digitalax/lit-listener) subgraph to retrieve the logs recorded on-chain.&#x20;
   * All DB contracts deployed through the Factory can easily retrieve their associated logs through this subgraph, with optionality to sort each query via your deployed DB address.
5. Make sure to fund your **PKP address** with enough MATIC to pay for the gas costs associated with broadcasting signed transactions to the deployed LitDB contract on Polygon network.


# Logs & Error Handling

Lit Action Responses and SDK Logs.

The `getLogs` method retrieves and returns the logs of the circuit. The logs provide a chronological record of events and actions that have taken place in the execution of the circuit. This can be helpful for debugging purposes or for recording the progression of circuit tasks.

{% hint style="info" %}
You can optionally specify a category to get logs of that category only. There are three categories:&#x20;

* Error: Returns errors logged in the circuit.
* Response: Returns a stringified JSON of the Lit Action response object.
* Condition: Returns the matched or unmatched status for each conditional check and the Emitted Value from the Contract or Webhook Event.&#x20;
* Broadcast: Returns the broadcast Transaction Hash for broadcast `ContractActions`.
* Execution: Returns when Conditional Logic or Execution Constraints are updated.
  {% endhint %}

{% hint style="warning" %}
The SDK retains a rolling log of the most recent **1000** entries; for more extensive or permanent log storage, please consider using an external database or logging service.
{% endhint %}

<pre class="language-typescript" data-overflow="wrap" data-full-width="true"><code class="lang-typescript"><strong>import { LogCategory } from "lit-listener-sdk"
</strong>
/* returns all logs recorded by the circuit.*/
const allLogs = newCircuit.getLogs()

/* the returned value is array of objects with both category, message and responseObject fields, the category is the enum type and the message is the log description and the responseObject is the returned response object.*/
const { category, message, responseObject } = allLogs[0];

/* returns only the error logs recorded by the circuit.*/
const errorLogsOnly = newCircuit.getLogs(LogCategory.Error)
</code></pre>

When a log is recorded a log event is also emitted. You can subscribe to the events in real time through the `.on` method.

{% code overflow="wrap" fullWidth="true" %}

```typescript
import { ILogEntry } from "lit-listener-sdk"

newCircuit.on('log', (logEntry: ILogEntry) => {
    console.log("new log recorded", logEntry);
})
```

{% endcode %}

#### Lit Action Response Object

The Response returned by the Lit.Actions.setResponse() method is a concatenated response object of all responses logged in the code, including error handling. It is found as a stringified JSON in the `responseObject.response` field of the returned log.&#x20;

Every action is wrapped in a function with a unique name based on its type and priority, for example `custom0`, `fetch1`, `contract2`, etc.&#x20;

When an action is executed the result is saved in the `concatenatedResponse` object under a key with the same name as the action function.

If you have a `CustomAction` with a priority of `0`, and that action function returns the string "Custom Action 1", then the `concatenatedResponse` object will have a key-value pair of: `custom0: "Custom Action 1"`.

For `ContractActions` the returned response value is the `generatedUnsignedTransactionData`, for `FetchActions` the returned response value is an object value found at the response path under the value key, and a boolean under signed, indicating whether the transaction was signed or not according to any `signCondition`. For `CustomActions` the custom indicated response object is returned.

Any console logs or signatures created when executing the Lit Action are also returned in the `responseObject` under the `signatures` and `logs` fields.

Each signature is named according to the `SetAction` **type** and **priority** order for `FetchActions` and `ContractActions`.

<pre class="language-typescript"><code class="lang-typescript"><strong>{
</strong><strong>    category: 1,
</strong><strong>    message: "Circuit executed successfully. Lit Action Response."
</strong><strong>    responseObject: {
</strong><strong>        signatures: {
</strong><strong>            
</strong><strong>            },
</strong><strong>        response: {
</strong><strong>            custom0: "Custom Action 1 Returned Response",
</strong>            contract1: {
                "to": "0x46C0Fa7Ef8384E356C62E7e4cC2578bD70D829aa",
                "nonce": 0,
                "chainId": 137,
                "gasLimit": "50000",
                "maxFeePerGas": "2601315606",
                "maxPriorityFeePerGas": "650328901",
                "from": "0x4F9DDeb2Fe6AB63809dC6A026F493B77F7df4400",
                "data": "0xa9059cbb00000000000000000000000046c0fa7ef8384e356c62e7ecc2578bd70d829aa0000000000000000000000000000000000000000000000016345785d8a0000",
                "value": 0,
                "type": 2
            },
            fetch2: {
                value: "returned fetch value",
                signed: true,
            } 
        }
        logs: "",
    }
  date: "2023-07-23T12:34:56.789Z"
<strong>}
</strong></code></pre>


# Error Strict Mode

Handling Encountered Errors in the Circuit.

When instantiating your circuit set `errorHandlingModeStrict` to `true` if you'd like the SDK to take a *strict* approach to error handling.

{% code overflow="wrap" fullWidth="true" %}

```typescript
const newCircuit = new Circuit(chronicleSigner, undefined, true);
```

{% endcode %}

Note that for when `errorHandlingModeStrict` is enabled, the retry count in condition monitoring is ignored and an error will throw immediately upon the first failure, interrupting the Circuit.&#x20;

Further, if an error is encountered at any point within executing the LitAction or broadcasting signed actions, the Circuit too will be interrupted under this mode.


# No Code Interface

listener.irrevocable.dev

**Account abstraction enticing you to abstract away the code too?**&#x20;

Check out a no code implementation of the SDK live [here](https://listener.irrevocable.dev/).

(Note that the no-code instance is still in beta mode and uses an experimental PKP backend infrastructure for greater decentralisation, your circuit may be interrupted or reset at anytime).

<figure><img src="/files/lmgthl5i7kbNMkAUc4fH" alt=""><figcaption></figcaption></figure>

### How it works:

The application architecture consists of:

* Next.js frontend
  * Collects circuit logic from users.
* Node backend server
  * Instantiates all circuits for uninterrupted service and continuous webhook and contract event monitoring. An assigned PKP batches and signs all logs, errors and results to the [Listener Factory](/database-and-server-interactions/persistant-circuit-architecture) Database Contract.
* On-chain database
  * Logs are written on-chain with IPFS and stored within the Lit Database contract. &#x20;
* Graph Protocol subgraph
  * Real-time data retrieval from the Lit Database Contract to the frontend application.


# Issues

Reporting Issues and Errors.

<figure><img src="/files/fHI4RNPbCouT52x1yp39" alt=""><figcaption></figcaption></figure>

If you encounter any issues or errors while using the SDK, you can open a new issue [here](https://github.com/DIGITALAX/LitListenerSDK/issues).

When creating a new issue, please provide as much information as possible to help us understand and reproduce the problem. Here are some guidelines to follow:

* Give the issue a clear and concise title that summarizes the problem.&#x20;
* Describe the issue in detail, inclusive of reproduction steps, error messages, and related logs.
* Specify your environment, including the versions of the SDK, Node.js and any other relevant software or packages you are using.&#x20;
* Include screenshots if possible.


# Testing

Running the Test Suite.

<figure><img src="/files/OV3XWKut9A9MEAP4n0cz" alt=""><figcaption></figcaption></figure>

A comprehensive Test Suite for the SDK is located in the `test` folder in the root of the project. Before running the tests, you will need to compile the project and set up your environment variables.

{% hint style="info" %}
You will need LIT Testnet Tokens to run the Test Suite. You can get LIT Testnet Tokens from the official faucet [here](https://faucet.litprotocol.com/).
{% endhint %}

{% hint style="info" %}
Note that some tests use the [api.weather.gov](https://api.weather.gov/gridpoints/LWX/97,71/forecast) as the endpoint for Webhook Conditions. The data returned by this API can change frequently, if you encounter test failures related to these specific tests, you may need to update the expected values with the latest data from the endpoint to ensure accurate comparisons.
{% endhint %}

Place a valid `PRIVATE_KEY` in your `.env` file to create the signer object for minting PKPs on Chronicle.&#x20;

```sh
PRIVATE_KEY=
```

Run the following in your command line:

```bash
npm run test
```


# Contributors

Contributing to the SDK.

<figure><img src="/files/Jx8RYSOgzBNuikuJUVO6" alt=""><figcaption></figcaption></figure>

### Know what else can be signed? Your contributions.

1. Clone the forked repository.

```bash
git clone https://github.com/DIGITALAX/LitListenerSDK
```

2. Create a new branch.

```bash
git checkout -b <branch_name>
```

3. Commit code additions locally.

```bash
git add .
git commit -m "Description of the changes"
```

4. Push changes to the forked repository.

```bash
git push origin <branch_name>
```

5. Create a pull request on the LitListenerSDK repository.

```bash
git checkout <branch_name>
git pull upstream main
```


# System Architecture

SDK Blueprint.

<figure><img src="/files/9YU5gNKZXZgQJOCeTKo3" alt=""><figcaption></figcaption></figure>


# About the Devs

<figure><img src="/files/L9a5qZOkSIDQtboYF0un" alt=""><figcaption></figcaption></figure>

Brought to you by the core devs at [DIGITALAX](https://www.digitalax.xyz/).


# Storefront

listener.irrevocable.dev/shop

Decrypt your wardrobe at [The Listener Storefront](https://listener.irrevocable.dev/shop). Handcrafted cypherpunk pieces for anon insiders.

<div><figure><img src="/files/q013aKPEnQMQDQSdXwnw" alt=""><figcaption></figcaption></figure> <figure><img src="/files/zX8jnGxHDl5kOuDjI3Ip" alt=""><figcaption></figcaption></figure> <figure><img src="/files/GpOqxwz6Ab9JbP2j0i3R" alt=""><figcaption></figcaption></figure> <figure><img src="/files/iJoiFuv1HTAf7ZMCdqKT" alt=""><figcaption></figcaption></figure></div>


