web3 tutorial [10/10] - twilio: push notifications with alchemy notify

by parttimelarry

Blockchain Notifications

Welcome to Part 10. It’s been a long journey. We have successfully created a Solidity smart contract and a React UI, and users are able connect their wallets and schedule meetings.

But as an admin, how do I know if someone has added a meeting? Well, I could refresh my calendar repeatedly and poll for new meetings until one appears. But that wouldn’t be very efficient. It would be great to just receive an email or text message when a new meeting has been scheduled. In a database like PostgreSQL we might use a TRIGGER or the LISTEN and NOTIFY commands to take action on an event, but how do we do this on the blockchain?

Alchemy Notify

We will use Alchemy Notify to add real-time push notifications to our application. With Alchemy Notify, we can receive a webhook message when there is on-chain activity of interest. We can write code to process this webhook message and add our own custom functionality. In our case, we will send an SMS text message when a user interacts with our smart contract.

We’ll create a webhook with Alchemy Notify and write a Node.js application to receive the webhook. The Node.js application will use the Twilio API to send an SMS text message to the admin’s phone number.

To get started, log in to the Alchemy account that you created in Part 5 of this tutorial. Click the Notify tab, then click “Create Webhook”:

Alchemy Notify Webhook

You will select your App in the dropdown and enter your smart contract address in this form. You will also enter a URL for your webhook. We won’t create the webhook in Alchemy just yet since we don’t have our webhook URL ready to receive it. We can code up a web application or a serverless function in any language to provide this webhook URL.

Node.js and Express

Since we’ve been using JavaScript throughout this tutorial, we’ll build a small Node.js application to receive the webhook message. To get started, let’s make a directory called webhooks in the top level of our project. Then we’ll go into that directory and initialize a new node application. In the webhooks directory, we will use npm to install express, body-parser, and twilio.

mkdir webhooks
cd webhooks && npm init -y
npm install express body-parser twilio

In your webhooks directory, create a file called index.js with the following code:

const express = require("express");
const bodyParser = require("body-parser");

const app = express();
const PORT = 3100;

// this application will receive JSON data
app.use(bodyParser.json());

// start the server on port 3100
app.listen(PORT, () => console.log(`Running on port ${PORT}`));

// process a GET request to http://localhost:3100/hello
app.get("/hello", (request, response) => {
    console.log(request.body);

    response.send("hi!");
});

We will use Express, a lightweight Node.js web framework for this application. Run the following command in the webhooks directory to run the server:

node index.js

You should see a server running on port 3100. If you access the URL http://localhost:3100/hello in your web browser, you should receive the message “hi!”. In this case, we are making a GET request since we are simply getting data from the server. A request comes in to /hello and we send it a response back.

Processing a POST request

For Alchemy webhooks, we need to be able to process POST requests since Alchemy will be posting a webhook message that we need to process. Since the webhook message is in JSON format, we add app.use(bodyParser.json()) to parse the JSON request. This will give us a nice JavaScript object that we can use when forming our SMS text message. Let’s expand on our app and add a /webhook endpoint that accepts a POST request.

const express = require("express");
const bodyParser = require("body-parser");

const app = express();
const PORT = 3100;

// this application will receive JSON data
app.use(bodyParser.json());

// start the server on port 3100
app.listen(PORT, () => console.log(`Running on port ${PORT}`));

// process a GET request to http://localhost:3100/hello
app.get("/hello", (request, response) => {
    console.log(request.body);

    response.send("hi!");
});

app.post("/webhook", (request, response) => {
    console.log(request.body);

    const message = "webhook post message received";
    response.send(message);
});

Let’s test that we can receive a POST request with a JSON body. To do this, we need to send a POST request to our application using either the command line or a graphical client. I use Insomnia REST Client, but some people use Postman or curl. Alchemy provides an example webhook request in the Notify documentation. We’ll take this sample and paste it into the body of a new POST request to http://localhost:3100/webhook in Insomnia.

Alchemy Notify Webhook

When you send the post request to your express app, you should receive a response back. Awesome! Let’s see if we can isolate some details from the JSON that is posted. Notice there is an “activity” attribute that contains an array of activity.

