# Example with the Execution API

While the Execution Layer provides the foundational security and access control for automations, the Orchestration Layer enhances it with scheduling capabilities. For complex automation scenarios that go beyond the standard scheduling API, developers can directly utilize the core execution endpoints. Let's explore this flexibility through an example.

## A Simple Cross Chain Refuel BOT

An automation built using Console Automation APIs that helps keep ETH balances topped up across different chains.

Users can subscribe by providing:

* A `refuelAddress` to monitor
* `minAmount` of ETH to maintain
* List of `chains` to watch

The automation checks if the `refuelAddress` drops below `minAmount` of ETH on any of the specified chains. If it does, it automatically bridges funds to keep the balances above the minimum threshold

## Creating the Automation

To create an automation, the executor must first configure the automation parameters.

```js
// Define the EIP-712 message parameters
const msgParams = {
  domain: {
    chainId: 42161
  },
  message: {
    timestamp: 1,
    executor: "0xAE75B29ADe678372D77A8B41225654138a7E6ff1",
    inputTokens: ["0x0000000000000000000000000000000000000000"],
    hopAddresses: ["0x3a23F943181408EAC424116Af7b7790c94Cb97a5"],
    feeInBPS: 0,
    feeToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
    feeReceiver: "0xAE75B29ADe678372D77A8B41225654138a7E6ff1",
    limitPerExecution: true,
    clientId: "auto-refuel"
  },
  primaryType: "RegisterExecutor",
  types: {
    EIP712Domain: [
      { name: "chainId", type: "uint256" },
      { name: "name", type: "string" },
      { name: "version", type: "string" },
      { name: "verifyingContract", type: "address" }
    ],
    RegisterExecutor: [
      { name: "timestamp", type: "uint256" },
      { name: "executor", type: "address" },
      { name: "inputTokens", type: "address[]" },
      { name: "hopAddresses", type: "address[]" },
      { name: "feeInBPS", type: "uint256" },
      { name: "feeToken", type: "address" },
      { name: "feeReceiver", type: "address" },
      { name: "limitPerExecution", type: "bool" },
      { name: "clientId", type: "string" }
    ]
  }
};
```

Once we have the automation parameters defined, we can create a helper to sign the parameters, as this signature is required for automation creation.

```js
const provider = new providers.JsonRpcProvider(RPC_URL);
const signer = new Wallet(EXECUTOR_PRIVATE_KEY, provider);

async function signAutomationRegistrationMessage(domain, types, message) {
  try {
    // Create a wallet instance using the private key
    const wallet = new ethers.Wallet(privateKey);

    // Sign the message according to EIP-712
    const signature = await wallet._signTypedData(domain, types, message);

    console.log("Signature:", signature);
    return signature; // Return the generated signature
  } catch (err) {
    console.error("Error signing message:", err);
    throw err;
  }
}
```

Here, `RPC_URL` and `EXECUTOR_PRIVATE_KEY` must be configured with appropriate values.

Once the signature is ready, we can simply call the **POST** `/executor` endpoint to create the automation for the executor.

```js
const SVC_BASE_URL = "https://gtw.brahma.fi";
// Function to execute the API call
async function executeApiCall() {
  try {
    // Generate signature
    const signature = await signMessage(
      msgParams.domain,
      msgParams.types,
      msgParams.message
    );

    // Prepare the data for the POST request
    const data = {
      config: {
        feeInBPS: msgParams.message.feeInBPS,
        feeReceiver: msgParams.message.feeReceiver,
        feeToken: msgParams.message.feeToken,
        limitPerExecution: msgParams.message.limitPerExecution,
        inputTokens: msgParams.message.inputTokens,
        hopAddresses: msgParams.message.hopAddresses
      },
      executor: msgParams.message.executor,
      signature: signature, // Use the  generated signature
      chainId: msgParams.domain.chainId,
      timestamp: msgParams.message.timestamp,
      executorMetadata: {
        id: msgParams.message.clientId,
        name: "auto-refuel",
        logo: "https://smolrefuel.com/logo.png",
        metadata: {}
      }
    };

    // Make the POST request using Axios
    const response = await axios.post(
      `${SVC_BASE_URL}/v1/automations/executor`,
      data,
      {
        headers: {
          "Content-Type": "application/json"
        }
      }
    );

    console.log("Create Executor response:", response.data);
  } catch (error) {
    console.error("Error creating Executor response:", error);
  }
}
```

## Fetching subscribers of automation

Once the automation is created, Console users will be able to subscribe to the automation from console's Automation Marketplace. The automation executors can fetch a list of all subscribers and their metadata from the API, to perform further operations.

First, the executor's registryID must be fetched in order to fetch its subscribers.

```js
const fetchExecutorByAddress = async (address, chainId) => {
  try {
    const resp = await Axios.get(
      `${SVC_BASE_URL}/v1/automations/executor/${address}/${chainId}`
    );
    return resp.data.data;
  } catch (err) {
    console.log("err: failed to call fetchExecutorByAddress");
    throw "failed to fetch executor by address";
  }
};
```

