@graphprotocol/graph-cli 0.24.0 → 0.25.2
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/examples/basic-event-handlers/package.json +1 -1
- package/examples/basic-event-handlers/yarn.lock +4 -4
- package/examples/example-subgraph/package.json +1 -1
- package/examples/example-subgraph/yarn.lock +4 -4
- package/package.json +2 -2
- package/src/codegen/schema.js +9 -2
- package/src/codegen/types/defaults.js +34 -0
- package/src/codegen/types/index.js +8 -1
- package/src/command-helpers/scaffold.js +66 -0
- package/src/command-helpers/version.js +1 -1
- package/src/command-helpers/version.test.js +1 -1
- package/src/commands/init.js +129 -92
- package/src/commands/test.js +210 -27
- package/src/protocols/ethereum/contract.js +22 -0
- package/src/protocols/ethereum/scaffold/manifest.js +32 -0
- package/src/protocols/ethereum/scaffold/mapping.js +76 -0
- package/src/protocols/ethereum/subgraph.js +0 -14
- package/src/protocols/index.js +94 -12
- package/src/protocols/near/contract.js +46 -0
- package/src/protocols/near/scaffold/manifest.js +16 -0
- package/src/protocols/near/scaffold/mapping.js +39 -0
- package/src/protocols/near/subgraph.js +1 -23
- package/src/{scaffold.test.js → scaffold/ethereum.test.js} +27 -19
- package/src/scaffold/index.js +149 -0
- package/src/scaffold/mapping.js +49 -0
- package/src/scaffold/near.test.js +89 -0
- package/src/scaffold/schema.js +62 -0
- package/src/subgraph.js +8 -0
- package/src/validation/contract.js +56 -0
- package/src/validation/index.js +2 -1
- package/src/validation/manifest.js +0 -29
- package/tests/cli/init/{abis → ethereum/abis}/Marketplace.json +0 -0
- package/tests/cli/init/{abis → ethereum/abis}/OverloadedElements.json +0 -0
- package/tests/cli/init/{abis → ethereum/abis}/SoloMargin.json +0 -0
- package/tests/cli/init/{from-contract-with-abi-and-structs.stderr → ethereum/from-contract-with-abi-and-structs.stderr} +1 -1
- package/tests/cli/init/{from-contract-with-abi-and-structs.stdout → ethereum/from-contract-with-abi-and-structs.stdout} +0 -0
- package/tests/cli/init/{from-contract-with-abi.stderr → ethereum/from-contract-with-abi.stderr} +1 -1
- package/tests/cli/init/{from-contract-with-abi.stdout → ethereum/from-contract-with-abi.stdout} +0 -0
- package/tests/cli/init/{from-contract-with-overloaded-elements.stderr → ethereum/from-contract-with-overloaded-elements.stderr} +1 -1
- package/tests/cli/init/{from-contract-with-overloaded-elements.stdout → ethereum/from-contract-with-overloaded-elements.stdout} +0 -0
- package/tests/cli/init/{from-contract.stderr → ethereum/from-contract.stderr} +1 -1
- package/tests/cli/init/{from-contract.stdout → ethereum/from-contract.stdout} +0 -0
- package/tests/cli/init/{from-example.stderr → ethereum/from-example.stderr} +0 -0
- package/tests/cli/init/{from-example.stdout → ethereum/from-example.stdout} +0 -0
- package/tests/cli/init/near/from-contract.stderr +12 -0
- package/tests/cli/init/near/from-contract.stdout +12 -0
- package/tests/cli/init.test.js +143 -112
- package/tests/cli/util.js +56 -41
- package/tests/cli/validation/near-is-valid/subgraph.yaml +1 -0
- package/src/scaffold.js +0 -364
package/src/commands/test.js
CHANGED
|
@@ -2,61 +2,125 @@ const { Binary } = require('binary-install-raw')
|
|
|
2
2
|
const os = require('os')
|
|
3
3
|
const chalk = require('chalk')
|
|
4
4
|
const fetch = require('node-fetch')
|
|
5
|
+
const { filesystem, print } = require('gluegun')
|
|
6
|
+
const { fixParameters } = require('../command-helpers/gluegun')
|
|
5
7
|
const semver = require('semver')
|
|
8
|
+
const { spawn, exec } = require('child_process')
|
|
9
|
+
const yaml = require('js-yaml')
|
|
6
10
|
|
|
7
11
|
const HELP = `
|
|
8
12
|
${chalk.bold('graph test')} ${chalk.dim('[options]')} ${chalk.bold('<datasource>')}
|
|
9
13
|
|
|
10
14
|
${chalk.dim('Options:')}
|
|
11
|
-
|
|
12
|
-
-
|
|
15
|
+
-c, --coverage Run the tests in coverage mode. Works with v0.2.1 and above
|
|
16
|
+
-d, --docker Run the tests in a docker container(Note: Please execute from the root folder of the subgraph)
|
|
17
|
+
-f --force Binary - overwrites folder + file when downloading. Docker - rebuilds the docker image
|
|
13
18
|
-h, --help Show usage information
|
|
14
19
|
-l, --logs Logs to the console information about the OS, CPU model and download url (debugging purposes)
|
|
20
|
+
-r, --recompile Force-recompile tests (Not available in 0.2.2 and earlier versions)
|
|
15
21
|
-v, --version <tag> Choose the version of the rust binary that you want to be downloaded/used
|
|
16
22
|
`
|
|
17
23
|
|
|
18
24
|
module.exports = {
|
|
19
25
|
description: 'Runs rust binary for subgraph testing',
|
|
20
26
|
run: async toolbox => {
|
|
21
|
-
// Obtain tools
|
|
22
|
-
let { print } = toolbox
|
|
23
|
-
|
|
24
27
|
// Read CLI parameters
|
|
25
|
-
let {
|
|
26
|
-
|
|
28
|
+
let {
|
|
29
|
+
c,
|
|
30
|
+
coverage,
|
|
31
|
+
d,
|
|
32
|
+
docker,
|
|
33
|
+
f,
|
|
34
|
+
force,
|
|
35
|
+
h,
|
|
36
|
+
help,
|
|
37
|
+
l,
|
|
38
|
+
logs,
|
|
39
|
+
r,
|
|
40
|
+
recompile,
|
|
41
|
+
v,
|
|
42
|
+
version,
|
|
43
|
+
} = toolbox.parameters.options
|
|
27
44
|
|
|
45
|
+
let opts = new Map()
|
|
28
46
|
// Support both long and short option variants
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
opts.set("coverage", coverage || c)
|
|
48
|
+
opts.set("docker", docker || d)
|
|
49
|
+
opts.set("force", force || f)
|
|
50
|
+
opts.set("help", help || h)
|
|
51
|
+
opts.set("logs", logs || l)
|
|
52
|
+
opts.set("recompile", recompile || r)
|
|
53
|
+
opts.set("version", version || v)
|
|
54
|
+
|
|
55
|
+
// Fix if a boolean flag (e.g -c, --coverage) has an argument
|
|
56
|
+
try {
|
|
57
|
+
fixParameters(toolbox.parameters, {
|
|
58
|
+
h,
|
|
59
|
+
help,
|
|
60
|
+
c,
|
|
61
|
+
coverage,
|
|
62
|
+
d,
|
|
63
|
+
docker,
|
|
64
|
+
f,
|
|
65
|
+
force,
|
|
66
|
+
l,
|
|
67
|
+
logs,
|
|
68
|
+
r,
|
|
69
|
+
recompile
|
|
70
|
+
})
|
|
71
|
+
} catch (e) {
|
|
72
|
+
print.error(e.message)
|
|
73
|
+
process.exitCode = 1
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let datasource = toolbox.parameters.first || toolbox.parameters.array[0]
|
|
33
78
|
|
|
34
79
|
// Show help text if requested
|
|
35
|
-
if (help) {
|
|
80
|
+
if (opts.get("help")) {
|
|
36
81
|
print.info(HELP)
|
|
37
82
|
return
|
|
38
83
|
}
|
|
39
84
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
85
|
+
let result = await fetch('https://api.github.com/repos/LimeChain/matchstick/releases/latest')
|
|
86
|
+
let json = await result.json()
|
|
87
|
+
opts.set("latestVersion", json.tag_name)
|
|
88
|
+
|
|
89
|
+
if(opts.get("docker")) {
|
|
90
|
+
runDocker(datasource, opts)
|
|
91
|
+
} else {
|
|
92
|
+
runBinary(datasource, opts)
|
|
45
93
|
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
46
96
|
|
|
47
|
-
|
|
97
|
+
async function runBinary(datasource, opts) {
|
|
98
|
+
let coverageOpt = opts.get("coverage")
|
|
99
|
+
let forceOpt = opts.get("force")
|
|
100
|
+
let logsOpt = opts.get("logs")
|
|
101
|
+
let versionOpt = opts.get("version")
|
|
102
|
+
let latestVersion = opts.get("latestVersion")
|
|
103
|
+
let recompileOpt = opts.get("recompile")
|
|
48
104
|
|
|
49
|
-
|
|
50
|
-
console.log(`Download link: ${url}`)
|
|
51
|
-
}
|
|
105
|
+
const platform = getPlatform(logsOpt)
|
|
52
106
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
107
|
+
const url = `https://github.com/LimeChain/matchstick/releases/download/${versionOpt || latestVersion}/${platform}`
|
|
108
|
+
|
|
109
|
+
if (logsOpt) {
|
|
110
|
+
print.info(`Download link: ${url}`)
|
|
56
111
|
}
|
|
112
|
+
|
|
113
|
+
let binary = new Binary(platform, url, versionOpt || latestVersion)
|
|
114
|
+
forceOpt ? await binary.install(true) : await binary.install(false)
|
|
115
|
+
let args = new Array()
|
|
116
|
+
|
|
117
|
+
if (coverageOpt) args.push('-c')
|
|
118
|
+
if (recompileOpt) args.push('-r')
|
|
119
|
+
if (datasource) args.push(datasource)
|
|
120
|
+
args.length > 0 ? binary.run(...args) : binary.run()
|
|
57
121
|
}
|
|
58
122
|
|
|
59
|
-
function getPlatform(
|
|
123
|
+
function getPlatform(logsOpt) {
|
|
60
124
|
const type = os.type()
|
|
61
125
|
const arch = os.arch()
|
|
62
126
|
const release = os.release()
|
|
@@ -64,8 +128,8 @@ function getPlatform(logs) {
|
|
|
64
128
|
const majorVersion = semver.major(release)
|
|
65
129
|
const isM1 = cpuCore.model.includes("Apple M1")
|
|
66
130
|
|
|
67
|
-
if (
|
|
68
|
-
|
|
131
|
+
if (logsOpt) {
|
|
132
|
+
print.info(`OS type: ${type}\nOS arch: ${arch}\nOS release: ${release}\nOS major version: ${majorVersion}\nCPU model: ${cpuCore.model}`)
|
|
69
133
|
}
|
|
70
134
|
|
|
71
135
|
if (arch === 'x64' || (arch === 'arm64' && isM1)) {
|
|
@@ -90,3 +154,122 @@ function getPlatform(logs) {
|
|
|
90
154
|
|
|
91
155
|
throw new Error(`Unsupported platform: ${type} ${arch} ${majorVersion}`)
|
|
92
156
|
}
|
|
157
|
+
|
|
158
|
+
async function runDocker(datasource, opts) {
|
|
159
|
+
let coverageOpt = opts.get("coverage")
|
|
160
|
+
let forceOpt = opts.get("force")
|
|
161
|
+
let versionOpt = opts.get("version")
|
|
162
|
+
let latestVersion = opts.get("latestVersion")
|
|
163
|
+
let recompileOpt = opts.get("recompile")
|
|
164
|
+
|
|
165
|
+
// Remove binary-install-raw binaries, because docker has permission issues
|
|
166
|
+
// when building the docker images
|
|
167
|
+
await filesystem.remove("./node_modules/binary-install-raw/bin")
|
|
168
|
+
|
|
169
|
+
// Get current working directory
|
|
170
|
+
let current_folder = await filesystem.cwd()
|
|
171
|
+
|
|
172
|
+
// Build the Dockerfile location. Defaults to ./tests/.docker if
|
|
173
|
+
// a custom testsFolder is not declared in the subgraph.yaml
|
|
174
|
+
let dockerDir = ""
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
let doc = await yaml.load(filesystem.read('subgraph.yaml', 'utf8'))
|
|
178
|
+
testsFolder = doc.testsFolder || './tests'
|
|
179
|
+
dockerDir = testsFolder.endsWith('/') ? testsFolder + '.docker' : testsFolder + '/.docker'
|
|
180
|
+
} catch (error) {
|
|
181
|
+
print.error(error.message)
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Create the Dockerfile
|
|
186
|
+
try {
|
|
187
|
+
await filesystem.write(`${dockerDir}/Dockerfile`, dockerfile(versionOpt, latestVersion))
|
|
188
|
+
print.info('Successfully generated Dockerfile.')
|
|
189
|
+
} catch (error) {
|
|
190
|
+
print.info('A problem occurred while generating the Dockerfile. Please attend to the errors below:')
|
|
191
|
+
print.error(error.message)
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Run a command to check if matchstick image already exists
|
|
196
|
+
exec('docker images -q matchstick', (error, stdout, stderr) => {
|
|
197
|
+
// Collect all(if any) flags and options that have to be passed to the matchstick binary
|
|
198
|
+
let testArgs = ''
|
|
199
|
+
if (coverageOpt) testArgs = testArgs + ' -c'
|
|
200
|
+
if (recompileOpt) testArgs = testArgs + ' -r'
|
|
201
|
+
if (datasource) testArgs = testArgs + ' ' + datasource
|
|
202
|
+
|
|
203
|
+
// Build the `docker run` command options and flags
|
|
204
|
+
let dockerRunOpts = ['run', '-it', '--rm', '--mount', `type=bind,source=${current_folder},target=/matchstick`]
|
|
205
|
+
|
|
206
|
+
if(testArgs !== '') {
|
|
207
|
+
dockerRunOpts.push('-e')
|
|
208
|
+
dockerRunOpts.push(`ARGS=${testArgs.trim()}`)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
dockerRunOpts.push('matchstick')
|
|
212
|
+
|
|
213
|
+
// If a matchstick image does not exists, the command returns an empty string,
|
|
214
|
+
// else it'll return the image ID. Skip `docker build` if an image already exists
|
|
215
|
+
// If `-v/--version` is specified, delete current image(if any) and rebuild.
|
|
216
|
+
// Use spawn() and {stdio: 'inherit'} so we can see the logs in real time.
|
|
217
|
+
if(stdout === '' || versionOpt || forceOpt) {
|
|
218
|
+
if ((stdout !== '' && versionOpt) || forceOpt) {
|
|
219
|
+
exec('docker image rm matchstick', (error, stdout, stderr) => {
|
|
220
|
+
print.info(chalk.bold(`Removing matchstick image\n${stdout}`))
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
// Build a docker image. If the process has executed successfully
|
|
224
|
+
// run a container from that image.
|
|
225
|
+
spawn(
|
|
226
|
+
'docker',
|
|
227
|
+
['build', '--no-cache', '-f', `${dockerDir}/Dockerfile`, '-t', 'matchstick', '.'],
|
|
228
|
+
{ stdio: 'inherit' }
|
|
229
|
+
).on('close', code => {
|
|
230
|
+
if (code === 0) {
|
|
231
|
+
spawn('docker', dockerRunOpts, { stdio: 'inherit' })
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
} else {
|
|
235
|
+
print.info("Docker image already exists. Skipping `docker build` command.")
|
|
236
|
+
// Run the container from the existing matchstick docker image
|
|
237
|
+
spawn('docker', dockerRunOpts, { stdio: 'inherit' })
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// TODO: Move these in separate file (in a function maybe)
|
|
243
|
+
function dockerfile(versionOpt, latestVersion) {
|
|
244
|
+
return `
|
|
245
|
+
FROM ubuntu:20.04
|
|
246
|
+
ENV ARGS=""
|
|
247
|
+
|
|
248
|
+
# Install necessary packages
|
|
249
|
+
RUN apt update
|
|
250
|
+
RUN apt install -y nodejs
|
|
251
|
+
RUN apt install -y npm
|
|
252
|
+
RUN apt install -y git
|
|
253
|
+
RUN apt install -y postgresql
|
|
254
|
+
RUN apt install -y curl
|
|
255
|
+
RUN apt install -y cmake
|
|
256
|
+
RUN npm install -g @graphprotocol/graph-cli
|
|
257
|
+
|
|
258
|
+
# Download the latest linux binary
|
|
259
|
+
RUN curl -OL https://github.com/LimeChain/matchstick/releases/download/${versionOpt || latestVersion}/binary-linux-20
|
|
260
|
+
|
|
261
|
+
# Make it executable
|
|
262
|
+
RUN chmod a+x binary-linux-20
|
|
263
|
+
|
|
264
|
+
# Create a matchstick dir where the host will be copied
|
|
265
|
+
RUN mkdir matchstick
|
|
266
|
+
WORKDIR matchstick
|
|
267
|
+
|
|
268
|
+
# Copy host to /matchstick
|
|
269
|
+
COPY ../ .
|
|
270
|
+
|
|
271
|
+
RUN graph codegen
|
|
272
|
+
RUN graph build
|
|
273
|
+
|
|
274
|
+
CMD ../binary-linux-20 \${ARGS}`
|
|
275
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports = class EthereumContract {
|
|
2
|
+
static identifierName() {
|
|
3
|
+
return 'address'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
constructor(address) {
|
|
7
|
+
this.address = address
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
validate() {
|
|
11
|
+
const pattern = /^(0x)?[0-9a-fA-F]{40}$/
|
|
12
|
+
|
|
13
|
+
const errorMessage = "Must be 40 hexadecimal characters, with an optional '0x' prefix."
|
|
14
|
+
|
|
15
|
+
const valid = pattern.test(this.address)
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
valid,
|
|
19
|
+
error: valid ? null : errorMessage,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { abiEvents } = require('../../../scaffold/schema')
|
|
2
|
+
const ABI = require('../abi')
|
|
3
|
+
|
|
4
|
+
const source = ({ contract, contractName }) => `
|
|
5
|
+
address: '${contract}'
|
|
6
|
+
abi: ${contractName}`
|
|
7
|
+
|
|
8
|
+
const mapping = ({ abi, contractName }) => `
|
|
9
|
+
kind: ethereum/events
|
|
10
|
+
apiVersion: 0.0.5
|
|
11
|
+
language: wasm/assemblyscript
|
|
12
|
+
entities:
|
|
13
|
+
${abiEvents(abi)
|
|
14
|
+
.map(event => `- ${event.get('_alias')}`)
|
|
15
|
+
.join('\n ')}
|
|
16
|
+
abis:
|
|
17
|
+
- name: ${contractName}
|
|
18
|
+
file: ./abis/${contractName}.json
|
|
19
|
+
eventHandlers:
|
|
20
|
+
${abiEvents(abi)
|
|
21
|
+
.map(
|
|
22
|
+
event => `
|
|
23
|
+
- event: ${ABI.eventSignature(event)}
|
|
24
|
+
handler: handle${event.get('_alias')}`,
|
|
25
|
+
)
|
|
26
|
+
.join('')}
|
|
27
|
+
file: ./src/mapping.ts`
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
source,
|
|
31
|
+
mapping,
|
|
32
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { generateEventFieldAssignments } = require('../../../scaffold/mapping')
|
|
2
|
+
|
|
3
|
+
const generatePlaceholderHandlers = ({ abi, events, contractName }) =>
|
|
4
|
+
`
|
|
5
|
+
import { BigInt } from '@graphprotocol/graph-ts'
|
|
6
|
+
import { ${contractName}, ${events.map(event => event._alias)} }
|
|
7
|
+
from '../generated/${contractName}/${contractName}'
|
|
8
|
+
import { ExampleEntity } from '../generated/schema'
|
|
9
|
+
|
|
10
|
+
${events
|
|
11
|
+
.map((event, index) =>
|
|
12
|
+
index === 0
|
|
13
|
+
? `
|
|
14
|
+
export function handle${event._alias}(event: ${event._alias}): void {
|
|
15
|
+
// Entities can be loaded from the store using a string ID; this ID
|
|
16
|
+
// needs to be unique across all entities of the same type
|
|
17
|
+
let entity = ExampleEntity.load(event.transaction.from.toHex())
|
|
18
|
+
|
|
19
|
+
// Entities only exist after they have been saved to the store;
|
|
20
|
+
// \`null\` checks allow to create entities on demand
|
|
21
|
+
if (!entity) {
|
|
22
|
+
entity = new ExampleEntity(event.transaction.from.toHex())
|
|
23
|
+
|
|
24
|
+
// Entity fields can be set using simple assignments
|
|
25
|
+
entity.count = BigInt.fromI32(0)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// BigInt and BigDecimal math are supported
|
|
29
|
+
entity.count = entity.count + BigInt.fromI32(1)
|
|
30
|
+
|
|
31
|
+
// Entity fields can be set based on event parameters
|
|
32
|
+
${generateEventFieldAssignments(event)
|
|
33
|
+
.slice(0, 2)
|
|
34
|
+
.join('\n')}
|
|
35
|
+
|
|
36
|
+
// Entities can be written to the store with \`.save()\`
|
|
37
|
+
entity.save()
|
|
38
|
+
|
|
39
|
+
// Note: If a handler doesn't require existing field values, it is faster
|
|
40
|
+
// _not_ to load the entity from the store. Instead, create it fresh with
|
|
41
|
+
// \`new Entity(...)\`, set the fields that should be updated and save the
|
|
42
|
+
// entity back to the store. Fields that were not set or unset remain
|
|
43
|
+
// unchanged, allowing for partial updates to be applied.
|
|
44
|
+
|
|
45
|
+
// It is also possible to access smart contracts from mappings. For
|
|
46
|
+
// example, the contract that has emitted the event can be connected to
|
|
47
|
+
// with:
|
|
48
|
+
//
|
|
49
|
+
// let contract = Contract.bind(event.address)
|
|
50
|
+
//
|
|
51
|
+
// The following functions can then be called on this contract to access
|
|
52
|
+
// state variables and other data:
|
|
53
|
+
//
|
|
54
|
+
// ${
|
|
55
|
+
abi
|
|
56
|
+
.codeGenerator()
|
|
57
|
+
.callableFunctions()
|
|
58
|
+
.isEmpty()
|
|
59
|
+
? 'None'
|
|
60
|
+
: abi
|
|
61
|
+
.codeGenerator()
|
|
62
|
+
.callableFunctions()
|
|
63
|
+
.map(fn => `- contract.${fn.get('name')}(...)`)
|
|
64
|
+
.join('\n// ')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`
|
|
68
|
+
: `
|
|
69
|
+
export function handle${event._alias}(event: ${event._alias}): void {}
|
|
70
|
+
`,
|
|
71
|
+
)
|
|
72
|
+
.join('\n')}`
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
generatePlaceholderHandlers,
|
|
76
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const immutable = require('immutable')
|
|
2
2
|
const ABI = require('./abi')
|
|
3
3
|
const DataSourcesExtractor = require('../../command-helpers/data-sources')
|
|
4
|
-
const { validateContractValues } = require('../../validation')
|
|
5
4
|
|
|
6
5
|
module.exports = class EthereumSubgraph {
|
|
7
6
|
constructor(options = {}) {
|
|
@@ -12,7 +11,6 @@ module.exports = class EthereumSubgraph {
|
|
|
12
11
|
|
|
13
12
|
validateManifest() {
|
|
14
13
|
return this.validateAbis()
|
|
15
|
-
.concat(this.validateContractAddresses())
|
|
16
14
|
.concat(this.validateEvents())
|
|
17
15
|
.concat(this.validateCallFunctions())
|
|
18
16
|
}
|
|
@@ -72,18 +70,6 @@ ${abiNames
|
|
|
72
70
|
return nameErrors.concat(fileErrors)
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
validateContractAddresses() {
|
|
76
|
-
const ethereumAddressPattern = /^(0x)?[0-9a-fA-F]{40}$/
|
|
77
|
-
|
|
78
|
-
return validateContractValues(
|
|
79
|
-
this.manifest,
|
|
80
|
-
this.protocol,
|
|
81
|
-
'address',
|
|
82
|
-
address => ethereumAddressPattern.test(address),
|
|
83
|
-
"Must be 40 hexadecimal characters, with an optional '0x' prefix.",
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
73
|
validateEvents() {
|
|
88
74
|
const dataSourcesAndTemplates = DataSourcesExtractor.fromManifest(this.manifest, this.protocol)
|
|
89
75
|
|
package/src/protocols/index.js
CHANGED
|
@@ -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,62 @@ 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
|
+
'aurora',
|
|
61
|
+
'aurora-testnet',
|
|
62
|
+
],
|
|
63
|
+
near: [
|
|
64
|
+
'near-mainnet',
|
|
65
|
+
'near-testnet'
|
|
66
|
+
],
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
27
70
|
normalizeName(name) {
|
|
28
|
-
return
|
|
29
|
-
|
|
71
|
+
return Protocol.availableProtocols().findKey(possibleNames =>
|
|
72
|
+
possibleNames.includes(name),
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
displayName() {
|
|
77
|
+
switch (this.name) {
|
|
78
|
+
case 'ethereum':
|
|
79
|
+
return 'Ethereum'
|
|
80
|
+
case 'near':
|
|
81
|
+
return 'NEAR'
|
|
82
|
+
}
|
|
30
83
|
}
|
|
31
84
|
|
|
32
85
|
// Receives a data source kind, and checks if it's valid
|
|
33
86
|
// for the given protocol instance (this).
|
|
34
87
|
isValidKindName(kind) {
|
|
35
|
-
return
|
|
88
|
+
return Protocol.availableProtocols()
|
|
36
89
|
.get(this.name, immutable.List())
|
|
37
90
|
.includes(kind)
|
|
38
91
|
}
|
|
@@ -40,7 +93,15 @@ module.exports = class Protocol {
|
|
|
40
93
|
hasABIs() {
|
|
41
94
|
switch (this.name) {
|
|
42
95
|
case 'ethereum':
|
|
43
|
-
|
|
96
|
+
return true
|
|
97
|
+
case 'near':
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
hasEvents() {
|
|
103
|
+
switch (this.name) {
|
|
104
|
+
case 'ethereum':
|
|
44
105
|
return true
|
|
45
106
|
case 'near':
|
|
46
107
|
return false
|
|
@@ -50,7 +111,6 @@ module.exports = class Protocol {
|
|
|
50
111
|
getTypeGenerator(options) {
|
|
51
112
|
switch (this.name) {
|
|
52
113
|
case 'ethereum':
|
|
53
|
-
case 'ethereum/contract':
|
|
54
114
|
return new EthereumTypeGenerator(options)
|
|
55
115
|
case 'near':
|
|
56
116
|
return null
|
|
@@ -60,7 +120,6 @@ module.exports = class Protocol {
|
|
|
60
120
|
getTemplateCodeGen(template) {
|
|
61
121
|
switch (this.name) {
|
|
62
122
|
case 'ethereum':
|
|
63
|
-
case 'ethereum/contract':
|
|
64
123
|
return new EthereumTemplateCodeGen(template)
|
|
65
124
|
default:
|
|
66
125
|
throw new Error(
|
|
@@ -72,7 +131,6 @@ module.exports = class Protocol {
|
|
|
72
131
|
getABI() {
|
|
73
132
|
switch (this.name) {
|
|
74
133
|
case 'ethereum':
|
|
75
|
-
case 'ethereum/contract':
|
|
76
134
|
return EthereumABI
|
|
77
135
|
case 'near':
|
|
78
136
|
return null
|
|
@@ -84,14 +142,38 @@ module.exports = class Protocol {
|
|
|
84
142
|
|
|
85
143
|
switch (this.name) {
|
|
86
144
|
case 'ethereum':
|
|
87
|
-
case 'ethereum/contract':
|
|
88
145
|
return new EthereumSubgraph(optionsWithProtocol)
|
|
89
146
|
case 'near':
|
|
90
147
|
return new NearSubgraph(optionsWithProtocol)
|
|
91
148
|
default:
|
|
92
|
-
throw new Error(
|
|
93
|
-
|
|
94
|
-
|
|
149
|
+
throw new Error(`Data sources with kind '${this.name}' are not supported yet`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getContract() {
|
|
154
|
+
switch (this.name) {
|
|
155
|
+
case 'ethereum':
|
|
156
|
+
return EthereumContract
|
|
157
|
+
case 'near':
|
|
158
|
+
return NearContract
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getManifestScaffold() {
|
|
163
|
+
switch (this.name) {
|
|
164
|
+
case 'ethereum':
|
|
165
|
+
return EthereumManifestScaffold
|
|
166
|
+
case 'near':
|
|
167
|
+
return NearManifestScaffold
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
getMappingScaffold() {
|
|
172
|
+
switch (this.name) {
|
|
173
|
+
case 'ethereum':
|
|
174
|
+
return EthereumMappingScaffold
|
|
175
|
+
case 'near':
|
|
176
|
+
return NearMappingScaffold
|
|
95
177
|
}
|
|
96
178
|
}
|
|
97
179
|
}
|
|
@@ -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
|
+
}
|