Add the following lines to your webhook and let’s return a formatted message.

const activity = request.body.activity;
const message = `💰🚀 ${activity[0].fromAddress} paid you ${activity[0].value} ETH. https://goerli.etherscan.io/tx/${activity[0].hash} 💰🚀`;

Now when you post the example message, you should get a response like the one below. Notice how we substituted in actual values from the activity attribute:

💰🚀 0x86005b57be708e031ea60acf9d3852377e74a6c9 paid you 0.1 ETH. https://goerli.etherscan.io/tx/0xbcbbd7c7de7b835939fb14d4ebe4d31ea6167f4c27c6f0940bb3fa1a90867abe 💰🚀

Can you see how it’s all coming together? Now we just need to figure out how to send this string as an SMS text message.

Twilio API

To send SMS messages, we’ll be using the Twilio API. Twilio is the market leader in programmable telecommunications tools. To get started, Sign up for Twilio and get a Trial Number, Account SID, and Auth Token.

Note: the link above is a referral link. If you end up upgrading, we both get $10 in Twilio credits. There are also ways to send SMS messages by using a special email address. I’ve covered this method in previous videos if you don’t want to use Twilio for whatever reason.

Twilio provides a Node.js example in the documentation. Let’s incorporate the Twilio client into our Express app and then we’ll be all set!

Fill in the accountSid, authToken, fromPhone, and toPhone with your own. We’ll test the endpoint again locally to make sure it sends a text message to our phone.

const express = require("express");
const bodyParser = require("body-parser");

const accountSid = 'YOURSID';
const authToken = 'YOURTOKEN';
const fromPhone = '+1TWILIOPHONE';
const toPhone = '+1YOURPHONE';

const app = express();
const PORT = 3100;

app.use(bodyParser.json());
app.listen(PORT, () => console.log(`Running on port ${PORT}`));

// set up twilio client
const client = require('twilio')(accountSid, authToken);

app.post("/webhook", (request, response) => {
  console.log(request.body);

  const activity = request.body.activity;
  const message = `💰🚀 ${activity[0].fromAddress} paid you ${activity[0].value} ETH. https://goerli.etherscan.io/tx/${activity[0].hash} 💰🚀`;

  console.log('message', message);
  console.log('activity', activity);

  client.messages
    .create({
       body: message,
       from: fromPhone,
       to: toPhone
     })
    .then(message => console.log(message.sid));

  response.status(200).end();
});

Exposing Our Webhook Publicly

Now we need to make our webhook available to Alchemy. We can either deploy our application, expose our IP address, or use a tool like ngrok. If you decide to use ngrok, you simply sign up for an account, download the ngrok command line utility, and run a few commands.

unzip /path/to/ngrok.zip
ngrok authtoken [your token]
ngrok http 3100

This will give you a public ngrok.io address that routes to your local server running on port 3100. We can then provide this webhook URL to Alchemy and test our app. If all went well, we should receive a text message confirming our appointments:

Alchemy Notify Webhook

Validating the Signature

A final point that I didn’t mention in the video. Alchemy Notify provides a mechanism of validating the request by checking for a valid signature. An example JavaScript function is provided that accepts the request and checks for a valid signature. This ensures that the request is coming from Alchemy and not a malicious actor who wants to bomb us with text messages :).

Request Signature

POST /yourWebhookServer/push HTTP/1.1
Content-Type: application/json;
X-Alchemy-Signature: your-hashed-signature
function isValidSignature(request) {    
    const token = 'Auth token provided by Alchemy on the Webhook setup page';
    const headers = request.headers;
    const signature = headers['x-alchemy-signature']; // Lowercase for NodeJS
    const body = request.body;    
    const hmac = crypto.createHmac('sha256', token) // Create a HMAC SHA256 hash using the auth token
    hmac.update(JSON.stringify(body), 'utf8') // Update the token hash with the request body using utf8
    const digest = hmac.digest('hex');     
    return (signature === digest); // If signature equals your computed hash, return true
}

That’s it for this series. Feel free to make this application your own!