Trigger node

In this tutorial, we are going to create a trigger node that will automatically gets triggered when a new block is mined on Ethereum Mainnet.

Prerequisite

Step 1

Go to Outerbridge/packages/components/nodes, create a folder MinedBlockTrigger. Inside the folder create a file MinedBlockTrigger.ts.

Step 2

Add an icon into the folder. It can be either jpg, png, or svg format. For instance, download this Ethereum Icon and save as mined-block-trigger.svg into the folder.

Step 3

In MinedBlockTrigger.ts, import libraries, interfaces and utils.

import {
INode,
INodeData,
INodeParams,
IProviders,
NodeType,
} from '../../src/Interface';
import { returnNodeExecutionData } from '../../src/utils';
import EventEmitter from 'events';
import { ethers } from "ethers";

Step 4

Create the class and its constructor with properties. Read more on Node Properties.

//...imports
class MinedBlockTrigger extends EventEmitter implements INode {
label: string;
name: string;
type: NodeType;
description?: string;
version: number;
icon: string;
incoming: number;
outgoing: number;
constructor() {
super();
this.label = 'Mined Block Trigger';
this.name = 'minedBlockTrigger';
this.icon = 'mined-block-trigger.svg';
this.type = 'trigger';
this.version = 1.0;
this.description = 'Start workflow whenever a block is mined';
this.incoming = 0;
this.outgoing = 1;
}
}
module.exports = { nodeClass: MinedBlockTrigger }

Step 5

Add node parameters networks. Read more on Node Parameters.

//...imports
class MinedBlockTrigger extends EventEmitter implements INode {
//...properties
networks: INodeParams[];
constructor() {
super();
//...properties
this.networks = [
{
label: 'Network',
name: 'network',
type: 'options',
options: [
{
label: 'Ethereum Mainnet',
name: 'homestead',
},
],
default: 'homestead',
},
] as INodeParams[];
}
}
module.exports = { nodeClass: MinedBlockTrigger }

In this tutorial, we only allow user to select Ethereum Mainnet for simplicity.

Step 6

Create 2 functions after the constructor. The exact 2 functions are needed for every trigger node.

//...imports
class MinedBlockTrigger extends EventEmitter implements INode {
//...properties
//...parameters
constructor() {
//...properties
//...parameters
}
async runTrigger(nodeData: INodeData): Promise<void> {
// function to start listening event
}
async removeTrigger(nodeData: INodeData): Promise<void> {
// function to remove event listener
}
}
module.exports = { nodeClass: MinedBlockTrigger }

Step 7

Inside runTrigger() function, copy and paste the following code:

const networksData = nodeData.networks;
if (networksData === undefined) {
throw new Error('Required data missing');
}
const network = networksData.network as string;
const emitEventKey = nodeData.emitEventKey as string;
let provider: any;
if (network === 'homestead')
provider = new ethers.providers.CloudflareProvider();
provider.on("block", (blockNumber: any) => {
// Emitted on every block change
this.emit(emitEventKey, returnNodeExecutionData(blockNumber));
});

First get the network choice that user has selected from ui. For every trigger node, an emitEventKey will be available from nodeData.emitEventKey. We start declaring the event listener - provider. It this example, we will be using the provider event emitter from ethers.js to listen for "block" event. Whenever a new block is mined, provider.on("block") will gets triggered. We then emit the result using the emitEventKey.

Step 8

Inside removeTrigger() function, copy and paste the following code:

const emitEventKey = nodeData.emitEventKey || '';
if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {
const provider = this.providers[emitEventKey].provider;
provider.off("block");
this.removeAllListeners(emitEventKey);
}

removeTrigger() will be called whenever user stops a deployed workflow, or after finish testing the trigger node. In order to remember which event listener to remove, we are going to declare a class-scoped variable - providers. It will be an object with emitEventKey as the key and event listener object as value.

// providers
{
[emitEventKey]: <EventListenerObject>
}

Step 9

Declare class-scoped variable providers. Then add new key-value pair in runTrigger().

//...imports
class MinedBlockTrigger extends EventEmitter implements INode {
//...properties
//...parameters
providers: IProviders;
constructor() {
//...properties
//...parameters
this.providers = {};
}
async runTrigger(nodeData: INodeData): Promise<void> {
// ...runTrigger
this.providers[emitEventKey] = { provider };
}
}
module.exports = { nodeClass: MinedBlockTrigger }

Step 10

At root path Outerbridge, run yarn run build to build the node into dist folder which will be eventually picked up by server. After build has successfully finished, run yarn run dev for development, or yarn run start for production.

🎉 You have successfully created a trigger node!


Outerbridge Mined Block Trigger

Complete Code

import {
INode,
INodeData,
INodeParams,
IProviders,
NodeType,
} from '../../src/Interface';
import { returnNodeExecutionData } from '../../src/utils';
import EventEmitter from 'events';
import { ethers } from "ethers";
class MinedBlockTrigger extends EventEmitter implements INode {
label: string;
name: string;
type: NodeType;
description?: string;
version: number;
icon: string;
incoming: number;
outgoing: number;
actions: INodeParams[];
networks: INodeParams[];
providers: IProviders;
constructor() {
super();
this.label = 'Mined Block Trigger';
this.name = 'MinedBlockTrigger';
this.icon = 'mined-block-trigger.svg';
this.type = 'trigger';
this.version = 1.0;
this.description = 'Start workflow whenever a block is mined';
this.incoming = 0;
this.outgoing = 1;
this.networks = [
{
label: 'Network',
name: 'network',
type: 'options',
options: [
{
label: 'Ethereum Mainnet',
name: 'homestead',
},
],
default: 'homestead',
},
] as INodeParams[];
this.providers = {};
}
async runTrigger(nodeData: INodeData): Promise<void> {
const networksData = nodeData.networks;
if (networksData === undefined) {
throw new Error('Required data missing');
}
const network = networksData.network as string;
const emitEventKey = nodeData.emitEventKey as string;
let provider: any;
if (network === 'homestead')
provider = new ethers.providers.CloudflareProvider();
provider.on("block", (blockNumber: any) => {
// Emitted on every block change
this.emit(emitEventKey, returnNodeExecutionData(blockNumber));
});
this.providers[emitEventKey] = { provider };
}
async removeTrigger(nodeData: INodeData): Promise<void> {
const emitEventKey = nodeData.emitEventKey || '';
if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {
const provider = this.providers[emitEventKey].provider;
provider.off("block");
this.removeAllListeners(emitEventKey);
}
}
}
module.exports = { nodeClass: MinedBlockTrigger }