@graphprotocol/graph-cli 0.30.0-alpha.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/arweave.ts +76 -0
  2. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/cosmos.ts +341 -0
  3. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/ethereum.ts +39 -0
  4. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/global/global.ts +286 -142
  5. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/index.ts +4 -2
  6. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/package.json +2 -1
  7. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/test/test.js +4 -2
  8. package/examples/basic-event-handlers/node_modules/keccak/build/config.gypi +300 -125
  9. package/examples/basic-event-handlers/node_modules/websocket/build/config.gypi +292 -15
  10. package/examples/basic-event-handlers/package.json +1 -1
  11. package/examples/basic-event-handlers/yarn.lock +4 -4
  12. package/examples/example-subgraph/package.json +1 -1
  13. package/examples/example-subgraph/yarn.lock +4 -4
  14. package/package.json +1 -1
  15. package/src/codegen/schema.js +5 -1
  16. package/src/codegen/schema.test.js +102 -0
  17. package/src/command-helpers/abi.js +69 -0
  18. package/src/command-helpers/scaffold.js +67 -0
  19. package/src/commands/add.js +198 -0
  20. package/src/commands/init.js +25 -72
  21. package/src/protocols/arweave/manifest.graphql +57 -0
  22. package/src/protocols/arweave/scaffold/manifest.js +19 -0
  23. package/src/protocols/arweave/scaffold/mapping.js +53 -0
  24. package/src/protocols/arweave/subgraph.js +20 -0
  25. package/src/protocols/{tendermint → cosmos}/manifest.graphql +6 -0
  26. package/src/protocols/cosmos/scaffold/manifest.js +16 -0
  27. package/src/protocols/cosmos/scaffold/mapping.js +39 -0
  28. package/src/protocols/{tendermint → cosmos}/subgraph.js +2 -1
  29. package/src/protocols/ethereum/codegen/abi.js +21 -4
  30. package/src/protocols/ethereum/scaffold/manifest.js +2 -1
  31. package/src/protocols/index.js +51 -18
  32. package/src/protocols/near/manifest.graphql +1 -0
  33. package/src/protocols/near/scaffold/manifest.js +4 -2
  34. package/src/scaffold/cosmos.test.js +86 -0
  35. package/src/scaffold/ethereum.test.js +2 -1
  36. package/src/scaffold/index.js +5 -3
  37. package/src/scaffold/near.test.js +2 -1
  38. package/src/validation/schema.js +2 -0
  39. package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/tendermint.ts +0 -554
  40. package/examples/basic-event-handlers/node_modules/keccak/build/Makefile +0 -342
  41. package/examples/basic-event-handlers/node_modules/keccak/build/Release/.deps/Release/obj.target/keccak/src/addon.o.d.raw +0 -27
  42. package/examples/basic-event-handlers/node_modules/keccak/build/binding.Makefile +0 -6
  43. package/examples/basic-event-handlers/node_modules/keccak/build/gyp-mac-tool +0 -615
  44. package/examples/basic-event-handlers/node_modules/keccak/build/keccak.target.mk +0 -209
  45. package/examples/basic-event-handlers/node_modules/websocket/build/Makefile +0 -347
  46. package/examples/basic-event-handlers/node_modules/websocket/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d.raw +0 -25
  47. package/examples/basic-event-handlers/node_modules/websocket/build/binding.Makefile +0 -6
  48. package/examples/basic-event-handlers/node_modules/websocket/build/bufferutil.target.mk +0 -192
  49. package/examples/basic-event-handlers/node_modules/websocket/build/gyp-mac-tool +0 -615
  50. package/examples/basic-event-handlers/node_modules/websocket/build/validation.target.mk +0 -192
  51. package/src/codegen/types/defaults.js +0 -34
  52. package/tests/cli/init/ethereum/from-example/.gitattributes +0 -1
  53. package/tests/cli/init/ethereum/from-example/LICENSE +0 -21
  54. package/tests/cli/init/ethereum/from-example/README.md +0 -3
  55. package/tests/cli/init/ethereum/from-example/abis/Gravity.json +0 -1
  56. package/tests/cli/init/ethereum/from-example/bin/Counter.bin +0 -1
  57. package/tests/cli/init/ethereum/from-example/contracts/Gravity.sol +0 -63
  58. package/tests/cli/init/ethereum/from-example/contracts/Migrations.sol +0 -23
  59. package/tests/cli/init/ethereum/from-example/docker-compose.yml +0 -39
  60. package/tests/cli/init/ethereum/from-example/migrations/1_initial_migration.js +0 -5
  61. package/tests/cli/init/ethereum/from-example/migrations/2_deploy_contract.js +0 -5
  62. package/tests/cli/init/ethereum/from-example/migrations/3_create_gravatars.js +0 -15
  63. package/tests/cli/init/ethereum/from-example/networks.json +0 -7
  64. package/tests/cli/init/ethereum/from-example/package.json +0 -23
  65. package/tests/cli/init/ethereum/from-example/schema.graphql +0 -6
  66. package/tests/cli/init/ethereum/from-example/src/mapping.ts +0 -22
  67. package/tests/cli/init/ethereum/from-example/subgraph.yaml +0 -27
  68. package/tests/cli/init/ethereum/from-example/truffle.js +0 -27
  69. package/tests/cli/init/ethereum/from-example/yarn.lock +0 -5977
