@graphprotocol/graph-cli 0.24.1 → 0.25.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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/codegen/types/index.js +8 -1
  3. package/src/command-helpers/scaffold.js +66 -0
  4. package/src/commands/init.js +128 -92
  5. package/src/protocols/ethereum/contract.js +22 -0
  6. package/src/protocols/ethereum/scaffold/manifest.js +32 -0
  7. package/src/protocols/ethereum/scaffold/mapping.js +76 -0
  8. package/src/protocols/ethereum/subgraph.js +0 -14
  9. package/src/protocols/index.js +88 -8
  10. package/src/protocols/near/contract.js +46 -0
  11. package/src/protocols/near/scaffold/manifest.js +16 -0
  12. package/src/protocols/near/scaffold/mapping.js +39 -0
  13. package/src/protocols/near/subgraph.js +1 -23
  14. package/src/{scaffold.test.js → scaffold/ethereum.test.js} +27 -19
  15. package/src/scaffold/index.js +149 -0
  16. package/src/scaffold/mapping.js +49 -0
  17. package/src/scaffold/near.test.js +89 -0
  18. package/src/scaffold/schema.js +62 -0
  19. package/src/subgraph.js +8 -0
  20. package/src/validation/contract.js +56 -0
  21. package/src/validation/index.js +2 -1
  22. package/src/validation/manifest.js +0 -29
  23. package/tests/cli/init/{abis → ethereum/abis}/Marketplace.json +0 -0
  24. package/tests/cli/init/{abis → ethereum/abis}/OverloadedElements.json +0 -0
  25. package/tests/cli/init/{abis → ethereum/abis}/SoloMargin.json +0 -0
  26. package/tests/cli/init/{from-contract-with-abi-and-structs.stderr → ethereum/from-contract-with-abi-and-structs.stderr} +1 -1
  27. package/tests/cli/init/{from-contract-with-abi-and-structs.stdout → ethereum/from-contract-with-abi-and-structs.stdout} +0 -0
  28. package/tests/cli/init/{from-contract-with-abi.stderr → ethereum/from-contract-with-abi.stderr} +1 -1
  29. package/tests/cli/init/{from-contract-with-abi.stdout → ethereum/from-contract-with-abi.stdout} +0 -0
  30. package/tests/cli/init/{from-contract-with-overloaded-elements.stderr → ethereum/from-contract-with-overloaded-elements.stderr} +1 -1
  31. package/tests/cli/init/{from-contract-with-overloaded-elements.stdout → ethereum/from-contract-with-overloaded-elements.stdout} +0 -0
  32. package/tests/cli/init/{from-contract.stderr → ethereum/from-contract.stderr} +1 -1
  33. package/tests/cli/init/{from-contract.stdout → ethereum/from-contract.stdout} +0 -0
  34. package/tests/cli/init/{from-example.stderr → ethereum/from-example.stderr} +0 -0
  35. package/tests/cli/init/{from-example.stdout → ethereum/from-example.stdout} +0 -0
  36. package/tests/cli/init/near/from-contract.stderr +12 -0
  37. package/tests/cli/init/near/from-contract.stdout +12 -0
  38. package/tests/cli/init.test.js +143 -112
  39. package/tests/cli/util.js +56 -41
  40. package/tests/cli/validation/near-is-valid/subgraph.yaml +1 -0
  41. package/src/scaffold.js +0 -364
@@ -4,6 +4,12 @@ const EthereumTemplateCodeGen = require('./ethereum/codegen/template')
4
4
  const EthereumABI = require('./ethereum/abi')
5
5
  const EthereumSubgraph = require('./ethereum/subgraph')
6
6
  const NearSubgraph = require('./near/subgraph')
7
+ const EthereumContract = require('./ethereum/contract')
8
+ const NearContract = require('./near/contract')
9
+ const EthereumManifestScaffold = require('./ethereum/scaffold/manifest')
10
+ const NearManifestScaffold = require('./near/scaffold/manifest')
11
+ const EthereumMappingScaffold = require('./ethereum/scaffold/mapping')
12
+ const NearMappingScaffold = require('./near/scaffold/mapping')
7
13
 
