/
queries.ts
87 lines (79 loc) 路 2.64 KB
/
queries.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { argumentsObjectFromField } from 'apollo-utilities';
import { FieldNode, Kind } from 'graphql';
import nanoid from 'nanoid/generate';
import { AbiCoder } from 'web3-eth-abi';
import { AbiItem } from 'web3-utils';
import { QUERY_TYPE } from '../constants';
import { IQueryInfo } from '../types';
import { makeCallTree } from './ast';
import { positionalArgsFromObject } from './web3';
export const extractCallQueries = ({
abi,
abiCoder,
address,
nodes,
}: {
abi: AbiItem[],
abiCoder: AbiCoder,
address: string,
nodes: FieldNode[],
}) => {
const queryInfo: IQueryInfo = {};
const callQueries = nodes.reduce((acc, query) => {
// if no function calls within, we won't do anything for this query
if (!query.selectionSet) { return acc; }
// convert each function call in this query to a call query
const contractCallQueries = query.selectionSet.selections.reduce((queries, selection) => {
if (selection.kind !== Kind.FIELD) { return queries; }
const functionName = selection.name.value;
const trackerId = nanoid('abcdefghijklmnopqrstuvwxyz', 10);
const contractAlias = query.alias ? query.alias.value : query.name.value;
const functionAlias = selection.alias ? selection.alias.value : functionName;
// does the method actually exist in the abi
const abiItem = abi.find(({ name, type, constant }) =>
name === functionName &&
type === 'function' &&
constant === true,
);
if (!abiItem) {
// tslint:disable-next-line:no-console
console.warn('Tried to call a function that doesn\'t exist on the contract');
return queries;
}
// keep track of info about the original query
queryInfo[trackerId] = {
includedFields: [], // TODO: implement - do we need it, or does apollo auto filter?
name: functionName,
path: [contractAlias, functionAlias],
type: QUERY_TYPE,
};
// convert contract queries to calls
return [...queries, createCallQuery({
abiCoder,
abiItem,
address,
args: argumentsObjectFromField(selection, {}) || {},
trackerId,
})];
}, [] as FieldNode[]);
return [...acc, ...contractCallQueries];
}, [] as FieldNode[]);
return { callQueries, queryInfo };
};
export const createCallQuery = ({
abiCoder,
abiItem,
address,
args,
trackerId,
}: {
abiCoder: AbiCoder,
abiItem: AbiItem,
address: string,
args: object,
trackerId: string,
}) => {
const argsArray = positionalArgsFromObject(args, abiItem);
const data = abiCoder.encodeFunctionCall(abiItem, argsArray);
return makeCallTree(trackerId, data, address);
};