apollo-link-ethereum-resolver-ethersjs
v0.1.8
Published
Ethers.js bindings for [apollo-link-ethereum](../../../..).
Downloads
20
Readme
apollo-link-ethereum-resolver-ethersjs
Ethers.js bindings for apollo-link-ethereum.
Setup
Install required packages:
$ yarn add apollo-link-ethereum apollo-link-ethereum-resolver-ethersjs
Setup a new Apollo Client:
import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { withClientState } from 'apollo-link-state'
import { EthereumLink } from 'apollo-link-ethereum'
import { EthersResolver } from 'apollo-link-ethereum-resolver-ethersjs'
import { AbiMapping } from 'apollo-link-ethereum'
import { ethers } from 'ethers'
import mkrAbi from './mkrAbi'
export const abiMapping = new AbiMapping()
abiMapping.addAbi('MKR', mkrAbi)
abiMapping.addAddress('MKR', 1, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2')
const provider = new ethers.getDefaultProvider('homestead')
const ethersResolver = new EthersResolver({
abiMapping,
provider
})
const ethereumLink = new EthereumLink(ethersResolver)
const cache = new InMemoryCache()
const stateLink = withClientState({
cache
})
export const apolloClient = new ApolloClient({
cache,
link: ApolloLink.from([stateLink, ethereumLink])
})
Now you can use Ethereum contracts deployed to a network as a data source.
Usage
GraphQL queries are supported by custom directives.
Query Directives
@contract
: declares that the query and sub-queries are for an Ethereum contract.@call
: interprets the query as aneth_call
(default for sub-queries of@contract
)@pastEvents
: retrieves past events of a contract
Subscription Directives
@block
: subscribes to new blocks@contract
: declares that the query and sub-queries are for an Ethereum contract.@events
: listens for new events from the containing@contract
Example using React
import React, { Component } from 'react'
import { Query } from 'react-apollo'
import { get } from 'lodash'
const GET_TOKEN_INFO = gql`
query GetTokenInfo($tokenAddress: String!) {
ERC20Token @contract(address: $tokenAddress) {
totalSupply
myBalance: balanceOf(address: "0xff536c5497c7b244c25ca6b31a5af1545d0c6184")
allEvents @pastEvents(fromBlock: "0", toBlock: "latest")
}
}
`
export class App extends Component {
render() {
return (
<Query
query={GET_TOKEN_INFO}
variables={ { address: "0xa95e94ac1d1e5c57413a281f0197140fbb6d4ccf" } }>
{({ data }) => {
return (
<div className="App">
<header className="App-header">
<p>
Network Status: {get(data, 'status.isConnected', 'Unknown')}
</p>
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<p>
<i>{(get(data, 'ERC20Token.myBalance') || '').toString()}</i>
<br />
<i>{(get(data, 'ERC20Token.totalSupply') || '').toString()}</i>
<br />
<i>{(get(data, 'ERC20Token.allEvents') || []).length}</i>
</p>
</header>
</div>
)
}}
</Query>
)
}
}
Reference
EthersResolver
Provides Ethers.js bindings for EthereumLink. You'll need to create an instance of EthersResolver and pass it to the EthereumLink constructor.
new EthersResolver(options)
Creates a new EthersResolver.
| Option | Description | | --- | --- | | abiMapping | instance of AbiMapping | | provider | Ethers.js provider | defaultFromBlock | the block number to use as the default "fromBlock" for event queries if none is provided. | | defaultToBlock | the block number to use as the default "toBlock" for event queries if none is provided. |
Query Directives
@contract
Sets up the context of a query containing web3 calls. This can be used in either a query or a subscription. The field name to which the @contract
directive is applied will be used as the contract name for ABI and address lookups. You will likely need to use the type
and id
arguments to @contract in order for Apollo Client to correctly cache the results.
For example:
const GET_TOKEN_INFO = gql`
query GetTokenInfo {
myResult: ERC20Token @contract {
totalSupply
}
}
}
`
Here we see the field ERC20Token
has the @contract
directive and is being aliased to myResult
. ERC20Token will be used as the contract name.
@contract Arguments
type
: The name to use as the__typename
for the results. This is important to Apollo Client so that it can distinguish objects in the cache. By default it will use the name of the contract, but if you wish to separate sets of calls you can do so with the type argument.id
: The id to set in the result. Since Ethereum calls do not generate a GraphQL compliant shape, we can set an id field to help Apollo Client out.address
: The address of the contract. Normally this is determined by looking up the address using the AbiMapping. Otherwise you can override it here.
If you would like to override the address:
const GET_TOKEN_INFO = gql`
query GetTokenInfo($tokenAddress: String!) {
ERC20Token @contract(address: $tokenAddress) {
totalSupply
}
}
}
`
The above example will lookup the ABI using the name 'ERC20Token', and set the contract address to be the parameter $tokenAddress
.
@call
By default, fields within a @contract
query that don't have any custom directives will be treated as a @call
. A call is just a standard Ethereum contract call. You can pass parameters to the field, and pass options to the call using the @call directive.
For example:
const GET_TOKEN_INFO = gql`
query GetTokenInfo {
ERC20Token @contract {
totalSupply
}
}
}
`
The field totalSupply
will default to a @call
directive. The directive will call the totalSupply
function on the ERC20Token
contract as defined in the AbiMapping.
@call Arguments
- all arguments to the field will be reduced to a flat array and passed to Ethereum.
- all arguments to the directive will be passed as options to Ethers, allowing you to override gas price, limit etc.
Argument example:
const GET_TOKEN_INFO = gql`
query GetTokenInfo {
ERC20Token @contract {
balanceOf(address: '0x1c127465b8ba9cab06b014551ff5c809cadb59bd') @call(gasLimit: 500000)
}
}
}
`
Will call ERC20Token#balanceOf("0x1c127465b8ba9cab06b014551ff5c809cadb59bd")
with a gas limit of 500000. See Ethers.js for more details on what can be overridden.
@pastEvents
This directive will retrieve all past events for the contract. It will use the name of the field as the filter so you can target specific events. If the name of the field is 'allEvents' then all of the events will be retrieved.
For example:
const GET_TOKEN_INFO = gql`
query GetTokenInfo($address: String!) {
ERC20Token @contract {
allEvents @pastEvents(extraTopics: { types: ["uint256", "address", "address"], values: [null, null, $address] })
}
}
`
The above query will retrieve all past logs with the 3rd indexed Event parameter as the provided $address variable, for the ERC20Token contract defined in the AbiMapping. Because the field name is allEvents
, topic0 will be a wildcard. The remaining topics will be interpreted as 'extra topics'.
You may also wish to look for specific events:
const GET_TOKEN_INFO = gql`
query GetTokenInfo($address: String!) {
ERC20Token @contract {
Transfer(fromAddress: $address) @pastEvents(fromBlock: "430000", toBlock: "450000")
}
}
`
The above query will retrieve all Transfer
events whose first argument matches $address
between blocks 430000 and 450000. Note here that field arguments will be reduced to a flat array, so the fromAddress
in this case is unused.
@pastEvents Arguments
If the field name is allEvents
, then topic0 will be the wildcard topic. Otherwise, the ABI will be used to determine topic0.
- field arguments are reduced to a flat array and encoded as additional topics (not applicable for field name
allEvents
) - directive arguments:
- address: override the contract address
- fromBlock: the starting block number for the search
- toBlock: the last block number for the search
- extraTopics: extra topics to be concatentated onto the base topics. It's an object with two fields
types
andvalues
:- types: an array of the solidity data types of the fields
- values: an array of the values to encode
Subscription Directives
@contract
Same as @contract above.
@events
Same interface as @pastEvents, but used to listen to new events.
For example:
const transferSubscription = gql`
subscription transferSubscription {
ERC20Token @contract {
Transfer @events
}
}
`
Will listen for all Transfer events from the ERC20Token contract.