Chainlink External Adapters, Sanity, ERC20 and stuff like that – Part 1: Setting up Sanity.

Chainlink External Adapters, Sanity, ERC20 and stuff like that – Part 1: Setting up Sanity.

This is the first article in a series of articles where we would (hopefully) get a lot going. That was a bit abstract so let me outline the main objectives of this article series:

  1. Discuss the project and set up Sanity
  2. Write an External Adapter to query data from Sanity and host it in a locally running Chainlink Node.
  3. Develop a Smart Contract which would use the above and get a multi-variable output from the Job on the Chainlink Node.

Each bullet point corresponds to an article in the series. To keep things interesting, while we discuss the project and code along, I would like to keep things a bit short. Also, this would be a intermediate level tutorial but I will try to cater to the first-timers and beginners as well. So, without further ado…

Discussing the project and its extended application

In our project, we will define a User schema in Sanity. The User schema will have fields which would pertain to verification of the user. This can be further extended by making a full stack (end to end) solution by putting the Sanity DB we create behind a NodeJS Server, putting a frontend in front of that and then connecting the frontend with the Smart contract we would create in part 3 of the series. While that is certainly lucrative and I would love to create something of that sort, in my opinion it would be too long of an article series and there are already tons of awesome tutorials on how to create a frontend for Web3 and stuff like that. I would like to keep things crisp and focus only on the Backend (smart contract) side of things.

What could be the applications of a flow like this? Imagine a franchise like Walmart or Big Bazaar or Reliance Fresh (if you are Indian) creates a system whereby users are allotted a RFID card which they would need to swap on entering one of the stores. The moment that swipe happens, the user gets logged into a Royalty Points system. The user can buy as many stuffs as he/she/it (you never know when a robot might get offended) wants. At checkout, the user swipes the card again and gets points for the stuff bought. Now the user can choose to stake those points and earn material rewards or increase their own point. The whole Points thing can be represented by an ERC20 token. The franchise may place rules like “a user needs atleast 20 points to start staking or transfer points to someone else” or perhaps “a user needs to be a customer for atleast 1 month before they can use advanced features”.

Now representing the whole time-based flow on a smart contract can clog things up. It might result in gas skyrocketing (current ETH situation). And sure, there’s options like making things private or moving to meta transactions or layer 2 but a better UX solution here might be to take things like these off-chain. Franchises like the ones mentioned regularly store user data for analytics purposes and this would fit into that in a sense that whenever needs the smart contract can use oracles to query some of that data for verification before executing any action.

Once you understand this kind of a flow where Web2 and Web3 co-exist, more and more applications of such projects emerge. You can replace Sanity by something like AWS RDS or DynamoDB or perhaps even GCP’s Bigquery if you want to invoke a job to be run before data is sent to the adapter.

Applications are only limited by the imagination of the thinker here. I would recommend you to checkout this repo. Inside packages/sources is where the treasure trove lies. I couldn’t find an external adapter for Sanity there at the time of writing so I decided to show how to do it with these articles.

Code Where?

_No Offense Crypto Investors_

If you are new to Sanity, I will point you to this awesome short tutorial about Sanity given by Kapehe on Brad Traversy’s Traversy media YouTube channel:

{% embed %}

It might be a little old but not much has changed after that. If you are proceeding in this article from here, I will assume you already have Sanity CLI installed and you have logged In.

Run sanity init and follow the steps shown in the video to get yourself a clean new project. After that, open it in your favourite code editor and then browse to Schemas directory. There should a schema.js in there. We will define a new Users schema in here. Create a file called user.js in the Schemas directory and then define a schema as shown below.

export default {
    name: 'user',
    type: 'document',
      title: 'Users',
    fields: [
        name: 'name',
        type: 'string',
        title: 'Name'
        name: 'isVerified',
        type: 'boolean',
        title: 'User Verified?'
        name: 'signupDate',
        type: 'date',
        title: 'Date of Joining'
        name: 'walletAddress',
        type: 'string',
        title: 'Wallet Address'

In our schema above, we are defining a User who will have a name, a Boolean flag called isVerified, a date type field for denoting the signup date and a string field for wallet address. A little context here: - • The smart contract will contact the Chainlink node with the wallet address of the one invoking a function in the smart contract. • The Chainlink Node will be running a job which will search up the user in Sanity based on wallet address. The Job will then evaluate if the user is verified and whether 20 days have elapsed since the user signed up. • The Job will return these data back to the smart contract and based on the values the invoker/user will be allowed to invoke the function in the smart contract. The name and wallet address fields in our schema would be of string type and the verified flag will be of Boolean type. The Sign-up date would be (and you guessed it right!) of date type. This would render a calendar in the Sanity Studio later when we open it and try to define any user.

You might take this whole thing a step ahead and build a frontend infront of the DB and stuff. You can then implement a user verification flow where perhaps the user would need to verify their email address. We don't do that here (RIP King T’Chala).

RIP King

Once you have defined the schema in user.js save the file and open the schema.js in the same directory. We will import the schema we just defined and then make it so that Sanity can register it. This can be done by placing import user from “./user.js”; where all the file imports are and adding user inside the array in schemaTypes.concat(). The code inside schema.js would look like below after it’s all done.

// First, we must import the schema creator
import createSchema from 'part:@sanity/base/schema-creator'

// Then import schema types from any plugins that might expose them
import schemaTypes from 'all:part:@sanity/base/schema-type'

import user from './user'

// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
  name: 'default',
  types: schemaTypes.concat([

I will recommend you execute sanity start from a terminal at the root of your sanity project. This will open up Sanity studio. Add in a few users with valid wallet addresses. You can paste in Ethereum account addresses from your Metamask here as I have done below.

Sanity Studio

After this, if you want authenticated access to this DB and collection of users inside it that you have created you will need to browse to Sanity and create a view-only type API key. We will need it in the next part where things actually get interesting.

Before we close part 1

In this article I have kept things mostly theoretic and that might irk a few readers. Don’t worry. There’s much code ahead. I just wanted to discuss the main idea of the project in this article. In the next article we will build an external adapter that will use Sanity Client and communicate with the DB we just created, host it in our localhost, run a local Chainlink Node, add the external adapter via bridge. So see you there!

Did you find this article valuable?

Support Abhik Banerjee by becoming a sponsor. Any amount is appreciated!