DEV Community

Julien Genestoux
Julien Genestoux

Posted on

๐Ÿ” Creating a Crypto Token Gated Express application

Web3 encompasses a plethora of elements that empower us to build a more accessible web for everyone -- including Non-Fungible-Tokens, Token Gating, and decentralization. One perk from the emergence of web3 is moving away from the monetization of our personal data to sell ads. Win-win for us all!

I'm here to show you how simple the process is for token gating content in an Express.js application using the ษ„nlock Protocol. ษ„nlock is a protocol for memberships that allows anyone to deploy a "lock" (a membership smart contract). The contract specifies the terms of the membership: including price, duration, number of members, and many more parameters necessary to run a thriving membership subscription. When a supporter wants to join your membership, they simply pay the specified amount to the contract and will receive an NFT that represents the purchased membership.

app.get('/premium', membersOnly(), (req, res) => {
  // ... unchanged
})
Enter fullscreen mode Exit fullscreen mode

TL;DR: that's all you need to monetize your application!
Try it | Code

๐Ÿš€ Let's get started!

Requirements: you need to use an ethereum/web3 wallet such as MetaMask. We will be using a the Rinkeby test network for this tutorial (no need to spend real money, get some fake Ether on this faucet), and when you decide to ship, you can use Ethereum's mainnet, or the xDAI and Polygon networks if you want to skip the high gas fees.

Deploying a lock ๐Ÿ”’

This is the first step. Unlock Inc provides a front-end for this to be a bit easier, but know that you could also do it directly from the smart contracts.

Go to the Unlock Dashboard. Connect your wallet and then click on the "Create Lock" button.

You can then complete the form to specify the name of your membership contract, the duration (in days) of each membership, the amount of members, as well as the price for each membership.

Dashboard Example

Clicking on "Submit" will prompt you to submit a transaction that will deploy your lock! It usually does not take much more than a few seconds but you will be able to see the address of your lock right away. This is the address of the smart contract you just deployed! It is yours, and only yours.

The one I deployed is called "Another Lock", and its memberships are valid for 1 day, and they cost 0.001 Ether. It is at the address 0x32BeC8e1Fc72509fFa115bd6a0f064Ec77319E4A and we can inspect it on Etherscan.

Deployed Lock

The side bar shows lets you enable credit cards ๐Ÿ’ณ (for users who do not have a crypto wallet, withdraw its funds, and more... feel free to tinker with it!)

Express application for date and times

If you're familiar with Express, you won't learn much so skip to next section!

For this demo, I created a dummy Express application that gives the time โฐ in all timezones. The "free" version just includes 4 cities around the world, but the premium version offers a list of 457 cities!

Here is how the code looks like:

const express = require('express')
const cookieParser = require('cookie-parser')
const zones = require('./zones')

const app = express()
const port = process.env.PORT || 3000;

app.use(cookieParser())

// Helper function that returns date as a string in a city
const dateInZone = (zone, city) => {
  const d = new Date();
  return `${d.toLocaleString('en-US', { timeZone: zone })} in ${city}`
}

// Main root of the application that only shows 4 cities
app.get('/', (req, res) => {
  res.send(`<h1>World Clocks</h1>
  <p>It is now: </p>
  <ul>
    <li>${dateInZone("Asia/Tokyo", "Tokyo")}</li>
    <li>${dateInZone("Asia/Kolkata", "Kolkata")}</li>
    <li>${dateInZone("Europe/Paris", "Paris")}</li>
    <li>${dateInZone("America/New_York", "New York")}</li>
  </ul>
  <p>Premium version: <a href="/premium">select any time zone</a>!</p>`)
})

// Premium route where times in all timezones are displayed!
app.get('/premium', (req, res) => {
  res.send(`<h1>World Clocks</h1>
  <p>Thank you for your support!</p>
  ... (check github for code that renders all zones!)
  <p>Premium version: select any time zone!</p>`)
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})
Enter fullscreen mode Exit fullscreen mode

As you can see it is a pretty standard Express application. The list of zones is stored in zones.js file that you can find on the repo.

Adding the lock

There are multiple ways to integrate ษ„nlock in application, from the lowest levels by hooking into smart contracts directly, all the way to the front-end where some JavaScript can be used to hide/show content.

Here we will use the @unlock-protocol/unlock-express npm module. This module provides a middleware that can be added to any Express route in order to ensure that members only can indeed access the content.

