@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.
- package/package.json +1 -1
- package/src/codegen/types/index.js +8 -1
- package/src/command-helpers/scaffold.js +66 -0
- package/src/commands/init.js +128 -92
- 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 +88 -8
- 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/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,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
|
|
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
|
|
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
|
-
|
|
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
|
|
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('
|
|
1
|
+
const ABI = require('../protocols/ethereum/abi')
|
|
2
2
|
const immutable = require('immutable')
|
|
3
|
-
const
|
|
4
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
+
})
|