Pause Guardian - Automated Incident Response Using Defender

One of the key capabilities of OpenZeppelin Defender is its usefulness for security monitoring and general automation. This guide makes use of several Defender components to automate incident response for a given set of conditions on an ERC20 contract.

Overview

A Sentinel monitors transactions on the contract, and is set up to automatically fire an Autotask in the event of a large token transfer. The Autotask script sends a pause transaction to the ERC20 contract via a Relayer.

Overview

Setup

First, sign up for Defender and ensure the EOA used for contract deployment is funded with Sepolia ETH (via a faucet).

Fork the Fork the demo repo.

Clone your fork and install dependencies:

$ git clone https://github.com/[GitHub username]/pause-guardian.git
$ cd pause-guardian
$ npm install

Supply the necessary api keys in your local .env file. The following values are expected:

  • PRIVATE_KEY : For contract deployment on Sepolia network

  • API_KEY : Defender team API key

  • API_SECRET : Defender team API secret

Deploy ERC20 Contract

The OpenZeppelin Contracts Wizard features an API for easy smart contract creation. Generate a pausable ERC20 contract that is Mintable, Pausable, and implements role-based access control, with 1 million tokens pre-minted. The pre-supplied script automates this:

import { erc20 } from '@openzeppelin/wizard'

const params = {
  name: 'ExampleToken',
  symbol: 'ETK',
  mintable: true,
  premint: '1000000',
  access: 'roles',
  pausable: true,
}

const contract = erc20.print(params)

Run with $ npm run generate

Next, run $ npm run deploy to compile and deploy the contract.

  const adminClient = new AdminClient({
    apiKey: process.env.API_KEY,
    apiSecret: process.env.API_SECRET,
  })

  const contractDetails = {
    network: 'sepolia',
    address: contract.address,
    name: NAME,
    abi: contractABI,
  }

  const newAdminContract = await adminClient.addContract(contractDetails)

The script makes use of Defender’s admin-client and loads the contract into the Admin dashboard immediately after deployment.

Create Relayer and Assign the Pauser Role

Create a Relayer to run blockchain transactions via API:

`$ npm run relay`

Now that you have the Relayer, you need to grant it the appropriate role.

The Defender web interface makes it easy to manage access control. Via the Admin dashboard, select the newly-created ERC20 contract, then New Proposal -→ Modify Access. On the next screen, select the PAUSER role from the dropdown and supply the address of the Relayer just created. Select EOA as the execution strategy and select the address of the account used to deploy the contract. Give the access proposal a title and execute it.

Grant pauser role

Create Autotask To Send a Pause Transaction

Now that you have a Relayer with the appropriate access to pause the contract, it is time to build towards automating this functionality.

Create an Autotask that will send a pause transaction to the deployed ERC20 contract using the Relayer.

`$ npm run autotask`

The script creates a new Autotask in Defender and uploads the Autotask code, supplying the ID of the Relayer just created to run the transaction using the Relayer.

async function handler(event) {
  const provider = new DefenderRelayProvider(event)
  const signer = new DefenderRelaySigner(event, provider, { speed: 'fast' })

  const erc20 = new ethers.Contract(ADDRESS, ABI, signer)

  const isPaused = await erc20.paused()
  if (!isPaused) {
    const tx = await erc20.pause()
    console.log(tx)
  } else if (isPaused) {
    console.log('Contract is paused; doing nothing')
  }
}

With the Autotask created, the final building block is to set up a Sentinel to watch for on-chain events and trigger the Autotask.

Create Sentinel to Trigger the Autotask

Sentinels can watch for a wide array of contract conditions and send notifications or fire Autotasks when triggered.

Run $ npm run sentinel to create a Sentinel that triggers the Autotask if a high volume token transfer is detected:

    eventConditions: [
      {
        eventSignature: 'Transfer(address,address,uint256)',
        expression: 'value > 200000e18',
      },
    ],

Test the Auto-Pause Automation

Now that all the building blocks have been laid, the system is ready to be tested. Try transferring an amount of tokens greater than 200000 from the contract to another account. The Sentinel will detect the high volume Transfer event and trigger the Autotask, the Autotask will send the pause transaction via the Relayer, and the ERC20 contract will pause. Attempting any subsequent high volume transfer would therefore fail.