Add the plugin through yarn add @unlock-protocol/unlock-express or npm i --save @unlock-protocol/unlock-express, and then let's include it in our application:

const configUnlock = require('@unlock-protocol/unlock-express')
Enter fullscreen mode Exit fullscreen mode

After this, it's just a matter of configuring the express and using the middleware. The configuration has 3 important elements:

  • yieldPaywallConfig
  • getUserEthereumAddress
  • updateUserEthereumAddress

Let's go through them one by one.

yieldPaywallConfig

This one is used by the plugin to ask the application what configuration it should use. The main aspect of this is of course to list what "locks" should be used specifically. You can find more details in the Unlock docs, but here is what we'll use:

    yieldPaywallConfig: (request) => {
      return {
        locks: {
          '0x32BeC8e1Fc72509fFa115bd6a0f064Ec77319E4A': {
            network: 4,
          },
        },
      }
    },
Enter fullscreen mode Exit fullscreen mode

Quite simply, we say that we only have a single lock that we accept memberships from and it is on the network 4 (the Rinkeby test network).

getUserEthereumAddress

This one is a way for the middleware to ask the application for the user's Ethereum address, if it knows about it. Here for example, the application could look up its user database and just yield the address of the user... etc. However, in this example, we do not have a user database/table, so we will only use a cookie. So here is what that looks like:

    getUserEthereumAddress: async (request) => {
      return request.cookies.userAddress
    },
Enter fullscreen mode Exit fullscreen mode

updateUserEthereumAddress

Finally, the middleware has a way to inform the application of the user's wallet address, so that the application can store it. There again, the application could decide to store that information in a database or, like we do it here, just store the address in a cookie.

    updateUserEthereumAddress: async (
      request,
      response,
      address,
      message
    ) => {
      response.cookie('userAddress', address)
    },
Enter fullscreen mode Exit fullscreen mode

And that's it, here is how the full configuration looks like:


const { membersOnly } = configureUnlock(
  {
    yieldPaywallConfig: () => {
      return {
        locks: {
          '0xafa8fE6D93174D17D98E7A539A90a2EFBC0c0Fc1': {
            network: 4,
          },
        },
      }
    },
    getUserEthereumAddress: async (request) => {
      return request.cookies.userAddress
    },
    updateUserEthereumAddress: async (
      request,
      response,
      address,
    ) => {
      response.cookie('userAddress', address)
    },
  },
  app
)
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก Please note that the configuration block yields the membersOnly middleware that we'll use in the last step!

Adding the middleware to our premium route

Now that the middleware exists, we can very easily plug it into our premium route and here is how it looks like:

app.get('/premium', membersOnly(), (req, res) => {
  // ... unchanged
})
Enter fullscreen mode Exit fullscreen mode

โœ… As you can see, it's quite trivial!

Final touches

A nice addition is to let the user "log out" by just deleting the cookie. For this, we're adding a route:

app.get('/logout', (req, res) => {
  res.clearCookie('userAddress')
  res.redirect('/')
})
Enter fullscreen mode Exit fullscreen mode

๐Ÿงช Want to try it out? We deployed the application on Heroku.
๐Ÿง Want to look at the code? It is all on Github!

A note on security: one could note that the cookie itself could be spoofed. This is true, but also easily fixable using multiple approaches. One of them is to change how updateUserEthereumAddress is implemented by using the 4th and 5th arguments passed to this function that include a signature and the message signed by the user. By recovering the signature, the application can get the certainty that the user's address actually matches the visitor (especially if the message signed includes a timestamp or some kind of unique nonce, configurable in the paywall config).

๐Ÿ‘‹ Closing thoughts!

Using this express plugin means that a developer can start monetizing their application from anywhere in the world, in a matter of minutes. There is no need to agree to terms of service, apply for an API key and no approval or review of your application!

Additionally you as a developer can decide of the benefits you want to offer to your members, whether its access to unique content, special features. One of my favorite feature is that the locks themselves are not even tied to a specific platform. For example, if you decide to do support over Discord, you could use a bot like SwordyBot to easily identify paying members (or grant them access to a special channel!)

Unlock Showcase
Examples of Projects using Unlock!

If you enjoyed this and want to learn more, please join the Unlock Discord. I would also recommend that you apply for an Unlock grant!

๐Ÿ‘ฉโ€๐Ÿ’ป Oh! And we're hiring a Dev Evangelist. Is that you?

Top comments (0)