@graphprotocol/graph-cli 0.30.4 → 0.33.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.
package/README.md CHANGED
@@ -16,6 +16,7 @@ As of today, the command line interface supports the following commands:
16
16
  - `graph auth` — Stores a [Graph Node](https://github.com/graphprotocol/graph-node) access token in the system's keychain.
17
17
  - `graph local` — Runs tests against a [Graph Node](https://github.com/graphprotocol/graph-node) test environment (using Ganache by default).
18
18
  - `graph test` — Downloads and runs the [Matchstick](https://github.com/LimeChain/matchstick) rust binary in order to test a subgraph.
19
+ - `graph add` - Adds a new datasource to the yaml file and writes the necessary changes to other files - schema.graphql, abi and mapping.
19
20
 
20
21
  ## How It Works
21
22
 
@@ -49,9 +50,9 @@ yarn global add @graphprotocol/graph-cli
49
50
 
50
51
  ## Getting Started
51
52
 
52
- The Graph CLI can be used with a local or self-hosted [Graph Node](https://github.com/graphprotocol/graph-node) or with the [Hosted Service](https://thegraph.com/explorer/). To help you get going, there are [quick start guides](https://thegraph.com/docs/quick-start) available for both.
53
+ The Graph CLI can be used with a local or self-hosted [Graph Node](https://github.com/graphprotocol/graph-node) or with the [Hosted Service](https://thegraph.com/explorer/). To help you get going, there are [quick start guides](https://thegraph.com/docs/en/developer/quick-start/) available for both.
53
54
 
54
- If you are ready to dive into the details of building a subgraph from scratch, there is a [detailed walkthrough](https://thegraph.com/docs/define-a-subgraph) for that as well, along with API documentation for the [AssemblyScript API](https://thegraph.com/docs/assemblyscript-api).
55
+ If you are ready to dive into the details of building a subgraph from scratch, there is a [detailed walkthrough](https://thegraph.com/docs/en/developer/create-subgraph-hosted/) for that as well, along with API documentation for the [AssemblyScript API](https://thegraph.com/docs/en/developer/assemblyscript-api/).
55
56
 
56
57
  ## Release process
57
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphprotocol/graph-cli",
3
- "version": "0.30.4",
3
+ "version": "0.33.0",
4
4
  "license": "(Apache-2.0 OR MIT)",
5
5
  "description": "CLI for building for and deploying to The Graph",
6
6
  "dependencies": {
@@ -8,7 +8,7 @@
8
8
  "binary-install-raw": "0.0.13",
9
9
  "chalk": "3.0.0",
10
10
  "chokidar": "3.5.1",
11
- "debug": "4.1.1",
11
+ "debug": "4.3.1",
12
12
  "docker-compose": "0.23.4",
13
13
  "dockerode": "2.5.8",
14
14
  "fs-extra": "9.0.0",
@@ -17,7 +17,7 @@
17
17
  "graphql": "15.5.0",
18
18
  "immutable": "3.8.2",
19
19
  "ipfs-http-client": "34.0.0",
20
- "jayson": "3.2.0",
20
+ "jayson": "3.6.6",
21
21
  "js-yaml": "3.13.1",
22
22
  "node-fetch": "2.6.0",
23
23
  "pkginfo": "0.4.1",
@@ -143,9 +143,9 @@ const ASSEMBLYSCRIPT_TO_ETHEREUM_VALUE = [
143
143
  /^string\[([0-9]+)?\]$/,
144
144
  code => `ethereum.Value.fromStringArray(${code})`,
145
145
  ],
146
- ['Tuple', 'tuple', code => `ethereum.Value.fromTuple(${code})`],
146
+ ['ethereum.Tuple', 'tuple', code => `ethereum.Value.fromTuple(${code})`],
147
147
  [
148
- 'Array<Tuple>',
148
+ 'Array<ethereum.Tuple>',
149
149
  /^tuple\[([0-9]+)?\]$/,
150
150
  code => `ethereum.Value.fromTupleArray(${code})`,
151
151
  ],
@@ -60,6 +60,11 @@ const getEtherscanLikeAPIUrl = (network) => {
60
60
  case "aurora-testnet": return `https://api-testnet.aurorascan.dev/api`
61
61
  case "optimism-kovan": return `https://api-kovan-optimistic.etherscan.io/api`
62
62
  case "optimism": return `https://api-optimistic.etherscan.io/api`
63
+ case "moonbeam": return `https://api-moonbeam.moonscan.io/api`
64
+ case "moonriver": return `https://api-moonriver.moonscan.io/api`
65
+ case "mbase": return `https://api-moonbase.moonscan.io/api`
66
+ case "avalanche": return `https://api.snowtrace.io/api`;
67
+ case "fuji": return `https://api-testnet.snowtrace.io/api`;
63
68
  default: return `https://api-${network}.etherscan.io/api`
64
69
  }
65
70
  }
@@ -6,7 +6,7 @@ const Compiler = require('../compiler')
6
6
  // Helper function to construct a subgraph compiler
7
7
  const createCompiler = (
8
8
  manifest,
9
- { ipfs, outputDir, outputFormat, skipMigrations, blockIpfsMethods, protocol }
9
+ { ipfs, headers, outputDir, outputFormat, skipMigrations, blockIpfsMethods, protocol }
10
10
  ) => {
11
11
  // Parse the IPFS URL
12
12
  let url
@@ -25,6 +25,7 @@ The IPFS URL must be of the following format: http(s)://host[:port]/[path]`)
25
25
  host: url.hostname,
26
26
  port: url.port,
27
27
  'api-path': url.pathname.replace(/\/$/, '') + '/api/v0/',
28
+ headers,
28
29
  })
29
30
  : undefined
30
31
 
@@ -3,12 +3,16 @@ const toolbox = require('gluegun/toolbox')
3
3
 
4
4
  const createJsonRpcClient = url => {
5
5
  let params = {
6
- auth: url.auth,
7
6
  host: url.hostname,
8
7
  port: url.port,
9
8
  path: url.pathname,
10
9
  }
11
10
 
11
+ // username may be empty
12
+ if (url.password) {
13
+ params.auth = `${url.username}:${url.password}`
14
+ }
15
+
12
16
  if (url.protocol === 'https:') {
13
17
  return jayson.Client.https(params)
14
18
  } else if (url.protocol === 'http:') {
@@ -107,7 +107,19 @@ function hasChanges(identifierName, network, networkConfig, dataSource) {
107
107
  return networkChanged || addressChanged || startBlockChanged
108
108
  }
109
109
 
110
+ const updateNetworksFile = async (toolbox, network, dataSource, address, networksFile) => {
111
+ await toolbox.patching.update(networksFile, (config) => {
112
+ if(Object.keys(config).includes(network)) {
113
+ Object.assign(config[network], { [dataSource]: { address } })
114
+ } else {
115
+ Object.assign(config, { [network]: { [dataSource]: { address } }})
116
+ }
117
+ return config
118
+ })
119
+ }
120
+
110
121
  module.exports = {
111
122
  updateSubgraphNetwork,
112
- initNetworksConfig
123
+ initNetworksConfig,
124
+ updateNetworksFile
113
125
  }
@@ -7,10 +7,17 @@ const { step } = require('./spinner')
7
7
  const Scaffold = require('../scaffold')
8
8
  const { generateEventIndexingHandlers } = require('../scaffold/mapping')
9
9
  const { generateEventType, abiEvents } = require('../scaffold/schema')
10
+ const { generateTestsFiles } = require('../scaffold/tests')
10
11
  const { strings } = require('gluegun')
11
12
  const { Map } = require('immutable')
12
13
 
13
- const generateDataSource = async (protocol, contractName, network, contractAddress, abi) => {
14
+ const generateDataSource = async (
15
+ protocol,
16
+ contractName,
17
+ network,
18
+ contractAddress,
19
+ abi,
20
+ ) => {
14
21
  const protocolManifest = protocol.getManifestScaffold()
15
22
 
16
23
  return Map.of(
@@ -89,13 +96,13 @@ const writeABI = async (abi, contractName) => {
89
96
 
90
97
  const writeSchema = async (abi, protocol, schemaPath, entities) => {
91
98
  const events = protocol.hasEvents()
92
- ? abiEvents(abi).filter(event => entities.indexOf(event.get('name')) === -1).toJS()
99
+ ? abiEvents(abi)
100
+ .filter(event => entities.indexOf(event.get('name')) === -1)
101
+ .toJS()
93
102
  : []
94
103
 
95
104
  let data = prettier.format(
96
- events.map(
97
- event => generateEventType(event, protocol.name)
98
- ).join('\n\n'),
105
+ events.map(event => generateEventType(event, protocol.name)).join('\n\n'),
99
106
  {
100
107
  parser: 'graphql',
101
108
  },
@@ -106,18 +113,33 @@ const writeSchema = async (abi, protocol, schemaPath, entities) => {
106
113
 
107
114
  const writeMapping = async (abi, protocol, contractName, entities) => {
108
115
  const events = protocol.hasEvents()
109
- ? abiEvents(abi).filter(event => entities.indexOf(event.get('name')) === -1).toJS()
116
+ ? abiEvents(abi)
117
+ .filter(event => entities.indexOf(event.get('name')) === -1)
118
+ .toJS()
110
119
  : []
111
120
 
112
- let mapping = prettier.format(
113
- generateEventIndexingHandlers(
114
- events,
115
- contractName,
116
- ),
117
- { parser: 'typescript', semi: false },
118
- )
121
+ let mapping = prettier.format(generateEventIndexingHandlers(events, contractName), {
122
+ parser: 'typescript',
123
+ semi: false,
124
+ })
125
+
126
+ await fs.writeFile(`./src/${strings.kebabCase(contractName)}.ts`, mapping, {
127
+ encoding: 'utf-8',
128
+ })
129
+ }
130
+
131
+ const writeTestsFiles = async (abi, contractName) => {
132
+ // If a contract is added to a subgraph that has no tests folder
133
+ await fs.ensureDir('./tests/')
134
+
135
+ const events = abiEvents(abi).toJS()
136
+ const testsFiles = generateTestsFiles(contractName, events, true)
119
137
 
120
- await fs.writeFile(`./src/${strings.kebabCase(contractName)}.ts`, mapping, { encoding: 'utf-8' })
138
+ for (const [fileName, content] of Object.entries(testsFiles)) {
139
+ await fs.writeFile(`./tests/${fileName}`, content, {
140
+ encoding: 'utf-8',
141
+ })
142
+ }
121
143
  }
122
144
 
123
145
  module.exports = {
@@ -128,4 +150,5 @@ module.exports = {
128
150
  writeABI,
129
151
  writeSchema,
130
152
  writeMapping,
153
+ writeTestsFiles,
131
154
  }
@@ -5,10 +5,11 @@ const { withSpinner } = require('../command-helpers/spinner')
5
5
  const Subgraph = require('../subgraph')
6
6
  const Protocol = require('../protocols')
7
7
  const DataSourcesExtractor = require('../command-helpers/data-sources')
8
- const { generateDataSource, writeABI, writeSchema, writeMapping } = require('../command-helpers/scaffold')
8
+ const { generateDataSource, writeABI, writeSchema, writeMapping, writeTestsFiles } = require('../command-helpers/scaffold')
9
9
  const { loadAbiFromEtherscan, loadAbiFromBlockScout } = require('../command-helpers/abi')
10
10
  const EthereumABI = require('../protocols/ethereum/abi')
11
11
  const { fixParameters } = require('../command-helpers/gluegun')
12
+ const { updateNetworksFile } = require('../command-helpers/network')
12
13
 
13
14
  const HELP = `
14
15
  ${chalk.bold('graph add')} <address> [<subgraph-manifest default: "./subgraph.yaml">]
@@ -18,11 +19,12 @@ ${chalk.dim('Options:')}
18
19
  --abi <path> Path to the contract ABI (default: download from Etherscan)
19
20
  --contract-name Name of the contract (default: Contract)
20
21
  --merge-entities Whether to merge entities with the same name (default: false)
22
+ --network-file <path> Networks config file path (default: "./networks.json")
21
23
  -h, --help Show usage information
22
24
  `
23
25
 
24
26
  module.exports = {
25
- description: 'Creates a new subgraph with basic scaffolding',
27
+ description: 'Adds a new datasource to a subgraph',
26
28
  run: async toolbox => {
27
29
  // Obtain tools
28
30
  let { print, system } = toolbox
@@ -33,20 +35,12 @@ module.exports = {
33
35
  contractName,
34
36
  h,
35
37
  help,
36
- mergeEntities
38
+ mergeEntities,
39
+ networkFile
37
40
  } = toolbox.parameters.options
38
41
 
39
- let address = toolbox.parameters.first
40
- let manifestPath = toolbox.parameters.second || './subgraph.yaml'
41
42
  contractName = contractName || 'Contract'
42
43
 
43
- // Validate the address
44
- if (!address) {
45
- print.error('No contract address provided')
46
- process.exitCode = 1
47
- return
48
- }
49
-
50
44
  try {
51
45
  fixParameters(toolbox.parameters, {
52
46
  h,
@@ -59,12 +53,22 @@ module.exports = {
59
53
  return
60
54
  }
61
55
 
56
+ let address = toolbox.parameters.first || toolbox.parameters.array[0]
57
+ let manifestPath = toolbox.parameters.second || toolbox.parameters.array[1] || './subgraph.yaml'
58
+
62
59
  // Show help text if requested
63
60
  if (help || h) {
64
61
  print.info(HELP)
65
62
  return
66
63
  }
67
64
 
65
+ // Validate the address
66
+ if (!address) {
67
+ print.error('No contract address provided')
68
+ process.exitCode = 1
69
+ return
70
+ }
71
+
68
72
  const dataSourcesAndTemplates = await DataSourcesExtractor.fromFilePath(manifestPath)
69
73
  let protocol = Protocol.fromDataSources(dataSourcesAndTemplates)
70
74
  let manifest = await Subgraph.load(manifestPath, { protocol })
@@ -99,8 +103,12 @@ module.exports = {
99
103
  await writeSchema(ethabi, protocol, result.getIn(['schema', 'file']), collisionEntities)
100
104
  await writeMapping(ethabi, protocol, contractName, collisionEntities)
101
105
 
106
+ if (protocol.hasEvents()) {
107
+ await writeTestsFiles(ethabi, contractName)
108
+ }
109
+
102
110
  let dataSources = result.get('dataSources')
103
- let dataSource = await generateDataSource(protocol,
111
+ let dataSource = await generateDataSource(protocol,
104
112
  contractName, network, address, ethabi)
105
113
 
106
114
  // Handle the collisions edge case by copying another data source yaml data
@@ -120,6 +128,10 @@ module.exports = {
120
128
 
121
129
  await Subgraph.write(result, manifestPath)
122
130
 
131
+ // Update networks.json
132
+ const networksFile = networkFile || "./networks.json"
133
+ await updateNetworksFile(toolbox, network, contractName, address, networksFile)
134
+
123
135
  // Detect Yarn and/or NPM
124
136
  let yarn = await system.which('yarn')
125
137
  let npm = await system.which('npm')
@@ -173,12 +185,12 @@ const updateEventNamesOnCollision = (ethabi, entities, contractName, mergeEntiti
173
185
  if (dataRow.get('type') === 'event'){
174
186
  if (entities.indexOf(dataRow.get('name')) !== -1) {
175
187
  if (entities.indexOf(`${contractName}${dataRow.get('name')}`) !== -1) {
176
- print.error(`Contract name ('${contractName}')
188
+ print.error(`Contract name ('${contractName}')
177
189
  + event name ('${dataRow.get('name')}') entity already exists.`)
178
190
  process.exitCode = 1
179
191
  return
180
192
  }
181
-
193
+
182
194
  if (mergeEntities) {
183
195
  collisionEntities.push(dataRow.get('name'))
184
196
  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
@@ -17,8 +17,8 @@ Options:
17
17
  -t, --output-format <format> Output format for mappings (wasm, wast) (default: wasm)
18
18
  --skip-migrations Skip subgraph migrations (default: false)
19
19
  -w, --watch Regenerate types when subgraph files change (default: false)
20
- --network <name> Network to use from networks.json
21
- --network-file <path> Networks file (default: "./networks.json")
20
+ --network <name> Network configuration to use from the networks config file
21
+ --network-file <path> Networks config file path (default: "./networks.json")
22
22
  `
23
23
 
24
24
  module.exports = {
@@ -14,6 +14,7 @@ const { assertManifestApiVersion, assertGraphTsVersion } = require('../command-h
14
14
  const DataSourcesExtractor = require('../command-helpers/data-sources')
15
15
  const { validateStudioNetwork } = require('../command-helpers/studio')
16
16
  const Protocol = require('../protocols')
17
+ const { updateSubgraphNetwork } = require('../command-helpers/network')
17
18
 
18
19
  const HELP = `
19
20
  ${chalk.bold('graph deploy')} [options] ${chalk.bold('<subgraph-name>')} ${chalk.bold(
@@ -22,18 +23,21 @@ ${chalk.bold('graph deploy')} [options] ${chalk.bold('<subgraph-name>')} ${chalk
22
23
 
23
24
  Options:
24
25
 
25
- --product <subgraph-studio|hosted-service>
26
+ --product <subgraph-studio|hosted-service>
26
27
  Selects the product to which to deploy
27
- --studio Shortcut for --product subgraph-studio
28
- -g, --node <node> Graph node to which to deploy
29
- --deploy-key <key> User deploy key
30
- -l --version-label <label> Version label used for the deployment
31
- -h, --help Show usage information
32
- -i, --ipfs <node> Upload build results to an IPFS node (default: ${DEFAULT_IPFS_URL})
33
- --debug-fork ID of a remote subgraph whose store will be GraphQL queried
34
- -o, --output-dir <path> Output directory for build results (default: build/)
35
- --skip-migrations Skip subgraph migrations (default: false)
36
- -w, --watch Regenerate types when subgraph files change (default: false)
28
+ --studio Shortcut for --product subgraph-studio
29
+ -g, --node <node> Graph node to which to deploy
30
+ --deploy-key <key> User deploy key
31
+ -l --version-label <label> Version label used for the deployment
32
+ -h, --help Show usage information
33
+ -i, --ipfs <node> Upload build results to an IPFS node (default: ${DEFAULT_IPFS_URL})
34
+ -hdr, --headers <map> Add custom headers that will be used by the IPFS HTTP client (default: {})
35
+ --debug-fork ID of a remote subgraph whose store will be GraphQL queried
36
+ -o, --output-dir <path> Output directory for build results (default: build/)
37
+ --skip-migrations Skip subgraph migrations (default: false)
38
+ -w, --watch Regenerate types when subgraph files change (default: false)
39
+ --network <name> Network configuration to use from the networks config file
40
+ --network-file <path> Networks config file path (default: "./networks.json")
37
41
  `
38
42
 
39
43
  const processForm = async (
@@ -51,7 +55,7 @@ const processForm = async (
51
55
  name: 'product',
52
56
  message: 'Product for which to deploy',
53
57
  choices: ['subgraph-studio', 'hosted-service'],
54
- skip:
58
+ skip:
55
59
  product === 'subgraph-studio' ||
56
60
  product === 'hosted-service' ||
57
61
  studio !== undefined || node !== undefined,
@@ -91,6 +95,8 @@ module.exports = {
91
95
  i,
92
96
  help,
93
97
  ipfs,
98
+ headers,
99
+ hdr,
94
100
  node,
95
101
  o,
96
102
  outputDir,
@@ -98,16 +104,27 @@ module.exports = {
98
104
  w,
99
105
  watch,
100
106
  debugFork,
107
+ network,
108
+ networkFile,
101
109
  } = toolbox.parameters.options
102
110
 
103
111
  // Support both long and short option variants
104
112
  help = help || h
105
113
  ipfs = ipfs || i || DEFAULT_IPFS_URL
114
+ headers = headers || hdr || "{}"
106
115
  node = node || g
107
116
  outputDir = outputDir || o
108
117
  watch = watch || w
109
118
  versionLabel = versionLabel || l
110
119
 
120
+ try {
121
+ headers = JSON.parse(headers)
122
+ } catch (e) {
123
+ print.error("Please make sure headers is a valid JSON value")
124
+ process.exitCode = 1
125
+ return
126
+ }
127
+
111
128
  let subgraphName, manifest
112
129
  try {
113
130
  ;[subgraphName, manifest] = fixParameters(toolbox.parameters, {
@@ -129,6 +146,10 @@ module.exports = {
129
146
  manifest !== undefined && manifest !== ''
130
147
  ? manifest
131
148
  : filesystem.resolve('subgraph.yaml')
149
+ networkFile =
150
+ networkFile !== undefined && networkFile !== ''
151
+ ? networkFile
152
+ : filesystem.resolve("networks.json")
132
153
 
133
154
  try {
134
155
  const dataSourcesAndTemplates = await DataSourcesExtractor.fromFilePath(manifest)
@@ -211,11 +232,17 @@ module.exports = {
211
232
  return
212
233
  }
213
234
 
235
+ if (network) {
236
+ let identifierName = protocol.getContract().identifierName()
237
+ await updateSubgraphNetwork(toolbox, manifest, network, networkFile, identifierName)
238
+ }
239
+
214
240
  const isStudio = node.match(/studio/)
215
241
  const isHostedService = node.match(/thegraph.com/) && !isStudio
216
242
 
217
243
  let compiler = createCompiler(manifest, {
218
244
  ipfs,
245
+ headers,
219
246
  outputDir,
220
247
  outputFormat: 'wasm',
221
248
  skipMigrations,
@@ -1,10 +1,9 @@
1
1
  const chalk = require('chalk')
2
- const fetch = require('node-fetch')
3
- const immutable = require('immutable')
4
2
  const os = require('os')
5
3
  const path = require('path')
6
4
  const toolbox = require('gluegun/toolbox')
7
- const yaml = require('yaml')
5
+ const fs = require('fs')
6
+ const graphCli = require('../cli')
8
7
 
9
8
  const {
10
9
  getSubgraphBasename,
@@ -423,9 +422,9 @@ module.exports = {
423
422
  contractName,
424
423
  node,
425
424
  studio,
426
- product,
425
+ product
427
426
  },
428
- { commands },
427
+ { commands, addContract: false },
429
428
  )
430
429
  }
431
430
 
@@ -484,9 +483,9 @@ module.exports = {
484
483
  contractName: inputs.contractName,
485
484
  node,
486
485
  studio: inputs.studio,
487
- product: inputs.product,
486
+ product: inputs.product
488
487
  },
489
- { commands },
488
+ { commands, addContract: true },
490
489
  )
491
490
  }
492
491
  },
@@ -720,9 +719,9 @@ const initSubgraphFromContract = async (
720
719
  contractName,
721
720
  node,
722
721
  studio,
723
- product,
722
+ product
724
723
  },
725
- { commands },
724
+ { commands, addContract },
726
725
  ) => {
727
726
  let { print } = toolbox
728
727
 
@@ -815,5 +814,85 @@ const initSubgraphFromContract = async (
815
814
  return
816
815
  }
817
816
 
817
+ while (addContract) {
818
+ addContract = await addAnotherContract(toolbox, { protocolInstance, directory })
819
+ }
820
+
818
821
  printNextSteps(toolbox, { subgraphName, directory }, { commands })
819
822
  }
823
+
824
+ const addAnotherContract = async (toolbox, { protocolInstance, directory }) => {
825
+ const addContractConfirmation = await toolbox.prompt.confirm('Add another contract?')
826
+
827
+ if (addContractConfirmation) {
828
+ let abiFromFile
829
+ let ProtocolContract = protocolInstance.getContract()
830
+
831
+ let questions = [
832
+ {
833
+ type: 'input',
834
+ name: 'contract',
835
+ message: () => `Contract ${ProtocolContract.identifierName()}`,
836
+ validate: async (value) => {
837
+ // Validate whether the contract is valid
838
+ const { valid, error } = validateContract(value, ProtocolContract)
839
+ return valid ? true : error
840
+ },
841
+ },
842
+ {
843
+ type: 'select',
844
+ name: 'localAbi',
845
+ message: 'Provide local ABI path?',
846
+ choices: ['yes', 'no'],
847
+ result: (value) => {
848
+ abiFromFile = value === 'yes' ? true : false
849
+ return abiFromFile
850
+ },
851
+ },
852
+ {
853
+ type: 'input',
854
+ name: 'abi',
855
+ message: 'ABI file (path)',
856
+ skip: () => abiFromFile === false
857
+ },
858
+ {
859
+ type: 'input',
860
+ name: 'contractName',
861
+ message: 'Contract Name',
862
+ initial: 'Contract',
863
+ validate: (value) => value && value.length > 0,
864
+ },
865
+ ]
866
+
867
+ // Get the cwd before process.chdir in order to switch back in the end of command execution
868
+ const cwd = process.cwd();
869
+
870
+ try {
871
+ let { abi, contract, contractName } = await toolbox.prompt.ask(questions)
872
+
873
+ if (fs.existsSync(directory)) {
874
+ process.chdir(directory)
875
+ }
876
+
877
+ let commandLine = ['add', contract, '--contract-name', contractName]
878
+
879
+ if (abiFromFile) {
880
+ if (abi.includes(directory)) {
881
+ commandLine.push('--abi', path.normalize(abi.replace(directory, '')))
882
+ } else {
883
+ commandLine.push('--abi', abi)
884
+ }
885
+ }
886
+
887
+ await graphCli.run(commandLine)
888
+ } catch (e) {
889
+ toolbox.print.error(e)
890
+ process.exit(1)
891
+ }
892
+ finally {
893
+ process.chdir(cwd)
894
+ }
895
+ }
896
+
897
+ return addContractConfirmation
898
+ }
@@ -180,10 +180,8 @@ async function getPlatform(logsOpt) {
180
180
 
181
181
  if (arch === 'x64' || isM1) {
182
182
  if (type === 'Darwin') {
183
- if (majorVersion === 19) {
184
- return 'binary-macos-10.15'
185
- } else if (majorVersion === 18) {
186
- return 'binary-macos-10.14'
183
+ if (majorVersion === 18 || majorVersion === 19) {
184
+ return 'binary-macos-10.15' // GitHub dropped support for macOS 10.14 in Actions, but it seems 10.15 binary works on 10.14 too
187
185
  } else if (isM1) {
188
186
  return 'binary-macos-11-m1'
189
187
  }
@@ -196,8 +194,6 @@ async function getPlatform(logsOpt) {
196
194
  } else {
197
195
  return 'binary-linux-20'
198
196
  }
199
- } else if (type === 'Windows_NT') {
200
- return 'binary-windows'
201
197
  }
202
198
  }
203
199
 
@@ -19,7 +19,7 @@ module.exports = {
19
19
  return 'graph-ts dependency not installed yet'
20
20
  }
21
21
 
22
- let manifest = loadManifest(manifestFile)
22
+ let manifest = await loadManifest(manifestFile)
23
23
  return (
24
24
  // Only migrate if the graph-ts version is >= 0.22.0...
25
25
  // Coerce needed because we may be dealing with an alpha version
@@ -19,7 +19,7 @@ module.exports = {
19
19
  return 'graph-ts dependency not installed yet'
20
20
  }
21
21
 
22
- let manifest = loadManifest(manifestFile)
22
+ let manifest = await loadManifest(manifestFile)
23
23
  return (
24
24
  // Only migrate if the graph-ts version is >= 0.24.0...
25
25
  // Coerce needed because we may be dealing with an alpha version
@@ -52,6 +52,7 @@ module.exports = class Protocol {
52
52
  'matic',
53
53
  'mumbai',
54
54
  'fantom',
55
+ 'fantom-testnet',
55
56
  'bsc',
56
57
  'chapel',
57
58
  'clover',
@@ -60,6 +61,8 @@ module.exports = class Protocol {
60
61
  'celo',
61
62
  'celo-alfajores',
62
63
  'fuse',
64
+ 'moonbeam',
65
+ 'moonriver',
63
66
  'mbase',
64
67
  'arbitrum-one',
65
68
  'arbitrum-rinkeby',
@@ -33,6 +33,12 @@ type DataSource {
33
33
  type ContractSource {
34
34
  account: String
35
35
  startBlock: BigInt
36
+ accounts: PartialAccount
37
+ }
38
+
39
+ type PartialAccount {
40
+ prefixes: [String!]
41
+ suffixes: [String!]
36
42
  }
37
43
 
38
44
  type ContractMapping {
@@ -243,6 +243,265 @@ export function handleExampleEvent1(event: ExampleEvent1Event): void {
243
243
  entity.a = event.params.a
244
244
  entity.save()
245
245
  }
246
+ `)
247
+ })
248
+
249
+ test('Test Files (default)', () => {
250
+ const files = scaffoldWithIndexEvents.generateTests()
251
+ const testFile = files['contract.test.ts']
252
+ const utilsFile = files['contract-utils.ts']
253
+ expect(testFile).toEqual(`\
254
+ import {
255
+ assert,
256
+ describe,
257
+ test,
258
+ clearStore,
259
+ beforeAll,
260
+ afterAll
261
+ } from \"matchstick-as/assembly/index\"
262
+ import { BigInt, Bytes } from \"@graphprotocol/graph-ts\"
263
+ import { ExampleEvent } from \"../generated/schema\"
264
+ import { ExampleEvent as ExampleEventEvent } from \"../generated/Contract/Contract\"
265
+ import { handleExampleEvent } from \"../src/contract\"
266
+ import { createExampleEventEvent } from \"./contract-utils\"
267
+
268
+ // Tests structure (matchstick-as >=0.5.0)
269
+ // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0
270
+
271
+ describe(\"Describe entity assertions\", () => {
272
+ beforeAll(() => {
273
+ let a = BigInt.fromI32(234)
274
+ let b = [Bytes.fromI32(1234567890)]
275
+ let param2 = \"Example string value\"
276
+ let c = \"ethereum.Tuple Not implemented\"
277
+ let d = \"Example string value\"
278
+ let newExampleEventEvent = createExampleEventEvent(a, b, param2, c, d)
279
+ handleExampleEvent(newExampleEventEvent)
280
+ })
281
+
282
+ afterAll(() => {
283
+ clearStore()
284
+ })
285
+
286
+ // For more test scenarios, see:
287
+ // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test
288
+
289
+ test(\"ExampleEvent created and stored\", () => {
290
+ assert.entityCount(\"ExampleEvent\", 1)
291
+
292
+ // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function
293
+ assert.fieldEquals(
294
+ \"ExampleEvent\",
295
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
296
+ \"a\",
297
+ \"234\"
298
+ )
299
+ assert.fieldEquals(
300
+ \"ExampleEvent\",
301
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
302
+ \"b\",
303
+ \"[1234567890]\"
304
+ )
305
+ assert.fieldEquals(
306
+ \"ExampleEvent\",
307
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
308
+ \"param2\",
309
+ \"Example string value\"
310
+ )
311
+ assert.fieldEquals(
312
+ \"ExampleEvent\",
313
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
314
+ \"c\",
315
+ \"ethereum.Tuple Not implemented\"
316
+ )
317
+ assert.fieldEquals(
318
+ \"ExampleEvent\",
319
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
320
+ \"d\",
321
+ \"Example string value\"
322
+ )
323
+
324
+ // More assert options:
325
+ // https://thegraph.com/docs/en/developer/matchstick/#asserts
326
+ })
327
+ })
328
+ `)
329
+ expect(utilsFile).toEqual(`\
330
+ import { newMockEvent } from \"matchstick-as\"
331
+ import { ethereum, BigInt, Bytes } from \"@graphprotocol/graph-ts\"
332
+ import { ExampleEvent, ExampleEvent1 } from \"../generated/Contract/Contract\"
333
+
334
+ export function createExampleEventEvent(
335
+ a: BigInt,
336
+ b: Array<Bytes>,
337
+ param2: string,
338
+ c: ethereum.Tuple,
339
+ d: string
340
+ ): ExampleEvent {
341
+ let exampleEventEvent = changetype<ExampleEvent>(newMockEvent())
342
+
343
+ exampleEventEvent.parameters = new Array()
344
+
345
+ exampleEventEvent.parameters.push(
346
+ new ethereum.EventParam(\"a\", ethereum.Value.fromUnsignedBigInt(a))
347
+ )
348
+ exampleEventEvent.parameters.push(
349
+ new ethereum.EventParam(\"b\", ethereum.Value.fromBytesArray(b))
350
+ )
351
+ exampleEventEvent.parameters.push(
352
+ new ethereum.EventParam(\"param2\", ethereum.Value.fromString(param2))
353
+ )
354
+ exampleEventEvent.parameters.push(
355
+ new ethereum.EventParam(\"c\", ethereum.Value.fromTuple(c))
356
+ )
357
+ exampleEventEvent.parameters.push(
358
+ new ethereum.EventParam(\"d\", ethereum.Value.fromString(d))
359
+ )
360
+
361
+ return exampleEventEvent
362
+ }
363
+
364
+ export function createExampleEvent1Event(a: Bytes): ExampleEvent1 {
365
+ let exampleEvent1Event = changetype<ExampleEvent1>(newMockEvent())
366
+
367
+ exampleEvent1Event.parameters = new Array()
368
+
369
+ exampleEvent1Event.parameters.push(
370
+ new ethereum.EventParam(\"a\", ethereum.Value.fromFixedBytes(a))
371
+ )
372
+
373
+ return exampleEvent1Event
374
+ }
375
+ `)
376
+ })
377
+
378
+ test('Test Files (for indexing events)', () => {
379
+ const files = scaffoldWithIndexEvents.generateTests()
380
+ const testFile = files['contract.test.ts']
381
+ const utilsFile = files['contract-utils.ts']
382
+
383
+ expect(testFile).toEqual(`\
384
+ import {
385
+ assert,
386
+ describe,
387
+ test,
388
+ clearStore,
389
+ beforeAll,
390
+ afterAll
391
+ } from \"matchstick-as/assembly/index\"
392
+ import { BigInt, Bytes } from \"@graphprotocol/graph-ts\"
393
+ import { ExampleEvent } from \"../generated/schema\"
394
+ import { ExampleEvent as ExampleEventEvent } from \"../generated/Contract/Contract\"
395
+ import { handleExampleEvent } from \"../src/contract\"
396
+ import { createExampleEventEvent } from \"./contract-utils\"
397
+
398
+ // Tests structure (matchstick-as >=0.5.0)
399
+ // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0
400
+
401
+ describe(\"Describe entity assertions\", () => {
402
+ beforeAll(() => {
403
+ let a = BigInt.fromI32(234)
404
+ let b = [Bytes.fromI32(1234567890)]
405
+ let param2 = \"Example string value\"
406
+ let c = \"ethereum.Tuple Not implemented\"
407
+ let d = \"Example string value\"
408
+ let newExampleEventEvent = createExampleEventEvent(a, b, param2, c, d)
409
+ handleExampleEvent(newExampleEventEvent)
410
+ })
411
+
412
+ afterAll(() => {
413
+ clearStore()
414
+ })
415
+
416
+ // For more test scenarios, see:
417
+ // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test
418
+
419
+ test(\"ExampleEvent created and stored\", () => {
420
+ assert.entityCount(\"ExampleEvent\", 1)
421
+
422
+ // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function
423
+ assert.fieldEquals(
424
+ \"ExampleEvent\",
425
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
426
+ \"a\",
427
+ \"234\"
428
+ )
429
+ assert.fieldEquals(
430
+ \"ExampleEvent\",
431
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
432
+ \"b\",
433
+ \"[1234567890]\"
434
+ )
435
+ assert.fieldEquals(
436
+ \"ExampleEvent\",
437
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
438
+ \"param2\",
439
+ \"Example string value\"
440
+ )
441
+ assert.fieldEquals(
442
+ \"ExampleEvent\",
443
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
444
+ \"c\",
445
+ \"ethereum.Tuple Not implemented\"
446
+ )
447
+ assert.fieldEquals(
448
+ \"ExampleEvent\",
449
+ \"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1\",
450
+ \"d\",
451
+ \"Example string value\"
452
+ )
453
+
454
+ // More assert options:
455
+ // https://thegraph.com/docs/en/developer/matchstick/#asserts
456
+ })
457
+ })
458
+ `)
459
+ expect(utilsFile).toEqual(`\
460
+ import { newMockEvent } from \"matchstick-as\"
461
+ import { ethereum, BigInt, Bytes } from \"@graphprotocol/graph-ts\"
462
+ import { ExampleEvent, ExampleEvent1 } from \"../generated/Contract/Contract\"
463
+
464
+ export function createExampleEventEvent(
465
+ a: BigInt,
466
+ b: Array<Bytes>,
467
+ param2: string,
468
+ c: ethereum.Tuple,
469
+ d: string
470
+ ): ExampleEvent {
471
+ let exampleEventEvent = changetype<ExampleEvent>(newMockEvent())
472
+
473
+ exampleEventEvent.parameters = new Array()
474
+
475
+ exampleEventEvent.parameters.push(
476
+ new ethereum.EventParam(\"a\", ethereum.Value.fromUnsignedBigInt(a))
477
+ )
478
+ exampleEventEvent.parameters.push(
479
+ new ethereum.EventParam(\"b\", ethereum.Value.fromBytesArray(b))
480
+ )
481
+ exampleEventEvent.parameters.push(
482
+ new ethereum.EventParam(\"param2\", ethereum.Value.fromString(param2))
483
+ )
484
+ exampleEventEvent.parameters.push(
485
+ new ethereum.EventParam(\"c\", ethereum.Value.fromTuple(c))
486
+ )
487
+ exampleEventEvent.parameters.push(
488
+ new ethereum.EventParam(\"d\", ethereum.Value.fromString(d))
489
+ )
490
+
491
+ return exampleEventEvent
492
+ }
493
+
494
+ export function createExampleEvent1Event(a: Bytes): ExampleEvent1 {
495
+ let exampleEvent1Event = changetype<ExampleEvent1>(newMockEvent())
496
+
497
+ exampleEvent1Event.parameters = new Array()
498
+
499
+ exampleEvent1Event.parameters.push(
500
+ new ethereum.EventParam(\"a\", ethereum.Value.fromFixedBytes(a))
501
+ )
502
+
503
+ return exampleEvent1Event
504
+ }
246
505
  `)
247
506
  })
248
507
  })
@@ -15,6 +15,7 @@ const {
15
15
  generateExampleEntityType,
16
16
  } = require('./schema')
17
17
  const { generateEventIndexingHandlers } = require('./mapping')
18
+ const { generateTestsFiles } = require('./tests')
18
19
  const { getSubgraphBasename } = require('../command-helpers/subgraph')
19
20
 
20
21
  module.exports = class Scaffold {
@@ -48,11 +49,15 @@ module.exports = class Scaffold {
48
49
  `--node http://localhost:8020/ ` +
49
50
  `--ipfs http://localhost:5001 ` +
50
51
  this.subgraphName,
52
+ 'test': 'graph test',
51
53
  },
52
54
  dependencies: {
53
55
  '@graphprotocol/graph-cli': GRAPH_CLI_VERSION,
54
56
  '@graphprotocol/graph-ts': `0.27.0`,
55
57
  },
58
+ devDependencies: {
59
+ 'matchstick-as': `0.5.0`,
60
+ },
56
61
  }),
57
62
  { parser: 'json' },
58
63
  )
@@ -137,6 +142,12 @@ dataSources:
137
142
  : undefined
138
143
  }
139
144
 
145
+ generateTests() {
146
+ return this.protocol.hasEvents()
147
+ ? generateTestsFiles(this.contractName, abiEvents(this.abi).toJS(), this.indexEvents)
148
+ : undefined
149
+ }
150
+
140
151
  generate() {
141
152
  return {
142
153
  'package.json': this.generatePackageJson(),
@@ -145,6 +156,7 @@ dataSources:
145
156
  'tsconfig.json': this.generateTsConfig(),
146
157
  src: { [`${strings.kebabCase(this.contractName)}.ts`]: this.generateMapping() },
147
158
  abis: this.generateABIs(),
159
+ tests: this.generateTests(),
148
160
  }
149
161
  }
150
162
  }
@@ -0,0 +1,196 @@
1
+ const prettier = require('prettier')
2
+ const { strings } = require('gluegun')
3
+ const { ascTypeForEthereum, ethereumFromAsc } = require("../codegen/types")
4
+
5
+ const VARIABLES_VALUES = {
6
+ "i32": 123,
7
+ "BigInt": 234,
8
+ "Bytes": 1234567890,
9
+ "Address": "0x0000000000000000000000000000000000000001",
10
+ "string": "Example string value",
11
+ "bool": true,
12
+ }
13
+
14
+ const generateTestsFiles = (contract, events, indexEvents) => {
15
+ const eventsTypes = events
16
+ .flatMap(event =>
17
+ event
18
+ .inputs
19
+ .map(input => {
20
+ // If the asc type is Array<T> we need to check if T is a native type or a custom graph-ts type
21
+ // If we don't do that we may miss a type that should be imported from graph-ts
22
+ const ascType = ascTypeForEthereum(input.type)
23
+ const inner = fetchArrayInnerType(ascType)
24
+ return inner ? inner[1] : ascType
25
+ })
26
+ ).filter(type => !type.startsWith("ethereum.") && !isNativeType(type))
27
+ const importTypes = [...new Set(eventsTypes)].join(', ')
28
+
29
+ return {
30
+ [`${strings.kebabCase(contract)}.test.ts`]: prettier.format(generateExampleTest(contract, events[0], indexEvents, importTypes), { parser: 'typescript', semi: false }),
31
+ [`${strings.kebabCase(contract)}-utils.ts`]: prettier.format(generateTestHelper(contract, events, importTypes), { parser: 'typescript', semi: false }),
32
+ }
33
+ }
34
+
35
+ /*
36
+ Generates the arguments that will be passed to the mock event function from the event inputs. Example:
37
+ let id = BigInt.fromI32(234)
38
+ let owner = Address.fromString("0x0000000000000000000000000000000000000001")
39
+ let displayName = "Example string value"
40
+ let imageUrl = "Example string value"
41
+ */
42
+ const generateArguments = (eventInputs) => {
43
+ return eventInputs.map((input, index) => {
44
+ let ascType = ascTypeForEthereum(input.type)
45
+ return `let ${input.name || `param${index}`} = ${assignValue(ascType, input.name)}`
46
+ }).join('\n')
47
+ }
48
+
49
+ // Generates the value that will be assigned to a variable in generateArguments()
50
+ const assignValue = (type) => {
51
+ switch (type) {
52
+ case "string":
53
+ return `"${VARIABLES_VALUES[type]}"`
54
+ case "BigInt":
55
+ return `BigInt.fromI32(${VARIABLES_VALUES[type]})`
56
+ case "Address":
57
+ return `Address.fromString("${VARIABLES_VALUES[type]}")`
58
+ case "Bytes":
59
+ return `Bytes.fromI32(${VARIABLES_VALUES[type]})`
60
+ case fetchArrayInnerType(type)?.input:
61
+ innerType = fetchArrayInnerType(type)[1]
62
+ return `[${assignValue(innerType)}]`
63
+ default:
64
+ let value = VARIABLES_VALUES[type]
65
+ return value ? value : `"${type} Not implemented"`
66
+ }
67
+ }
68
+
69
+ /*
70
+ Generates the assert.fieldEquals() for a given entity and event inputs. Example:
71
+ assert.fieldEquals(
72
+ "ExampleEntity",
73
+ "0xa16081f360e3847006db660bae1c6d1b2e17ec2a",
74
+ "owner",
75
+ "0x0000000000000000000000000000000000000001"
76
+ )
77
+ */
78
+ const generateFieldsAssertions = (entity, eventInputs, indexEvents) => eventInputs.filter(input => input.name != "id").map((input, index) =>
79
+ `assert.fieldEquals(
80
+ "${entity}",
81
+ "0xa16081f360e3847006db660bae1c6d1b2e17ec2a${indexEvents ? "-1" : ""}",
82
+ "${input.name || `param${index}`}",
83
+ "${expectedValue(ascTypeForEthereum(input.type))}"
84
+ )`
85
+ ).join('\n')
86
+
87
+ // Returns the expected value for a given type in generateFieldsAssertions()
88
+ const expectedValue = type => {
89
+ switch (type) {
90
+ case fetchArrayInnerType(type)?.input:
91
+ innerType = fetchArrayInnerType(type)[1]
92
+ return `[${expectedValue(innerType)}]`
93
+ default:
94
+ let value = VARIABLES_VALUES[type]
95
+ return value ? value : `${type} Not implemented`
96
+ }
97
+ }
98
+
99
+ // Checks if the type is a native AS type or should be imported from graph-ts
100
+ const isNativeType = type => {
101
+ let natives = [
102
+ /i32/,
103
+ /string/,
104
+ /boolean/
105
+ ]
106
+
107
+ return natives.some(rx => rx.test(type));
108
+ }
109
+
110
+ const fetchArrayInnerType = type => type.match(/Array<(.*?)>/)
111
+
112
+ // Generates the example test.ts file
113
+ const generateExampleTest = (contract, event, indexEvents, importTypes) => {
114
+ const entity = indexEvents ? `${event._alias}` : 'ExampleEntity'
115
+ const eventInputs = event.inputs
116
+ const eventName = event._alias
117
+
118
+ return `
119
+ import { assert, describe, test, clearStore, beforeAll, afterAll } from "matchstick-as/assembly/index"
120
+ import { ${importTypes} } from "@graphprotocol/graph-ts"
121
+ import { ${entity} } from "../generated/schema"
122
+ import { ${indexEvents ? `${eventName} as ${eventName}Event` : eventName} } from "../generated/${contract}/${contract}"
123
+ import { handle${eventName} } from "../src/${strings.kebabCase(contract)}"
124
+ import { create${eventName}Event } from "./${strings.kebabCase(contract)}-utils"
125
+
126
+
127
+ // Tests structure (matchstick-as >=0.5.0)
128
+ // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0
129
+
130
+ describe("Describe entity assertions", () => {
131
+ beforeAll(() => {
132
+ ${generateArguments(eventInputs)}
133
+ let new${eventName}Event = create${eventName}Event(${eventInputs.map((input, index) => input.name || `param${index}`).join(', ')});
134
+ handle${eventName}(new${eventName}Event)
135
+ })
136
+
137
+ afterAll(() => {
138
+ clearStore()
139
+ })
140
+
141
+ // For more test scenarios, see:
142
+ // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test
143
+
144
+ test("${entity} created and stored", () => {
145
+ assert.entityCount('${entity}', 1)
146
+
147
+ // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function
148
+ ${generateFieldsAssertions(entity, eventInputs, indexEvents)}
149
+
150
+ // More assert options:
151
+ // https://thegraph.com/docs/en/developer/matchstick/#asserts
152
+ })
153
+ })
154
+ `
155
+ }
156
+
157
+ // Generates the utils helper file
158
+ const generateTestHelper = (contract, events, importTypes) => {
159
+ const eventsNames = events.map(event => event._alias)
160
+
161
+ return `
162
+ import { newMockEvent } from 'matchstick-as';
163
+ import { ethereum, ${importTypes} } from '@graphprotocol/graph-ts';
164
+ import { ${eventsNames.join(", ")} } from '../generated/${contract}/${contract}';
165
+
166
+ ${generateMockedEvents(events).join("\n")}`
167
+ }
168
+
169
+ const generateMockedEvents = events =>
170
+ events.reduce(
171
+ (acc, event) => acc.concat(generateMockedEvent(event)),
172
+ [],
173
+ )
174
+
175
+ const generateMockedEvent = event => {
176
+ const varName = `${strings.camelCase(event._alias)}Event`
177
+ const fnArgs = event.inputs.map((input, index) => `${input.name || `param${index}`}: ${ascTypeForEthereum(input.type)}`);
178
+ const ascToEth = event.inputs.map((input, index) => `${varName}.parameters.push(new ethereum.EventParam("${input.name || `param${index}`}", ${ethereumFromAsc(input.name || `param${index}`, input.type)}))`);
179
+
180
+ return `
181
+ export function create${event._alias}Event(${fnArgs.join(', ')}): ${event._alias} {
182
+ let ${varName} = changetype<${event._alias}>(newMockEvent());
183
+
184
+ ${varName}.parameters = new Array();
185
+
186
+ ${ascToEth.join('\n')}
187
+
188
+ return ${varName};
189
+ }
190
+ `
191
+
192
+ }
193
+
194
+ module.exports = {
195
+ generateTestsFiles,
196
+ }
@@ -10,6 +10,14 @@ dataSources:
10
10
  source:
11
11
  account: wnear.flux-dev
12
12
  startBlock: 1
13
+ accounts:
14
+ prefixes:
15
+ - some-prefix
16
+ - a-prefix.with-dot
17
+ suffixes:
18
+ - suffix.near
19
+ - near
20
+ - another-suffix.testnet
13
21
  mapping:
14
22
  apiVersion: 0.0.5
15
23
  language: wasm/assemblyscript