Post fetching, we can also perform further cleanup to filter out the active subscriptions.

```js
const fetchActiveSubscriptions = async (registryID) => {
  try {
    const response = await Axios.get(
      `${SVC_BASE_URL}/v1/automations/executor/${registryID}/subscriptions`
    );

    const subscriptions = response.data.data;

    let activeSubscriptions = [];
    for (let subscription of subscriptions) {
      // filter our if not currently active
      if (subscription.status != CANCELLED_STATUS) {
        activeSubscriptions.push(subscription);
      }
    }

    return activeSubscriptions;
  } catch (err) {
    console.log("err: failed to get subscriptions");
    throw "failed to get executor subscriptions";
  }
};
```

## Checking if the automation should be executed for a subscriber

The automation only has to be executed, if the `refuelAddress` has a balance lower than the `minAmount` threshold on any given chain ID from `chains`. We define a function to perform a check to see if a target address has a low balance-

```js
const canExecute = async (targetChainID, target, threshold) => {
  const provider = new providers.JsonRpcProvider(RPC_URL);

  const bal = await provider.getBalance(target);
  logger.info(
    logMessage(
      "CanExecute Check",
      `Current balance: ${bal.toString()}, Threshold: ${threshold.toString()}`
    )
  );

  if (bal.gt(threshold)) {
    return false;
  }

  return true;
};
```

Here, `RPC_URL` needs to be defined

Now we can go through all the subscribers and check if the automation is to be performed.

```js
const metadata = await fetchExecutorByAddress(EXECUTOR_ADDRESS, CHAIN_ID);
let subscriptions = await fetchActiveSubscriptions(metadata.id);
logger.info(
  logMessage("Found Subscriptions", JSON.stringify(subscriptions, "", 4))
);

for (let subscription of subscriptions) {
  const metadata = subscription.metadata;
  let refuel = false;
  let targetChainID = 0;
  let subaccount = subscription.subAccountAddress;
  // Check if refuel is needed for any of the target chains
  for (let targetChain of metadata.chains) {
    if (
      await canExecute(targetChain, metadata.refuelAddress, metadata.minAmount)
    ) {
      refuel = true;
      targetChainID = targetChain;
      break;
    }
  }
}
```

Here, `EXECUTOR_ADDRESS` and `CHAIN_ID` must be defined.

## Executing calldata to bridge funds to `refuelAddress` on required chains from subscriber

Before proceeding with the actual execution, we first need to define some helpers.

* **Signing execution digest**

  ```js
  const buildExecutionDigestSignature = async (
    chainId,
    to,
    value,
    data,
    operation,
    account,
    nonce,
    wallet,
    executor
  ) => {
    const types = {
      ExecutionParams: [
        { name: "operation", type: "uint8" },
        { name: "to", type: "address" },
        { name: "account", type: "address" },
        { name: "executor", type: "address" },
        { name: "gasToken", type: "address" },
        { name: "refundReceiver", type: "address" },
        { name: "value", type: "uint256" },
        { name: "nonce", type: "uint256" },
        { name: "safeTxGas", type: "uint256" },
        { name: "baseGas", type: "uint256" },
        { name: "gasPrice", type: "uint256" },
        { name: "data", type: "bytes" }
      ]
    };

    const executorPluginDomain = {
      name: "ExecutorPlugin",
      version: "1.0",
      chainId: chainId,
      verifyingContract: EXECUTOR_PLUGIN_ADDRESS
    };

    const executionParamsData = {
      to,
      value,
      data,
      operation,
      account,
      executor: executor,
      nonce,
      gasToken: ethers.constants.AddressZero,
      refundReceiver: ethers.constants.AddressZero,
      safeTxGas: 0,
      baseGas: 0,
      gasPrice: 0
    };

    const signature = await wallet._signTypedData(
      executorPluginDomain,
      types,
      executionParamsData
    );

    return signature;
  };

  const signCallData = async (
    account,
    executor,
    executable,
    wallet,
    chainId,
    rpc
  ) => {
    const provider = new ethers.providers.JsonRpcProvider(rpc);

    const executorPluginContract = new ethers.Contract(
      EXECUTOR_PLUGIN_ADDRESS,
      EXECUTOR_PLUGIN_ABI,
      provider
    );
    /// Generate signature for execution digest
    const executorSignature = await buildExecutionDigestSignature(
      chainId,
      executable.to,
      executable.value,
      executable.data,
      executable.callType,
      account,
      (
        await executorPluginContract.executorNonce(account, executor)
      ).toString(),
      wallet,
      executor
    );

    return executorSignature;
  };
  ```

  Here, `EXECUTOR_PLUGIN_ADDRESS` and `EXECUTOR_PLUGIN_ABI` must be assigned to the appropriate values.
