Skip to main content

Using OpenZeppelin Defender

  1. To start off, you’ll need to create an account on https://defender.openzeppelin.com/
  2. After signing up, users be directed to https://defender.openzeppelin.com/#/admin.
  3. Select Relay on the left panel and click on the ‘Add First Relayer’ button.

Add first Relayer

  1. Name the relayer, select Arbitrum One as the network and click on ‘Create’.

Name the relayer

  1. After creating the relayer, send some ETH (about $50 worth should be sufficient to run the relayer for about a year) to the generated address on the Arbitrum network.

Relayer Setup

  1. After funding the address with ETH, select Autotask on the left panel and click on the ‘Add first Autotask’ button.

Defender Autotask

  1. Name the Autotask and schedule the timespan to run every 15 minutes.

Create Autotask

  1. In the ‘Connect to a relayer’ section, click on the name of the relayer that was set up in set 4. Do not change the Dependency version.

Connect to a relayer

  1. Copy the below code and paste it into the Code section and click on the ‘Create’ button.
const { DefenderRelayProvider, DefenderRelaySigner } = require('defender-relay-client/lib/ethers');
const { ethers } = require('ethers');

const ABI = new ethers.utils.Interface([
"function selectClusters()",
"function EPOCH_LENGTH() view returns(uint256)",
"function MAX_REWARD_FOR_CLUSTER_SELECTION() view returns(uint256)",
"function refundGasForClusterSelection() view returns(uint256)",
"function getCurrentEpoch() view returns(uint256)",
"function getClusters(uint256 epochNumber) view returns(address[])"
]);
const contractAddress = "0xf1E4c2d4B2996C7c34b5a6136861D7385Cb0E843"

exports.handler = async function(credentials) {
const provider = new DefenderRelayProvider(credentials);
const signer = new DefenderRelaySigner(credentials, provider, { speed: 'fastest' });
const contract = new ethers.Contract(contractAddress, ABI, signer);

const maxReward = await contract.MAX_REWARD_FOR_CLUSTER_SELECTION();

const epoch = (await contract.getCurrentEpoch()).toNumber();
console.log(`${(new Date()).toJSON()} Current Epoch is ${epoch}`);
try {
await contract.getClusters(epoch+1);
console.log(`${new Date().toJSON()} Clusters already selected for epoch ${epoch+1}`);
console.log(`${new Date().toJSON()} Waiting to select clusters for epoch ${epoch+2}`);
return;
} catch(e) { console.log(`${new Date().toJSON()} Clusters not yet selected for epoch ${epoch+1}`); }

const estimatedGas = await contract.estimateGas.selectClusters();
const gasPrice = (await provider.getFeeData()).gasPrice;

if((estimatedGas.mul(gasPrice)).gt(maxReward)) {
console.log(`Error: Gas prices too high. Skipping cluster selection for epoch ${epoch+1}`);
return;
}

console.log(`${new Date().toJSON()} Preparing tx to select clusters for epoch ${epoch+1}`);
const tx = await contract.selectClusters();
console.log(`${new Date().toJSON()} Tx submitted to select clusters for epoch ${epoch+1}. Hash: ${tx.hash}`);
const receipt = await tx.wait();
if(receipt.status == 1) {
console.log(`${new Date().toJSON()} SUCCESS: Clusters selected for epoch ${epoch+1}. Hash: ${tx.hash}`);
} else {
console.log(`${new Date().toJSON()} FAILED: Missed clusters selection for epoch ${epoch+1}. Hash: ${tx.hash}`);
}
return receipt.status;
}

Autotask Code

  1. Upon successful creation, users can monitor the status in the dashboard.

Autotask Running