Ethereum — Auto Generate GraphQL Schema, Resolver, and Server

Over the last year, there have been more and more projects that allow developers to use GraphQL to query Ethereum smart contracts. Although this is great, their one big flaw is that it requires the developer to write a GraphQL schema and resolver. That means anytime an update in Solidity is made, the entire stack has to be updated. To make this processes easier, we’re open sourcing ethereum-to-graphql, a library that auto generates the schema and resolver for any Ethereum smart contract.


Problem: ETH → GraphQL Development Process

To use GraphQL with your Ethereum smart contract, you must do the following:

  1. Compile Solidity Contracts
  2. Create GraphQL Schema
  3. Create GraphQL Resolver
  4. Start GraphQL Server
  5. Send GraphQL Query

A GraphQL schema defines the types that are allowed for your query. A GraphQL resolver is a function that is triggered upon query to fetch data. You can make it do whatever you want as long is it returns the data requested.

What is cumbersome about the entire process is that you are forced to rewrite the schema and resolver anytime you change anything in your Smart Contract. This adds development time and complexity. To exemplify the process, take a look at the example below:

// (1) Solidity
function getBalanceInEth(address addr) public returns(uint) {
return ConvertLib.convert(getBalance(addr), 2);
}
// (2) GraphQL Schema
type getBalanceInEth {
balance: Int
}
// (3) GraphQL Resolver
Query: {
async getBalanceInEth() {
let balance = 0;
await contractInstance.getBalanceInEth.call()
.then(bal => (balance = bal / wie)) // convert to ETH
.catch(err => console.log(err));
return {
balance
};
}
}
// (4) Start server --> npm run start
// (5a) GraphQL Query
{
query {
getBalanceInEth(addr: "0x7b2c6c6e9026bcef8d...a") {
balance
}
}
}
// (5b) Result
{
'getBalanceInEth': {
'balance': 5
}
}

Solution: Auto Generate Schema And Resolver

Our package Ethereum-to-GraphQL consumes the ABI(Application Binary Interface) created from compiling Solidity and generates the necessary schema and resolver for ANY smart contract. This means the steps above are reduced to:

  1. Compile Solidity Contracts
  2. Start GraphQL Server
  3. Send GraphQL Query

This process has been vastly improved and streamlined. This means a developer can go directly from Solidity to GraphQL.

Imagine adding this to Etherscan, and being able to explore data within any smart contract. As long as you have access to the correct ABI, everything will be queryable. Just look at how simple the process blow is compared to the example above:

// (1) Solidity
function getBalanceInEth(address addr) public returns(uint) {
return ConvertLib.convert(getBalance(addr), 2);
}
// (2) Start server --> npm run start
// (3a) GraphQL Query
{
query {
getBalanceInEth(addr: "0x7b2c6c6e9026bcef8d...a") {
uint256_0 {
string
int
}
}
}
}

// (3b) Result
{
'getBalanceInEth': {
'uint256_0': {
'string': '5',
'int': 5
}
}
}

Details: How The Internals Work (Short)

When Solidity is compiled, an ABI is created. It describes all the inputs and outputs of the smart contract. For example, the ABI for getBalanceInEth is shown below:

[{
"constant": false,
"inputs": [{
"name": "addr",
"type": "address"
}],
"name": "getBalanceInEth",
"outputs": [{
"name": "",
"type": "uint256"
}],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}]

Given this ABI, we have enough information to generate the correct GraphQL schema.

type Value {
string: String
int: Number
}
type getBalanceInEth {
uint256_0: Value
}

Because we are auto generating everything we decided to define a few helpful types like the Value type. You can choose to see the string or int version of the result. We also have a type for bytes where you can select raw or decoded.

With the schema generated, all that is left to create is the resolver. This requires 2 functions: a function that gets data from Ethereum (sourceFn) and a function to handle the result (outputMapper). As we iterate through we create the wrapper function that binds the two together.

Notice how the SourceFn accepts an outputMapper. This output mapper receives the data and transforms the data in the necessary GraphQL format that allows queries.

Those two methods are combined and attached returned as a resolver for GraphQL to utilize. All you have to do is pass your contract ABI to our package and then pass that into your GraphQL server of choice. Below is an example for Truffle’s Metacoin:

Conclusion: Call To Action

We’re very excited to be open sourcing an internal project to the world. We see this package helping a lot of people as it lowers the barrier for GraphQL and Ethereum. We are very open to accepting pull requests and have a few suggestions on how to contribute.


To keep up with the latest HelloSugoi news as it’s happening, we invite you join our mailing list, message us on Discord, follow us on Twitter @HelloSugoi, and visit our website hellosugoi.