* **Helpers to get bridge routes from socket**

  ```js
  async function getQuote(
    fromChainId,
    fromTokenAddress,
    toChainId,
    toTokenAddress,
    fromAmount,
    senderAddress,
    recipient,
    uniqueRoutesPerBridge,
    sort,
    singleTxOnly
  ) {
    const response = await fetch(
      `https://api.socket.tech/v2/quote?fromChainId=${fromChainId}&fromTokenAddress=${fromTokenAddress}&toChainId=${toChainId}&toTokenAddress=${toTokenAddress}&fromAmount=${fromAmount}&userAddress=${senderAddress}&recipient=${recipient}&bridgeWithGas=false&sort=${sort}&singleTxOnly=${singleTxOnly}&isContractCall=true`,
      {
        method: "GET",
        headers: {
          "API-KEY": API_KEY,
          Accept: "application/json",
          "Content-Type": "application/json"
        }
      }
    );

    const json = await response.json();
    return json;
  }

  async function getRouteTransactionData(route) {
    const response = await fetch("https://api.socket.tech/v2/build-tx", {
      method: "POST",
      headers: {
        "API-KEY": API_KEY,
        Accept: "application/json",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ route: route })
    });

    const json = await response.json();
    return json;
  }
  ```

  Here, `API_KEY` must be assigned to the executor's socket API key.
* **Helper to use the `/execute` endpoint to execute a task on subscriber**

  ```js
  const executeTask = async ({
    chainId,
    executable,
    subaccount,
    executorSignature,
    executor
  }) => {
    try {
      const { data: relayerResponse } = await Axios.post(
        `${SVC_BASE_URL}/v1/automations/tasks/execute/${chainId}`,
        {
          task: {
            executable,
            subaccount,
            executorSignature,
            executor
          }
        },
        {
          timeout: 50 * 1000
        }
      );
      return relayerResponse;
    } catch (err) {
      console.log(
        "failed to execute task ",
        JSON.stringify(err.response.data, "", 4)
      );
      throw "failed to execute task";
    }
  };
  ```

Now that we have all the required helpers, we can combine them for the complete automation execution logic.

```js
for (let subscription of subscriptions) {
  const metadata = subscription.metadata;
  let refuel = false;
  let targetChainID = 0;
  let subaccount = subscription.subAccountAddress;
  // Check if refuel is needed for any of the target chains
  for (let targetChain of metadata.chains) {
    if (
      await canExecute(targetChain, metadata.refuelAddress, metadata.minAmount)
    ) {
      refuel = true;
      targetChainID = targetChain;
      break;
    }
  }
  if (refuel) {
    let amount = metadata.amount;
    logger.info(
      logMessage(
        "withdrawing amount",
        utils.formatEther(BigNumber.from(amount))
      )
    );
    let executable = {};

    logger.info(logMessage("Generating Bridging Quote", ""));
    // Get quote for bridging
    const quote = await getQuote(
      CHAIN_ID,
      "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
      targetChainID,
      "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
      amount,
      subaccount,
      metadata.refuelAddress,
      true,
      "time",
      true
    );

    const route = quote.result.routes[0];
    logger.info(logMessage("Quote", JSON.stringify(route)));

    // Get transaction data for the route
    const apiReturnData = await getRouteTransactionData(route);
    logger.info(logMessage("Call Data", JSON.stringify(apiReturnData, "", 2)));

    // Prepare executable transaction data
    executable = {
      callType: 0,
      to: apiReturnData.result.txTarget,
      value: BigNumber.from(apiReturnData.result.value).toString(),
      data: apiReturnData.result.txData
    };

    // Sign the call data
    const executorSignature = await signCallData(
      subaccount,
      EXECUTOR_ADDRESS,
      executable,
      signer,
      CHAIN_ID,
      RPC_URL
    );
    logger.info(logMessage("Executor Signature", executorSignature));

    // Execute the task
    const taskExecResponse = await executeTask({
      chainId: CHAIN_ID,
      executable,
      subaccount,
      executorSignature,
      executor: EXECUTOR_ADDRESS
    });

    logger.info(
      logMessage("Executed Task", JSON.stringify(taskExecResponse, "", 4))
    );

    // wait before next run
    logger.info(logMessage("Waiting for intent finalization", "~2 mins"));
    await delay(60 * second);
  } else {
    await delay(5 * second);
  }
}
```

Here, `EXECUTOR_ADDRESS`, `CHAIN_ID` and `RPC_URL` must be configured with appropriate values.

## Automation Demo

{% embed url="<https://www.youtube.com/watch?v=fTsQMTfMOUg>" %}

## Wrapping up

* Reference for the APIs used in this example can be found [here](broken://pages/VCNRHSk1VcSBcz5C259s).
* The complete script for this example automation can be found [here](https://github.com/Brahma-fi/console-automation-examples/blob/main/examples/auto-refuel.js).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.brahma.fi/brahma-builder-kit/example-with-the-execution-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