8
14
  module.exports = class Protocol {
9
15
  static fromDataSources(dataSourcesAndTemplates) {
@@ -15,7 +21,7 @@ module.exports = class Protocol {
15
21
  this.name = this.normalizeName(name)
16
22
  }
17
23
 
18
- availableProtocols() {
24
+ static availableProtocols() {
19
25
  return immutable.fromJS({
20
26
  // `ethereum/contract` is kept for backwards compatibility.
21
27
  // New networks (or protocol perhaps) shouldn't have the `/contract` anymore (unless a new case makes use of it).
@@ -24,15 +30,58 @@ module.exports = class Protocol {
24
30
  })
25
31
  }
26
32
 
33
+ static availableNetworks() {
34
+ return immutable.fromJS({
35
+ ethereum: [
36
+ 'mainnet',
37
+ 'kovan',
38
+ 'rinkeby',
39
+ 'ropsten',
40
+ 'goerli',
41
+ 'poa-core',
42
+ 'poa-sokol',
43
+ 'xdai',
44
+ 'matic',
45
+ 'mumbai',
46
+ 'fantom',
47
+ 'bsc',
48
+ 'chapel',
49
+ 'clover',
50
+ 'avalanche',
51
+ 'fuji',
52
+ 'celo',
53
+ 'celo-alfajores',
54
+ 'fuse',
55
+ 'mbase',
56
+ 'arbitrum-one',
57
+ 'arbitrum-rinkeby',
58
+ 'optimism',
59
+ 'optimism-kovan',
60
+ ],
61
+ near: [
62
+ 'near-mainnet',
63
+ ],
64
+ })
65
+ }
66
+
27
67
  normalizeName(name) {
28
- return this.availableProtocols()
68
+ return Protocol.availableProtocols()
29
69
  .findKey(possibleNames => possibleNames.includes(name))
30
70
  }
31
71
 
72
+ displayName() {
73
+ switch (this.name) {
74
+ case 'ethereum':
75
+ return 'Ethereum'
76
+ case 'near':
77
+ return 'NEAR'
78
+ }
79
+ }
80
+
32
81
  // Receives a data source kind, and checks if it's valid
33
82
  // for the given protocol instance (this).
34
83
  isValidKindName(kind) {
35
- return this.availableProtocols()
84
+ return Protocol.availableProtocols()
36
85
  .get(this.name, immutable.List())
37
86
  .includes(kind)
38
87
  }
@@ -40,7 +89,15 @@ module.exports = class Protocol {
40
89
  hasABIs() {
41
90
  switch (this.name) {
42
91
  case 'ethereum':
43
- case 'ethereum/contract':
92
+ return true
93
+ case 'near':
94
+ return false
95
+ }
96
+ }
97
+
98
+ hasEvents() {
99
+ switch (this.name) {
100
+ case 'ethereum':
44
101
  return true
45
102
  case 'near':
46
103
  return false
@@ -50,7 +107,6 @@ module.exports = class Protocol {
50
107
  getTypeGenerator(options) {
51
108
  switch (this.name) {
52
109
  case 'ethereum':
53
- case 'ethereum/contract':
54
110
  return new EthereumTypeGenerator(options)
55
111
  case 'near':
56
112
  return null
@@ -60,7 +116,6 @@ module.exports = class Protocol {
60
116
  getTemplateCodeGen(template) {
61
117
  switch (this.name) {
62
118
  case 'ethereum':
63
- case 'ethereum/contract':
64
119
  return new EthereumTemplateCodeGen(template)
65
120
  default:
66
121
  throw new Error(
@@ -72,7 +127,6 @@ module.exports = class Protocol {
72
127
  getABI() {
73
128
  switch (this.name) {
74
129
  case 'ethereum':
75
- case 'ethereum/contract':
76
130
  return EthereumABI
77
131
  case 'near':
78
132
  return null
@@ -84,7 +138,6 @@ module.exports = class Protocol {
84
138
 
85
139
  switch (this.name) {
86
140
  case 'ethereum':
87
- case 'ethereum/contract':
88
141
  return new EthereumSubgraph(optionsWithProtocol)
89
142
  case 'near':
90
143
  return new NearSubgraph(optionsWithProtocol)
@@ -94,4 +147,31 @@ module.exports = class Protocol {
94
147
  )
95
148
  }
96
149
  }
150
+
151
+ getContract() {
152
+ switch (this.name) {
153
+ case 'ethereum':
154
+ return EthereumContract
155
+ case 'near':
156
+ return NearContract
157
+ }
158
+ }
159
+
160
+ getManifestScaffold() {
161
+ switch (this.name) {
162
+ case 'ethereum':
163
+ return EthereumManifestScaffold
164
+ case 'near':
165
+ return NearManifestScaffold
166
+ }
167
+ }
168
+
169
+ getMappingScaffold() {
170
+ switch (this.name) {
171
+ case 'ethereum':
172
+ return EthereumMappingScaffold
173
+ case 'near':
174
+ return NearMappingScaffold
175
+ }
176
+ }
97
177
  }
@@ -0,0 +1,46 @@
1
+ const MINIMUM_ACCOUNT_ID_LENGTH = 2
2
+ const MAXIMUM_ACCOUNT_ID_LENGTH = 64
3
+
4
+ const RULES_URL = 'https://docs.near.org/docs/concepts/account#account-id-rules'
5
+
6
+ module.exports = class NearContract {
7
+ static identifierName() {
8
+ return 'account'
9
+ }
10
+
11
+ constructor(account) {
12
+ this.account = account
13
+ }
14
+
15
+ _validateLength() {
16
+ return this.account.length >= MINIMUM_ACCOUNT_ID_LENGTH &&
17
+ this.account.length <= MAXIMUM_ACCOUNT_ID_LENGTH
18
+ }
19
+
20
+ _validateFormat() {
21
+ const pattern = /^(([a-z\d]+[\-_])*[a-z\d]+\.)*([a-z\d]+[\-_])*[a-z\d]+$/
22
+
23
+ return pattern.test(this.account)
24
+ }
25
+
26
+ validate() {
27
+ if (!this._validateLength(this.account)) {
28
+ return {
29
+ valid: false,
30
+ error: `Account must be between '${MINIMUM_ACCOUNT_ID_LENGTH}' and '${MAXIMUM_ACCOUNT_ID_LENGTH}' characters, see ${RULES_URL}`,
31
+ }
32
+ }
33
+
34
+ if (!this._validateFormat()) {
35
+ return {
36
+ valid: false,
37
+ error: `Account must conform to the rules on ${RULES_URL}`,
38
+ }
39
+ }
40
+
41
+ return {
42
+ valid: true,
43
+ error: null,
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,16 @@
1
+ const source = ({ contract }) => `
2
+ account: '${contract}'`
3
+
4
+ const mapping = () => `
5
+ apiVersion: 0.0.5
6
+ language: wasm/assemblyscript
7
+ entities:
8
+ - ExampleEntity
9
+ receiptHandlers:
10
+ - handler: handleReceipt
11
+ file: ./src/mapping.ts`
12
+
13
+ module.exports = {
14
+ source,
15
+ mapping,
16
+ }
@@ -0,0 +1,39 @@
1
+ const generatePlaceholderHandlers = () =>
2
+ `
3
+ import { near, BigInt } from '@graphprotocol/graph-ts'
4
+ import { ExampleEntity } from '../generated/schema'
5
+
6
+ export function handleReceipt(receiptWithOutcome: near.ReceiptWithOutcome): 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(receiptWithOutcome.receipt.id.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(receiptWithOutcome.receipt.id.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.block = receiptWithOutcome.block.header.hash
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
+ }
@@ -1,5 +1,4 @@
1
1
  const immutable = require('immutable')
2
- const { validateContractValues } = require('../../validation')
3
2
 
4
3
  module.exports = class NearSubgraph {
5
4
  constructor(options = {}) {
@@ -9,28 +8,7 @@ module.exports = class NearSubgraph {
9
8
  }
10
9
 
11
10
  validateManifest() {
12
- return this.validateContractAccounts()
13
- }
14
-
15
- validateContractAccounts() {
16
- // Reference: https://docs.near.org/docs/concepts/account#account-id-rules
17
- const MINIMUM_ACCOUNT_ID_LENGTH = 2
18
- const MAXIMUM_ACCOUNT_ID_LENGTH = 64
19
- const validateLength = accountId =>
20
- accountId.length >= MINIMUM_ACCOUNT_ID_LENGTH &&
21
- accountId.length <= MAXIMUM_ACCOUNT_ID_LENGTH
22
- const nearAccountIdPattern = /^(([a-z\d]+[\-_])*[a-z\d]+\.)*([a-z\d]+[\-_])*[a-z\d]+$/
23
-
24
- return validateContractValues(
25
- this.manifest,
26
- this.protocol,
27
- 'account',
28
- accountId => validateLength(accountId) && nearAccountIdPattern.test(accountId),
29
- `Must be between '${MINIMUM_ACCOUNT_ID_LENGTH}' and '${MAXIMUM_ACCOUNT_ID_LENGTH}' characters
30
- An Account ID consists of Account ID parts separated by '.' (dots)
31
- Each Account ID part consists of lowercase alphanumeric symbols separated by either a '_' (underscore) or '-' (dash)
32
- For further information look for: https://docs.near.org/docs/concepts/account#account-id-rules`,
33
- )
11
+ return immutable.List()
34
12
  }
35
13
 
36
14
  handlerTypes() {
@@ -1,11 +1,7 @@
1
- const ABI = require('./protocols/ethereum/abi')
1
+ const ABI = require('../protocols/ethereum/abi')
2
2
  const immutable = require('immutable')
3
- const {
4
- generateEventFieldAssignments,
5
- generateManifest,
6
- generateMapping,
7
- generateSchema,
8
- } = require('./scaffold')
3
+ const Scaffold = require('./')
4
+ const Protocol = require('../protocols')
9
5
 
10
6
  const TEST_EVENT = {
11
7
  name: 'ExampleEvent',
@@ -73,21 +69,33 @@ const TEST_ABI = new ABI(
73
69
  ]),
74
70
  )
75
71
 
76
- describe('Subgraph scaffolding', () => {
72
+ const protocol = new Protocol('ethereum')
73
+
74
+ const scaffoldOptions = {
75
+ protocol,
76
+ abi: TEST_ABI,
77
+ contract: '0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d',
78
+ network: 'kovan',
79
+ contractName: 'Contract',
80
+ }
81
+
82
+ const scaffold = new Scaffold(scaffoldOptions)
83
+
84
+ const scaffoldWithIndexEvents = new Scaffold({
85
+ ...scaffoldOptions,
86
+ indexEvents: true,
87
+ })
88
+
89
+ describe('Ethereum subgraph scaffolding', () => {
77
90
  test('Manifest', () => {
78
91
  expect(
79
- generateManifest({
80
- abi: TEST_ABI,
81
- network: 'kovan',
82
- address: '0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d',
83
- contractName: 'Contract'
84
- }),
92
+ scaffold.generateManifest(),
85
93
  ).toEqual(`\
86
94
  specVersion: 0.0.1
87
95
  schema:
88
96
  file: ./schema.graphql
89
97
  dataSources:
90
- - kind: ethereum/contract
98
+ - kind: ethereum
91
99
  name: Contract
92
100
  network: kovan
93
101
  source:
@@ -113,7 +121,7 @@ dataSources:
113
121
  })
114
122
 
115
123
  test('Schema (default)', () => {
116
- expect(generateSchema({ abi: TEST_ABI })).toEqual(`\
124
+ expect(scaffold.generateSchema()).toEqual(`\
117
125
  type ExampleEntity @entity {
118
126
  id: ID!
119
127
  count: BigInt!
@@ -124,7 +132,7 @@ type ExampleEntity @entity {
124
132
  })
125
133
 
126
134
  test('Schema (for indexing events)', () => {
127
- expect(generateSchema({ abi: TEST_ABI, indexEvents: true })).toEqual(`\
135
+ expect(scaffoldWithIndexEvents.generateSchema()).toEqual(`\
128
136
  type ExampleEvent @entity {
129
137
  id: ID!
130
138
  a: BigInt! # uint256
@@ -147,7 +155,7 @@ type ExampleEvent1 @entity {
147
155
  })
148
156
 
149
157
  test('Mapping (default)', () => {
150
- expect(generateMapping({ abi: TEST_ABI, contractName: 'Contract' })).toEqual(`\
158
+ expect(scaffold.generateMapping()).toEqual(`\
151
159
  import { BigInt } from "@graphprotocol/graph-ts"
152
160
  import {
153
161
  Contract,
@@ -204,7 +212,7 @@ export function handleExampleEvent1(event: ExampleEvent1): void {}
204
212
  })
205
213
 
206
214
  test('Mapping (for indexing events)', () => {
207
- expect(generateMapping({ abi: TEST_ABI, indexEvents: true, contractName: 'Contract' })).toEqual(`\
215
+ expect(scaffoldWithIndexEvents.generateMapping()).toEqual(`\
208
216
  import {
209
217
  ExampleEvent as ExampleEventEvent,
210
218
  ExampleEvent1 as ExampleEvent1Event
@@ -0,0 +1,149 @@
1
+ const prettier = require('prettier')
2
+ const pkginfo = require('pkginfo')(module)
3
+
4
+ const GRAPH_CLI_VERSION = process.env.GRAPH_CLI_TESTS
5
+ // JSON.stringify should remove this key, we will install the local
6
+ // graph-cli for the tests using `npm link` instead of fetching from npm.
7
+ ? undefined
8
+ // For scaffolding real subgraphs
9
+ : `${module.exports.version}`
10
+
11
+ const {
12
+ abiEvents,
13
+ generateEventType,
14
+ generateExampleEntityType,
15
+ } = require('./schema')
16
+ const { generateEventIndexingHandlers } = require('./mapping')
17
+ const { getSubgraphBasename } = require('../command-helpers/subgraph')
18
+
19
+ module.exports = class Scaffold {
20
+ constructor(options = {}) {
21
+ this.protocol = options.protocol
22
+ this.abi = options.abi
23
+ this.indexEvents = options.indexEvents
24
+ this.contract = options.contract
25
+ this.network = options.network
26
+ this.contractName = options.contractName
27
+ this.subgraphName = options.subgraphName
28
+ this.node = options.node
29
+ }
30
+
31
+ generatePackageJson() {
32
+ return prettier.format(
33
+ JSON.stringify({
34
+ name: getSubgraphBasename(this.subgraphName),
35
+ license: 'UNLICENSED',
36
+ scripts: {
37
+ codegen: 'graph codegen',
38
+ build: 'graph build',
39
+ deploy:
40
+ `graph deploy ` +
41
+ `--node ${this.node} ` +
42
+ this.subgraphName,
43
+ 'create-local': `graph create --node http://localhost:8020/ ${this.subgraphName}`,
44
+ 'remove-local': `graph remove --node http://localhost:8020/ ${this.subgraphName}`,
45
+ 'deploy-local':
46
+ `graph deploy ` +
47
+ `--node http://localhost:8020/ ` +
48
+ `--ipfs http://localhost:5001 ` +
49
+ this.subgraphName,
50
+ },
51
+ dependencies: {
52
+ '@graphprotocol/graph-cli': GRAPH_CLI_VERSION,
53
+ '@graphprotocol/graph-ts': `0.24.1`,
54
+ },
55
+ }),
56
+ { parser: 'json' },
57
+ )
58
+ }
59
+
60
+ generateManifest() {
61
+ const protocolManifest = this.protocol.getManifestScaffold()
62
+
63
+ return prettier.format(`
64
+ specVersion: 0.0.1
65
+ schema:
66
+ file: ./schema.graphql
67
+ dataSources:
68
+ - kind: ${this.protocol.name}
69
+ name: ${this.contractName}
70
+ network: ${this.network}
71
+ source: ${protocolManifest.source(this)}
72
+ mapping: ${protocolManifest.mapping(this)}
73
+ `,
74
+ { parser: 'yaml' },
75
+ )
76
+ }
77
+
78
+ generateSchema() {
79
+ const hasEvents = this.protocol.hasEvents()
80
+ const events = hasEvents
81
+ ? abiEvents(this.abi).toJS()
82
+ : []
83
+
84
+ return prettier.format(
85
+ hasEvents && this.indexEvents
86
+ ? events.map(
87
+ event => generateEventType(event, this.protocol.name)
88
+ )
89
+ .join('\n\n')
90
+ : generateExampleEntityType(this.protocol, events),
91
+ {
92
+ parser: 'graphql',
93
+ },
94
+ )
95
+ }
96
+
97
+ generateTsConfig() {
98
+ return prettier.format(
99
+ JSON.stringify({
100
+ extends: '@graphprotocol/graph-ts/types/tsconfig.base.json',
101
+ include: ['src'],
102
+ }),
103
+ { parser: 'json' },
104
+ )
105
+ }
106
+
107
+ generateMapping() {
108
+ const hasEvents = this.protocol.hasEvents()
109
+ const events = hasEvents
110
+ ? abiEvents(this.abi).toJS()
111
+ : []
112
+
113
+ const protocolMapping = this.protocol.getMappingScaffold()
114
+
115
+ return prettier.format(
116
+ hasEvents && this.indexEvents
117
+ ? generateEventIndexingHandlers(
118
+ events,
119
+ this.contractName,
120
+ )
121
+ : protocolMapping.generatePlaceholderHandlers({
122
+ ...this,
123
+ events,
124
+ }),
125
+ { parser: 'typescript', semi: false },
126
+ )
127
+ }
128
+
129
+ generateABIs() {
130
+ return this.protocol.hasABIs()
131
+ ? {
132
+ [`${this.contractName}.json`]: prettier.format(JSON.stringify(this.abi.data), {
133
+ parser: 'json',
134
+ }),
135
+ }
136
+ : undefined
137
+ }
138
+
139
+ generate() {
140
+ return {
141
+ 'package.json': this.generatePackageJson(),
142
+ 'subgraph.yaml': this.generateManifest(),
143
+ 'schema.graphql': this.generateSchema(),
144
+ 'tsconfig.json': this.generateTsConfig(),
145
+ src: { 'mapping.ts': this.generateMapping() },
146
+ abis: this.generateABIs(),
147
+ }
148
+ }
149
+ }
@@ -0,0 +1,49 @@
1
+ const path = require('path')
2
+
3
+ const util = require('../codegen/util')
4
+
5
+ const generateFieldAssignment = path =>
6
+ `entity.${path.join('_')} = event.params.${path.join('.')}`
7
+
8
+ const generateFieldAssignments = ({ index, input }) =>
9
+ input.type === 'tuple'
10
+ ? util
11
+ .unrollTuple({ value: input, index, path: [input.name || `param${index}`] })
12
+ .map(({ path }) => generateFieldAssignment(path))
13
+ : generateFieldAssignment([input.name || `param${index}`])
14
+
15
+ const generateEventFieldAssignments = event =>
16
+ event.inputs.reduce(
17
+ (acc, input, index) => acc.concat(generateFieldAssignments({ input, index })),
18
+ [],
19
+ )
20
+
21
+ const generateEventIndexingHandlers = (events, contractName) =>
22
+ `
23
+ import { ${events.map(
24
+ event => `${event._alias} as ${event._alias}Event`,
25
+ )}} from '../generated/${contractName}/${contractName}'
26
+ import { ${events.map(event => event._alias)} } from '../generated/schema'
27
+
28
+ ${events
29
+ .map(
30
+ event =>
31
+ `
32
+ export function handle${event._alias}(event: ${event._alias}Event): void {
33
+ let entity = new ${
34
+ event._alias
35
+ }(event.transaction.hash.toHex() + '-' + event.logIndex.toString())
36
+ ${generateEventFieldAssignments(event).join('\n')}
37
+ entity.save()
38
+ }
39
+ `,
40
+ )
41
+ .join('\n')}
42
+ `
43
+
44
+ module.exports = {
45
+ generateFieldAssignment,
46
+ generateFieldAssignments,
47
+ generateEventFieldAssignments,
48
+ generateEventIndexingHandlers,
49
+ }
@@ -0,0 +1,89 @@
1
+ const immutable = require('immutable')
2
+ const Scaffold = require('./')
3
+ const Protocol = require('../protocols')
4
+
5
+ const protocol = new Protocol('near')
6
+
7
+ const scaffoldOptions = {
8
+ protocol,
9
+ contract: 'abc.def.near',
10
+ network: 'near-mainnet',
11
+ contractName: 'Contract',
12
+ }
13
+
14
+ const scaffold = new Scaffold(scaffoldOptions)
15
+
16
+ describe('NEAR subgraph scaffolding', () => {
17
+ test('Manifest', () => {
18
+ expect(
19
+ scaffold.generateManifest(),
20
+ ).toEqual(`\
21
+ specVersion: 0.0.1
22
+ schema:
23
+ file: ./schema.graphql
24
+ dataSources:
25
+ - kind: near
26
+ name: Contract
27
+ network: near-mainnet
28
+ source:
29
+ account: "abc.def.near"
30
+ mapping:
31
+ apiVersion: 0.0.5
32
+ language: wasm/assemblyscript
33
+ entities:
34
+ - ExampleEntity
35
+ receiptHandlers:
36
+ - handler: handleReceipt
37
+ file: ./src/mapping.ts
38
+ `)
39
+ })
40
+
41
+ test('Schema (default)', () => {
42
+ expect(scaffold.generateSchema()).toEqual(`\
43
+ type ExampleEntity @entity {
44
+ id: ID!
45
+ block: Bytes!
46
+ count: BigInt!
47
+ }
48
+ `)
49
+ })
50
+
51
+ test('Mapping (default)', () => {
52
+ expect(scaffold.generateMapping()).toEqual(`\
53
+ import { near, BigInt } from "@graphprotocol/graph-ts"
54
+ import { ExampleEntity } from "../generated/schema"
55
+
56
+ export function handleReceipt(
57
+ receiptWithOutcome: near.ReceiptWithOutcome
58
+ ): void {
59
+ // Entities can be loaded from the store using a string ID; this ID
60
+ // needs to be unique across all entities of the same type
61
+ let entity = ExampleEntity.load(receiptWithOutcome.receipt.id.toHex())
62
+
63
+ // Entities only exist after they have been saved to the store;
64
+ // \`null\` checks allow to create entities on demand
65
+ if (!entity) {
66
+ entity = new ExampleEntity(receiptWithOutcome.receipt.id.toHex())
67
+
68
+ // Entity fields can be set using simple assignments
69
+ entity.count = BigInt.fromI32(0)
70
+ }
71
+
72
+ // BigInt and BigDecimal math are supported
73
+ entity.count = entity.count + BigInt.fromI32(1)
74
+
75
+ // Entity fields can be set based on receipt information
76
+ entity.block = receiptWithOutcome.block.header.hash
77
+
78
+ // Entities can be written to the store with \`.save()\`
79
+ entity.save()
80
+
81
+ // Note: If a handler doesn't require existing field values, it is faster
82
+ // _not_ to load the entity from the store. Instead, create it fresh with
83
+ // \`new Entity(...)\`, set the fields that should be updated and save the
84
+ // entity back to the store. Fields that were not set or unset remain
85
+ // unchanged, allowing for partial updates to be applied.
86
+ }
87
+ `)
88
+ })
89
+ })