@@ -1,8 +1,28 @@
1
1
  const fs = require('fs-extra')
2
2
  const path = require('path')
3
+ const prettier = require('prettier')
4
+ const yaml = require('yaml')
3
5
 
4
6
  const { step } = require('./spinner')
5
7
  const Scaffold = require('../scaffold')
8
+ const { generateEventIndexingHandlers } = require('../scaffold/mapping')
9
+ const { generateEventType, abiEvents } = require('../scaffold/schema')
10
+ const { strings } = require('gluegun')
11
+ const { Map } = require('immutable')
12
+
13
+ const generateDataSource = async (protocol, contractName, network, contractAddress, abi) => {
14
+ const protocolManifest = protocol.getManifestScaffold()
15
+
16
+ return Map.of(
17
+ 'kind', protocol.name,
18
+ 'name', contractName,
19
+ 'network', network,
20
+ 'source', yaml.parse(prettier.format(protocolManifest.source({contract: contractAddress, contractName}),
21
+ {parser: 'yaml'})),
22
+ 'mapping', yaml.parse(prettier.format(protocolManifest.mapping({abi, contractName}),
23
+ {parser: 'yaml'}))
24
+ ).asMutable()
25
+ }
6
26
 
7
27
  const generateScaffold = async (
8
28
  {
@@ -13,6 +33,7 @@ const generateScaffold = async (
13
33
  subgraphName,
14
34
  indexEvents,
15
35
  contractName = 'Contract',
36
+ dataSourceName,
16
37
  node,
17
38
  },
18
39
  spinner,
@@ -27,6 +48,7 @@ const generateScaffold = async (
27
48
  network,
28
49
  contractName,
29
50
  subgraphName,
51
+ dataSourceName,
30
52
  node,
31
53
  })
32
54
 
@@ -59,8 +81,53 @@ const writeScaffold = async (scaffold, directory, spinner) => {
59
81
  await writeScaffoldDirectory(scaffold, directory, spinner)
60
82
  }
61
83
 
84
+ const writeABI = async (abi, contractName, abiPath = `./abis/${contractName}.json`) => {
85
+ let data = prettier.format(JSON.stringify(abi.data), {
86
+ parser: 'json',
87
+ })
88
+
89
+ await fs.writeFile(abiPath, data, { encoding: 'utf-8' })
90
+ }
91
+
92
+ const writeSchema = async (abi, protocol, schemaPath, entities) => {
93
+ const events = protocol.hasEvents()
94
+ ? abiEvents(abi).filter(event => entities.indexOf(event.get('name')) === -1).toJS()
95
+ : []
96
+
97
+ let data = prettier.format(
98
+ events.map(
99
+ event => generateEventType(event, protocol.name)
100
+ ).join('\n\n'),
101
+ {
102
+ parser: 'graphql',
103
+ },
104
+ )
105
+
106
+ await fs.appendFile(schemaPath, data, { encoding: 'utf-8' })
107
+ }
108
+
109
+ const writeMapping = async (abi, protocol, contractName, entities) => {
110
+ const events = protocol.hasEvents()
111
+ ? abiEvents(abi).filter(event => entities.indexOf(event.get('name')) === -1).toJS()
112
+ : []
113
+
114
+ let mapping = prettier.format(
115
+ generateEventIndexingHandlers(
116
+ events,
117
+ contractName,
118
+ ),
119
+ { parser: 'typescript', semi: false },
120
+ )
121
+
122
+ await fs.writeFile(`./src/${strings.kebabCase(contractName)}.ts`, mapping, { encoding: 'utf-8' })
123
+ }
124
+
62
125
  module.exports = {
63
126
  ...module.exports,
64
127
  generateScaffold,
65
128
  writeScaffold,
129
+ generateDataSource,
130
+ writeABI,
131
+ writeSchema,
132
+ writeMapping,
66
133
  }
@@ -0,0 +1,198 @@
1
+ const chalk = require('chalk')
2
+ const toolbox = require('gluegun/toolbox')
3
+ const immutable = require('immutable')
4
+ const { withSpinner } = require('../command-helpers/spinner')
5
+ const Subgraph = require('../subgraph')
6
+ const Protocol = require('../protocols')
7
+ const DataSourcesExtractor = require('../command-helpers/data-sources')
8
+ const { generateDataSource, writeABI, writeSchema, writeMapping } = require('../command-helpers/scaffold')
9
+ const { loadAbiFromEtherscan, loadAbiFromBlockScout } = require('../command-helpers/abi')
10
+ const EthereumABI = require('../protocols/ethereum/abi')
11
+ const { fixParameters } = require('../command-helpers/gluegun')
12
+
13
+ const HELP = `
14
+ ${chalk.bold('graph add')} <address> [<subgraph-manifest default: "./subgraph.yaml">]
15
+
16
+ ${chalk.dim('Options:')}
17
+
18
+ --abi <path> Path to the contract ABI (default: download from Etherscan)
19
+ --contract-name Name of the contract (default: Contract)
20
+ --merge-entities Whether to merge entities with the same name (default: false)
21
+ -h, --help Show usage information
22
+ `
23
+
24
+ module.exports = {
25
+ description: 'Creates a new subgraph with basic scaffolding',
26
+ run: async toolbox => {
27
+ // Obtain tools
28
+ let { print, system } = toolbox
29
+
30
+ // Read CLI parameters
31
+ let {
32
+ abi,
33
+ contractName,
34
+ h,
35
+ help,
36
+ mergeEntities
37
+ } = toolbox.parameters.options
38
+
39
+ let address = toolbox.parameters.first
40
+ let manifestPath = toolbox.parameters.second || './subgraph.yaml'
41
+ contractName = contractName || 'Contract'
42
+
43
+ // Validate the address
44
+ if (!address) {
45
+ print.error('No contract address provided')
46
+ process.exitCode = 1
47
+ return
48
+ }
49
+
50
+ try {
51
+ fixParameters(toolbox.parameters, {
52
+ h,
53
+ help,
54
+ mergeEntities,
55
+ })
56
+ } catch (e) {
57
+ print.error(e.message)
58
+ process.exitCode = 1
59
+ return
60
+ }
61
+
62
+ // Show help text if requested
63
+ if (help || h) {
64
+ print.info(HELP)
65
+ return
66
+ }
67
+
68
+ const dataSourcesAndTemplates = await DataSourcesExtractor.fromFilePath(manifestPath)
69
+ let protocol = Protocol.fromDataSources(dataSourcesAndTemplates)
70
+ let manifest = await Subgraph.load(manifestPath, { protocol })
71
+ let network = manifest.result.get('dataSources').get(0).get('network')
72
+ let result = manifest.result.asMutable()
73
+
74
+ let entities = getEntities(manifest)
75
+ let contractNames = getContractNames(manifest)
76
+ if (contractNames.indexOf(contractName) !== -1) {
77
+ print.error(
78
+ `Datasource or template with name ${contractName} already exists, please choose a different name`,
79
+ )
80
+ process.exitCode = 1
81
+ return
82
+ }
83
+
84
+ let ethabi = null
85
+ if (abi) {
86
+ ethabi = EthereumABI.load(contractName, abi)
87
+ } else {
88
+ if (network === 'poa-core') {
89
+ ethabi = await loadAbiFromBlockScout(EthereumABI, network, address)
90
+ } else {
91
+ ethabi = await loadAbiFromEtherscan(EthereumABI, network, address)
92
+ }
93
+ }
94
+
95
+ let { collisionEntities, onlyCollisions, abiData } = updateEventNamesOnCollision(ethabi, entities, contractName, mergeEntities)
96
+ ethabi.data = abiData
97
+
98
+ await writeABI(ethabi, contractName, abi)
99
+ await writeSchema(ethabi, protocol, result.getIn(['schema', 'file']), collisionEntities)
100
+ await writeMapping(ethabi, protocol, contractName, collisionEntities)
101
+
102
+ let dataSources = result.get('dataSources')
103
+ let dataSource = await generateDataSource(protocol,
104
+ contractName, network, address, ethabi)
105
+
106
+ // Handle the collisions edge case by copying another data source yaml data
107
+ if (mergeEntities && onlyCollisions) {
108
+ let firstDataSource = dataSources.get(0)
109
+ let source = dataSource.get('source')
110
+ let mapping = firstDataSource.get('mapping').asMutable()
111
+
112
+ // Save the address of the new data source
113
+ source.abi = firstDataSource.get('source').get('abi')
114
+
115
+ dataSource.set('mapping', mapping)
116
+ dataSource.set('source', source)
117
+ }
118
+
119
+ result.set('dataSources', dataSources.push(dataSource))
120
+
121
+ await Subgraph.write(result, manifestPath)
122
+
123
+ // Detect Yarn and/or NPM
124
+ let yarn = await system.which('yarn')
125
+ let npm = await system.which('npm')
126
+ if (!yarn && !npm) {
127
+ print.error(
128
+ `Neither Yarn nor NPM were found on your system. Please install one of them.`,
129
+ )
130
+ process.exitCode = 1
131
+ return
132
+ }
133
+
134
+ await withSpinner(
135
+ 'Running codegen',
136
+ 'Failed to run codegen',
137
+ 'Warning during codegen',
138
+ async spinner => {
139
+ await system.run(yarn ? 'yarn codegen' : 'npm run codegen')
140
+ }
141
+ )
142
+ }
143
+ }
144
+
145
+ const getEntities = (manifest) => {
146
+ let dataSources = manifest.result.get('dataSources', immutable.List())
147
+ let templates = manifest.result.get('templates', immutable.List())
148
+
149
+ return dataSources
150
+ .concat(templates)
151
+ .map(dataSource => dataSource.getIn(['mapping', 'entities']))
152
+ .flatten()
153
+ }
154
+
155
+ const getContractNames = (manifest) => {
156
+ let dataSources = manifest.result.get('dataSources', immutable.List())
157
+ let templates = manifest.result.get('templates', immutable.List())
158
+
159
+ return dataSources
160
+ .concat(templates)
161
+ .map(dataSource => dataSource.get('name'))
162
+ }
163
+
164
+ const updateEventNamesOnCollision = (ethabi, entities, contractName, mergeEntities) => {
165
+ let abiData = ethabi.data
166
+ let { print } = toolbox
167
+ let collisionEntities = []
168
+ let onlyCollisions = true
169
+
170
+ for (let i = 0; i < abiData.size; i++) {
171
+ let dataRow = abiData.get(i).asMutable()
172
+
173
+ if (dataRow.get('type') === 'event'){
174
+ if (entities.indexOf(dataRow.get('name')) !== -1) {
175
+ if (entities.indexOf(`${contractName}${dataRow.get('name')}`) !== -1) {
176
+ print.error(`Contract name ('${contractName}')
177
+ + event name ('${dataRow.get('name')}') entity already exists.`)
178
+ process.exitCode = 1
179
+ return
180
+ }
181
+
182
+ if (mergeEntities) {
183
+ collisionEntities.push(dataRow.get('name'))
184
+ abiData = abiData.asImmutable().delete(i) // needs to be immutable when deleting, yes you read that right - https://github.com/immutable-js/immutable-js/issues/1901
185
+ i-- // deletion also shifts values to the left
186
+ continue
187
+ } else {
188
+ dataRow.set('name', `${contractName}${dataRow.get('name')}`)
189
+ }
190
+ } else {
191
+ onlyCollisions = false
192
+ }
193
+ }
194
+ abiData = abiData.asMutable().set(i, dataRow)
195
+ }
196
+
197
+ return { abiData, collisionEntities, onlyCollisions }
198
+ }
@@ -16,6 +16,7 @@ const { initNetworksConfig } = require('../command-helpers/network')
16
16
  const { withSpinner, step } = require('../command-helpers/spinner')
17
17
  const { fixParameters } = require('../command-helpers/gluegun')
18
18
  const { chooseNodeUrl } = require('../command-helpers/node')
19
+ const { loadAbiFromEtherscan, loadAbiFromBlockScout } = require('../command-helpers/abi')
19
20
  const { generateScaffold, writeScaffold } = require('../command-helpers/scaffold')
20
21
  const { abiEvents } = require('../scaffold/schema')
21
22
  const { validateContract } = require('../validation')
@@ -57,6 +58,11 @@ ${chalk.dim.underline('NEAR:')}
57
58
 
58
59
  --network <${availableNetworks.get('near').join('|')}>
59
60
  Selects the network the contract is deployed to
61
+
62
+ ${chalk.dim.underline('Cosmos:')}
63
+
64
+ --network <${availableNetworks.get('cosmos').join('|')}>
65
+ Selects the network the contract is deployed to
60
66
  `
61
67
 
62
68
  const processInitForm = async (
@@ -169,6 +175,11 @@ const processInitForm = async (
169
175
  return value
170
176
  },
171
177
  },
178
+ // TODO:
179
+ //
180
+ // protocols that don't support contract
181
+ // - arweave
182
+ // - tendermint
172
183
  {
173
184
  type: 'input',
174
185
  name: 'contract',
@@ -176,10 +187,10 @@ const processInitForm = async (
176
187
  ProtocolContract = protocolInstance.getContract()
177
188
  return `Contract ${ProtocolContract.identifierName()}`
178
189
  },
179
- skip: fromExample !== undefined,
190
+ skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
180
191
  initial: contract,
181
192
  validate: async value => {
182
- if (fromExample !== undefined) {
193
+ if (fromExample !== undefined || !protocolInstance.hasContract()) {
183
194
  return true
184
195
  }
185
196
 
@@ -237,11 +248,11 @@ const processInitForm = async (
237
248
  name: 'contractName',
238
249
  message: 'Contract Name',
239
250
  initial: contractName || 'Contract',
240
- skip: () => fromExample !== undefined,
251
+ skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
241
252
  validate: value => value && value.length > 0,
242
253
  result: value => {
243
- contractName = value;
244
- return value;
254
+ contractName = value
255
+ return value
245
256
  }
246
257
  },
247
258
  ]
@@ -254,68 +265,6 @@ const processInitForm = async (
254
265
  }
255
266
  }
256
267
 
257
- const loadAbiFromBlockScout = async (ABI, network, address) =>
258
- await withSpinner(
259
- `Fetching ABI from BlockScout`,
260
- `Failed to fetch ABI from BlockScout`,
261
- `Warnings while fetching ABI from BlockScout`,
262
- async spinner => {
263
- let result = await fetch(
264
- `https://blockscout.com/${
265
- network.replace('-', '/')
266
- }/api?module=contract&action=getabi&address=${address}`,
267
- )
268
- let json = await result.json()
269
-
270
- // BlockScout returns a JSON object that has a `status`, a `message` and
271
- // a `result` field. The `status` is '0' in case of errors and '1' in
272
- // case of success
273
- if (json.status === '1') {
274
- return new ABI('Contract', undefined, immutable.fromJS(JSON.parse(json.result)))
275
- } else {
276
- throw new Error('ABI not found, try loading it from a local file')
277
- }
278
- },
279
- )
280
-
281
- const getEtherscanLikeAPIUrl = (network) => {
282
- switch(network){
283
- case "mainnet": return `https://api.etherscan.io/api`;
284
- case "arbitrum-one": return `https://api.arbiscan.io/api`;
285
- case "bsc": return `https://api.bscscan.com/api`;
286
- case "matic": return `https://api.polygonscan.com/api`;
287
- case "mumbai": return `https://api-testnet.polygonscan.com/api`;
288
- case "aurora": return `https://api.aurorascan.dev/api`;
289
- case "aurora-testnet": return `https://api-testnet.aurorascan.dev/api`;
290
- case "optimism-kovan": return `https://api-kovan-optimistic.etherscan.io/api`;
291
- case "avalanche": return `https://api.snowtrace.io/api`;
292
- default: return `https://api-${network}.etherscan.io/api`;
293
- }
294
- }
295
-
296
- const loadAbiFromEtherscan = async (ABI, network, address) =>
297
- await withSpinner(
298
- `Fetching ABI from Etherscan`,
299
- `Failed to fetch ABI from Etherscan`,
300
- `Warnings while fetching ABI from Etherscan`,
301
- async spinner => {
302
- const scanApiUrl = getEtherscanLikeAPIUrl(network);
303
- let result = await fetch(
304
- `${scanApiUrl}?module=contract&action=getabi&address=${address}`,
305
- )
306
- let json = await result.json()
307
-
308
- // Etherscan returns a JSON object that has a `status`, a `message` and
309
- // a `result` field. The `status` is '0' in case of errors and '1' in
310
- // case of success
311
- if (json.status === '1') {
312
- return new ABI('Contract', undefined, immutable.fromJS(JSON.parse(json.result)))
313
- } else {
314
- throw new Error('ABI not found, try loading it from a local file')
315
- }
316
- },
317
- )
318
-
319
268
  const loadAbiFromFile = async (ABI, filename) => {
320
269
  let exists = await toolbox.filesystem.exists(filename)
321
270
 
@@ -769,6 +718,7 @@ const initSubgraphFromContract = async (
769
718
  contract,
770
719
  indexEvents,
771
720
  contractName,
721
+ dataSourceName,
772
722
  node,
773
723
  studio,
774
724
  product,
@@ -823,6 +773,7 @@ const initSubgraphFromContract = async (
823
773
  contract,
824
774
  indexEvents,
825
775
  contractName,
776
+ dataSourceName,
826
777
  node,
827
778
  },
828
779
  spinner,
@@ -836,11 +787,13 @@ const initSubgraphFromContract = async (
836
787
  return
837
788
  }
838
789
 
839
- let identifierName = protocolInstance.getContract().identifierName()
840
- let networkConf = await initNetworksConfig(toolbox, directory, identifierName)
841
- if (networkConf !== true) {
842
- process.exitCode = 1
843
- return
790
+ if (protocolInstance.hasContract()) {
791
+ let identifierName = protocolInstance.getContract().identifierName()
792
+ let networkConf = await initNetworksConfig(toolbox, directory, identifierName)
793
+ if (networkConf !== true) {
794
+ process.exitCode = 1
795
+ return
796
+ }
844
797
  }
845
798
 
846
799
  // Initialize a fresh Git repository
@@ -0,0 +1,57 @@
1
+ # Each referenced type's in any of the types below must be listed
2
+ # here either as `scalar` or `type` for the validation code to work
3
+ # properly.
4
+ #
5
+ # That's why `String` is listed as a scalar even though it's built-in
6
+ # GraphQL basic types.
7
+ scalar String
8
+ scalar File
9
+ scalar BigInt
10
+
11
+ type SubgraphManifest {
12
+ specVersion: String!
13
+ schema: Schema!
14
+ description: String
15
+ repository: String
16
+ graft: Graft
17
+ dataSources: [DataSource!]!
18
+ }
19
+
20
+ type Schema {
21
+ file: File!
22
+ }
23
+
24
+ type DataSource {
25
+ kind: String!
26
+ name: String!
27
+ network: String
28
+ source: ContractSource!
29
+ mapping: ContractMapping!
30
+ }
31
+
32
+ type ContractSource {
33
+ owner: String
34
+ startBlock: BigInt
35
+ }
36
+
37
+ type ContractMapping {
38
+ apiVersion: String!
39
+ language: String!
40
+ file: File!
41
+ entities: [String!]!
42
+ blockHandlers: [BlockHandler!]
43
+ transactionHandlers: [TransactionHandler!]
44
+ }
45
+
46
+ type BlockHandler {
47
+ handler: String!
48
+ }
49
+
50
+ type TransactionHandler {
51
+ handler: String!
52
+ }
53
+
54
+ type Graft {
55
+ base: String!
56
+ block: BigInt!
57
+ }
@@ -0,0 +1,19 @@
1
+ const source = ({ contract }) => `
2
+ owner: '${contract}'`
3
+
4
+ const mapping = () => `
5
+ apiVersion: 0.0.5
6
+ language: wasm/assemblyscript
7
+ entities:
8
+ - Block
9
+ - Transaction
10
+ blockHandlers:
11
+ - handler: handleBlock
12
+ transactionHandlers:
13
+ - handler: handleTx
14
+ file: ./src/mapping.ts`
15
+
16
+ module.exports = {
17
+ source,
18
+ mapping,
19
+ }
@@ -0,0 +1,53 @@
1
+ const generatePlaceholderHandlers = () =>
2
+ `
3
+ import { arweave, BigInt } from '@graphprotocol/graph-ts'
4
+ import { Block, Transaction } from '../generated/schema'
5
+
6
+ export function handleBlock(block: arweave.Block): void {
7
+ // Entities can be loaded from the store using a indepHash; this indepHash
8
+ // needs to be unique across all entities of the same type
9
+ let entity = Block.load(block.indepHash.toHexString())
10
+
11
+ // Entities only exist after they have been saved to the store;
12
+ // \`null\` checks allow to create entities on demand
13
+ if (!entity) {
14
+ entity = new Block(block.indepHash.toHexString());
15
+
16
+ // Entity fields can be set using simple assignments
17
+ entity.height = BigInt.fromU64(block.height)
18
+ }
19
+
20
+ // Entities can be written to the store with \`.save()\`
21
+ entity.save()
22
+
23
+ // Note: If a handler doesn't require existing field values, it is faster
24
+ // _not_ to load the entity from the store. Instead, create it fresh with
25
+ // \`new Entity(...)\`, set the fields that should be updated and save the
26
+ // entity back to the store. Fields that were not set or unset remain
27
+ // unchanged, allowing for partial updates to be applied.
28
+ }
29
+
30
+ export function handleTx(tb: arweave.TransactionWithBlockPtr): void {
31
+ const tx = tb.tx;
32
+ const entity = new Transaction(tx.id.toHexString());
33
+
34
+ entity.block = tb.block.indepHash;
35
+ entity.tx_id = tx.id;
36
+ entity.last_tx = tx.lastTx;
37
+ entity.owner = tx.owner;
38
+ entity.tags = saveTags(tx.id.toHexString(), tx.tags);
39
+ entity.data = tx.data;
40
+ entity.data_root = tx.dataRoot;
41
+ entity.data_size = tx.dataSize;
42
+ entity.target = tx.target;
43
+ entity.quantity = tx.quantity;
44
+ entity.signature = tx.signature;
45
+ entity.reward = tx.reward;
46
+
47
+ entity.save();
48
+ }
49
+ `
50
+
51
+ module.exports = {
52
+ generatePlaceholderHandlers,
53
+ }
@@ -0,0 +1,20 @@
1
+ const immutable = require('immutable')
2
+
3
+ module.exports = class ArweaveSubgraph {
4
+ constructor(options = {}) {
5
+ this.manifest = options.manifest
6
+ this.resolveFile = options.resolveFile
7
+ this.protocol = options.protocol
8
+ }
9
+
10
+ validateManifest() {
11
+ return immutable.List()
12
+ }
13
+
14
+ handlerTypes() {
15
+ return immutable.List([
16
+ 'blockHandlers',
17
+ 'transactionHandlers',
18
+ ])
19
+ }
20
+ }
@@ -10,6 +10,7 @@ scalar BigInt
10
10
 
11
11
  type SubgraphManifest {
12
12
  specVersion: String!
13
+ features: [String!]
13
14
  schema: Schema!
14
15
  description: String
15
16
  repository: String
@@ -40,6 +41,11 @@ type ContractMapping {
40
41
  entities: [String!]!
41
42
  blockHandlers: [BlockHandler!]
42
43
  eventHandlers: [EventHandler!]
44
+ transactionHandlers: [TransactionHandler!]
45
+ }
46
+
47
+ type TransactionHandler {
48
+ handler: String!
43
49
  }
44
50
 
45
51
  type BlockHandler {
@@ -0,0 +1,16 @@
1
+ const source = () => `
2
+ startBlock: 0`
3
+
4
+ const mapping = () => `
5
+ apiVersion: 0.0.5
6
+ language: wasm/assemblyscript
7
+ entities:
8
+ - ExampleEntity
9
+ blockHandlers:
10
+ - handler: handleBlock
11
+ file: ./src/mapping.ts`
12
+
13
+ module.exports = {
14
+ source,
15
+ mapping,
16
+ }
@@ -0,0 +1,39 @@
1
+ const generatePlaceholderHandlers = () =>
2
+ `
3
+ import { cosmos, BigInt } from '@graphprotocol/graph-ts'
4
+ import { ExampleEntity } from '../generated/schema'
5
+
6
+ export function handleBlock(block: cosmos.Block): void {
7
+ // Entities can be loaded from the store using a string ID; this ID
8
+ // needs to be unique across all entities of the same type
9
+ let entity = ExampleEntity.load(block.header.hash.toHex())
10
+
11
+ // Entities only exist after they have been saved to the store;
12
+ // \`null\` checks allow to create entities on demand
13
+ if (!entity) {
14
+ entity = new ExampleEntity(block.header.hash.toHex())
15
+
16
+ // Entity fields can be set using simple assignments
17
+ entity.count = BigInt.fromI32(0)
18
+ }
19
+
20
+ // BigInt and BigDecimal math are supported
21
+ entity.count = entity.count + BigInt.fromI32(1)
22
+
23
+ // Entity fields can be set based on receipt information
24
+ entity.height = block.header.height
25
+
26
+ // Entities can be written to the store with \`.save()\`
27
+ entity.save()
28
+
29
+ // Note: If a handler doesn't require existing field values, it is faster
30
+ // _not_ to load the entity from the store. Instead, create it fresh with
31
+ // \`new Entity(...)\`, set the fields that should be updated and save the
32
+ // entity back to the store. Fields that were not set or unset remain
33
+ // unchanged, allowing for partial updates to be applied.
34
+ }
35
+ `
36
+
37
+ module.exports = {
38
+ generatePlaceholderHandlers,
39
+ }