Schema, mock data and resolvers.

This commit is contained in:
Ashwin Phatak 2021-05-11 16:32:04 +05:30
parent 2b4e1ba315
commit 7133b3ab86
5 changed files with 176 additions and 58 deletions

View File

@ -19,6 +19,7 @@
"homepage": "https://github.com/vulcanize/erc20-watcher#readme", "homepage": "https://github.com/vulcanize/erc20-watcher#readme",
"dependencies": { "dependencies": {
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"apollo-type-bigint": "^0.1.3",
"express": "^4.17.1", "express": "^4.17.1",
"express-graphql": "^0.12.0", "express-graphql": "^0.12.0",
"graphql": "^15.5.0", "graphql": "^15.5.0",

View File

@ -1,29 +1,100 @@
type Author { #
id: Int! # ERC20 GQL schema
firstName: String #
lastName: String
""" # Types
the list of Posts by this author
""" # Support uint256 values.
posts: [Post] scalar BigInt
# Proof for returned data. Serialized blob for now.
# Will be converted into a well defined structure later.
type Proof {
data: String!
} }
type Post { # Result type, with proof, for uint256 method return values.
id: Int! type ResultUInt256 {
title: String value: BigInt!
author: Author
votes: Int # Proof from state/storage trie.
proof: Proof
} }
# the schema allows the following query: # ERC20 Token https://eips.ethereum.org/EIPS/eip-20
# ABI: https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/
type Token {
name: String!
symbol: String!
decimals: Int!
totalSupply: BigInt!
}
# Transfer Event
# Emitted by: `function transfer(address _to, uint256 _value) public returns (bool success)`
# Emitted by: `function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)`
type TransferEvent {
from: String!
to: String!
value: BigInt!
}
# Approval Event
# Emittted by: `function approve(address _spender, uint256 _value) public returns (bool success)`
type ApprovalEvent {
owner: String!
spender: String!
value: BigInt!
}
# All possible event types fired by an ERC20 contract.
union TokenEvent = TransferEvent | ApprovalEvent
# Result type, with proof, for event return values.
type ResultEvent {
event: TokenEvent!
# Proof from receipts trie.
proof: Proof
}
#
# Queries
#
type Query { type Query {
posts: [Post]
author(id: Int!): Author # `function balanceOf(address _owner) public view returns (uint256 balance)`
balanceOf(
blockHash: String!
token: String!
owner: String!
): ResultUInt256!
# `function allowance(address _owner, address _spender) public view returns (uint256 remaining)`
allowance(
blockHash: String!
token: String!
owner: String!
spender: String!
): ResultUInt256!
# Get token events at a certain block, optionally filter by event name.
events(
blockHash: String!
token: String!
name: String
): [ResultEvent!]
} }
# this schema allows the following mutation: #
type Mutation { # Subscriptions
upvotePost ( #
postId: Int! type Subscription {
): Post
# Watch for token events (at head of chain).
onTokenEvent(token: String!): ResultEvent!
} }

View File

@ -1,36 +1,51 @@
import 'lodash';
import 'graphql-import-node'; import 'graphql-import-node';
import { find, filter } from 'lodash';
import { makeExecutableSchema } from '@graphql-tools/schema'; import { makeExecutableSchema } from '@graphql-tools/schema';
import BigInt from 'apollo-type-bigint';
import * as typeDefs from './erc20.graphql'; import * as typeDefs from './erc20.graphql';
import data from './mock-data'; import { blocks } from './mock-data';
const { posts, authors } = data;
const resolvers = { const resolvers = {
Query: { BigInt: new BigInt('bigInt'),
posts: () => posts,
author: (_, { id }) => find(authors, { id }),
},
Mutation: { TokenEvent: {
upvotePost: (_, { postId }) => { __resolveType: (obj) => {
const post = find(posts, { id: postId }); if (obj.owner) {
if (!post) { return 'ApprovalEvent';
throw new Error(`Couldn't find post with id ${postId}`); }
return 'TransferEvent';
}
},
Query: {
balanceOf: (_, { blockHash, token, owner }) => {
console.log('balanceOf', blockHash, token, owner);
return {
value: blocks[blockHash][token].balanceOf[owner],
proof: { data: '' }
} }
post.votes += 1;
return post;
}, },
},
Author: { allowance: (_, { blockHash, token, owner, spender }) => {
posts: author => filter(posts, { authorId: author.id }), console.log('allowance', blockHash, token, owner, spender);
},
Post: { return {
author: post => find(authors, { id: post.authorId }), value: blocks[blockHash][token].allowance[owner][spender],
}, proof: { data: '' }
}
},
events: (_, { blockHash, token, name }) => {
console.log('events', blockHash, token, name);
return blocks[blockHash][token].events
.filter(e => !name || name === e.name)
.map(e => ({ 'event': e }));
}
}
}; };
export const schema = makeExecutableSchema({ export const schema = makeExecutableSchema({

View File

@ -1,17 +1,43 @@
const authors = [ // TODO: Pull mock data for 5 tokens from rinkeby.
{ id: 1, firstName: 'Tom', lastName: 'Coleman' },
{ id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
{ id: 3, firstName: 'Mikhail', lastName: 'Novikov' },
];
const posts = [ export const tokens = {
{ id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 }, '0xd87fea54f506972e3267239ec8e159548892074a': {
{ id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3 }, name: 'ChainLink Token',
{ id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 }, symbol: 'LINK',
{ id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7 }, decimals: 18,
]; totalSupply: '1000000'
}
export default { };
posts,
authors export const blocks = {
// Block hash.
'0x77b5479a5856dd8ec63df6aabf9ce0913071a6dda3a3d54f3c9c940574bcb8ab': {
// ERC20 token address.
'0xd87fea54f506972e3267239ec8e159548892074a': {
balanceOf: {
'0xDC7d7A8920C8Eecc098da5B7522a5F31509b5Bfc': 10000,
'0xCA6D29232D1435D8198E3E5302495417dD073d61': 500
},
allowance: {
'0xDC7d7A8920C8Eecc098da5B7522a5F31509b5Bfc': {
'0xCA6D29232D1435D8198E3E5302495417dD073d61': 100
}
},
events: [
{
name: 'Transfer',
from: '0xDC7d7A8920C8Eecc098da5B7522a5F31509b5Bfc',
to: '0xCA6D29232D1435D8198E3E5302495417dD073d61',
value: 500
},
{
name: 'Approval',
owner: '0xDC7d7A8920C8Eecc098da5B7522a5F31509b5Bfc',
spender: '0xCA6D29232D1435D8198E3E5302495417dD073d61',
value: 100
}
]
}
}
}; };

View File

@ -943,6 +943,11 @@ anymatch@~3.1.1:
normalize-path "^3.0.0" normalize-path "^3.0.0"
picomatch "^2.0.4" picomatch "^2.0.4"
apollo-type-bigint@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/apollo-type-bigint/-/apollo-type-bigint-0.1.3.tgz#9242115ca909b9467ba5c4bc6493a56a06984c0b"
integrity sha512-nyfwEWRZ+kon3Nnot20DufGm2EHZrkJoryYzw3soD+USdxhkcW434w1c/n+mjMLQDl86Z6EvlkvMX5Lordf2Wg==
apollo-upload-client@14.1.3: apollo-upload-client@14.1.3:
version "14.1.3" version "14.1.3"
resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-14.1.3.tgz#91f39011897bd08e99c0de0164e77ad2f3402247" resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-14.1.3.tgz#91f39011897bd08e99c0de0164e77ad2f3402247"