Autotasks

The Defender Autotasks service allows you to run small code snippets on a regular basis that can make calls to the Ethereum network or to external APIs. Thanks to tight integration to Relay, you can use Autotasks to automate regular actions on your contracts.

Use cases

Use Autotasks whenever you have a recurrent action you need to run on your Contracts. Since you can run arbitrary code snippets, you can trigger any transactions you need, checking whatever conditions you want, and gathering info from any external APIs you may need.

  • Check your balance in contracts and sweep funds to a wallet upon reaching a threshold

  • Update an on-chain oracle with information from an external API

  • Monitor your contracts to verify their state or check that an off-chain data source is in-sync

  • Poke your contracts to have them transition to a new state once a set of conditions is met

What’s in an Autotask?

In a nutshell, an Autotask is a snippet of javascript code invoked at a regular interval, similar to a serverless function. And as a matter of fact, they are implemented with AWS Lambda functions.

When you create an Autotask, you provide a javascript snippet, choose a frequency for it to run, and optionally link it to a Relayer. Defender will then make sure to invoke your function at the specified interval and provide access to the specified Relayer.

The specified interval is between two consecutive execution starts, not between the end of an Autotask and the beginning of the next one.

Autotasks can also be manually executed from the UI for quick testing. The last 30 Autotask runs will be shown in the Autotask view, allowing you to access the run logs (generated via console.log) for troubleshooting. Additionally, when an Autotask fails, Defender will send you a notification email.

Handler function

The code snippet must export a handler async function that will be invoked on each execution of the Autotask. Given that each Autotask is powered by an AWS Lambda behind the scenes, keep in mind that the same rules apply when dealing with global variables.

exports.handler = async function() {
  // Your code here
}

Relayer integration

If you connect your Autotask to a Relayer, then Defender will automatically inject temporary credentials to access your Relayer from your Autotask. Simply pass the event object to the relayer client in place of the credentials:

const { Relayer } = require('defender-relay-client');

exports.handler = async function(event) {
  const relayer = new Relayer(event);
  // Use relayer as usual
}

Environment

Autotasks are executed in a node 12 runtime with 256mb RAM and a 5-minute timeout. Code snippets are restricted to be smaller than 50mb in size. For ease-of-use, a set of common dependencies are pre-installed in the environment:

"defender-relay-client": "^0.2.4",
"ethers": "5.0.3",
"web3": "1.2.9"
If you need to use any dependency not listed above, you can either use a javascript module bundler such as webpack to include it in your code or just contact us to add it to the set of common dependencies.

Local development

To reproduce exactly the same Autotask environment in your development setup, you can use the following lockfile to install the same set of dependencies via yarn install --frozen-lockfile.

📎 yarn.lock

You can also use the following template for local development, which will run your Autotask code when invoked locally using node. It will load the Relayer credentials from environment variables when run locally, or use the injected credentials when run in Defender.

const { Relayer } = require('defender-relay-client');

// Entrypoint for the Autotask
exports.handler = async function(credentials) {
  const relayer = new Relayer(event);
  // Use relayer for sending txs
}

// To run locally (this code will not be executed in Autotasks)
if (require.main === module) {
  const { API_KEY: apiKey, API_SECRET: apiSecret } = process.env;
  exports.handler({ apiKey, apiSecret })
    .then(() => process.exit(0))
    .catch(error => { console.error(error); process.exit(1); });
}

A complete example

The following example uses ethers.js and the Autotask Relayer integration to call myFunction on a given contract. Note that you would need to replace ADDRESS and ABI in the following code by their corresponding values.

const { ethers } = require("ethers");
const { DefenderRelaySigner } = require('defender-relay-client/lib/ethers');

// Entrypoint for the Autotask
exports.handler = async function(credentials) {
  // Initialize default provider and defender relayer signer
  const provider = ethers.getDefaultProvider('rinkeby');
  const signer = new DefenderRelaySigner(credentials, provider, { speed: 'fast' });

  // Create contract instance from the signer and use it to send a tx
  const contract = new ethers.Contract(ADDRESS, ABI, signer);
  await contract.myFunction();
  console.log(`Called myFunction at ${ADDRESS}`);
}

// To run locally (this code will not be executed in Autotasks)
if (require.main === module) {
  const { API_KEY: apiKey, API_SECRET: apiSecret } = process.env;
  exports.handler({ apiKey, apiSecret })
    .then(() => process.exit(0))
    .catch(error => { console.error(error); process.exit(1); });
}
Note that we are not waiting for the transaction to be mined. The Defender Relayer will take care of monitoring the transaction and resubmitting if needed. The Autotask should just send the request to the Relayer and exit.

Security considerations

Each Autotask is implemented as a separate AWS Lambda, ensuring strong separation among each individual Autotask and across Defender tenants.

Autotasks are restricted via Identity and Access Management to have zero access to the Defender internal infrastructure. The only exception is that an Autotask may access its linked Relayer, which is negotiated via temporary credentials injected by the Defender Autotask service upon each execution. Still, the Autotask can only call the Relayer exposed methods and has no direct access to the backing private key.

Coming up…​

We want to simplify uploading code to an Autotask, so we are experimenting with console-based approaches that can be called from a development environment or hooked into a CD pipeline to ensure your Autotasks are always up to date. Let us know if you have anything in mind!