@graphprotocol/graph-cli 0.30.0-alpha.0 → 0.30.1
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/node_modules/@graphprotocol/graph-ts/chain/arweave.ts +76 -0
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/cosmos.ts +341 -0
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/ethereum.ts +39 -0
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/global/global.ts +286 -142
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/index.ts +4 -2
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/package.json +2 -1
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/test/test.js +4 -2
- package/examples/basic-event-handlers/node_modules/keccak/build/config.gypi +300 -125
- package/examples/basic-event-handlers/node_modules/websocket/build/config.gypi +292 -15
- 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 +1 -1
- package/resources/test/docker-compose.yml +4 -2
- package/src/codegen/schema.js +8 -45
- package/src/codegen/schema.test.js +102 -6
- package/src/codegen/types/conversions.js +19 -19
- package/src/codegen/types/index.js +0 -25
- package/src/codegen/types/index.test.js +0 -22
- package/src/command-helpers/abi.js +69 -0
- package/src/command-helpers/scaffold.js +67 -0
- package/src/commands/add.js +198 -0
- package/src/commands/init.js +25 -71
- package/src/migrations/spec_version_0_0_2.js +1 -1
- package/src/migrations/{spec_version_0_0_4.js → spec_version_0_0_3.js} +10 -6
- package/src/migrations.js +1 -1
- package/src/protocols/arweave/manifest.graphql +57 -0
- package/src/protocols/arweave/scaffold/manifest.js +19 -0
- package/src/protocols/arweave/scaffold/mapping.js +53 -0
- package/src/protocols/arweave/subgraph.js +20 -0
- package/src/protocols/{tendermint → cosmos}/manifest.graphql +6 -0
- package/src/protocols/cosmos/scaffold/manifest.js +16 -0
- package/src/protocols/cosmos/scaffold/mapping.js +39 -0
- package/src/protocols/{tendermint → cosmos}/subgraph.js +2 -1
- package/src/protocols/ethereum/codegen/abi.js +21 -4
- package/src/protocols/ethereum/scaffold/manifest.js +2 -1
- package/src/protocols/index.js +53 -20
- package/src/protocols/near/manifest.graphql +1 -0
- package/src/protocols/near/scaffold/manifest.js +4 -2
- package/src/scaffold/cosmos.test.js +86 -0
- package/src/scaffold/ethereum.test.js +2 -1
- package/src/scaffold/index.js +5 -3
- package/src/scaffold/near.test.js +2 -1
- package/src/validation/manifest.js +52 -12
- package/src/validation/schema.js +2 -0
- package/tests/cli/validation/conflicting-protocol-names/Abi.json +7 -0
- package/tests/cli/validation/conflicting-protocol-names/mapping.ts +0 -0
- package/tests/cli/validation/conflicting-protocol-names/schema.graphql +3 -0
- package/tests/cli/validation/conflicting-protocol-names/subgraph.yaml +41 -0
- package/tests/cli/validation/conflicting-protocol-names.stderr +10 -0
- package/tests/cli/validation.test.js +9 -0
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/tendermint.ts +0 -554
- package/examples/basic-event-handlers/node_modules/keccak/build/Makefile +0 -342
- package/examples/basic-event-handlers/node_modules/keccak/build/Release/.deps/Release/obj.target/keccak/src/addon.o.d.raw +0 -27
- package/examples/basic-event-handlers/node_modules/keccak/build/binding.Makefile +0 -6
- package/examples/basic-event-handlers/node_modules/keccak/build/gyp-mac-tool +0 -615
- package/examples/basic-event-handlers/node_modules/keccak/build/keccak.target.mk +0 -209
- package/examples/basic-event-handlers/node_modules/websocket/build/Makefile +0 -347
- package/examples/basic-event-handlers/node_modules/websocket/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d.raw +0 -25
- package/examples/basic-event-handlers/node_modules/websocket/build/binding.Makefile +0 -6
- package/examples/basic-event-handlers/node_modules/websocket/build/bufferutil.target.mk +0 -192
- package/examples/basic-event-handlers/node_modules/websocket/build/gyp-mac-tool +0 -615
- package/examples/basic-event-handlers/node_modules/websocket/build/validation.target.mk +0 -192
- package/src/codegen/types/defaults.js +0 -34
- package/src/migrations/mapping_api_version_0_0_6.js +0 -74
|
@@ -188,28 +188,28 @@ const VALUE_TO_ASSEMBLYSCRIPT = [
|
|
|
188
188
|
const ASSEMBLYSCRIPT_TO_VALUE = [
|
|
189
189
|
// Arrays
|
|
190
190
|
|
|
191
|
-
['Array<Address>', '[Bytes]', code => `Value.fromBytesArray(${code})
|
|
192
|
-
['Array<Bytes>', '[Bytes]', code => `Value.fromBytesArray(${code})
|
|
193
|
-
['Array<boolean>', '[Boolean]', code => `Value.fromBooleanArray(${code})
|
|
194
|
-
['Array<i32>', '[Int]', code => `Value.fromI32Array(${code})
|
|
195
|
-
['Array<BigInt>', '[BigInt]', code => `Value.fromBigIntArray(${code})
|
|
196
|
-
['Array<string>', '[String]', code => `Value.fromStringArray(${code})
|
|
197
|
-
['Array<string>', '[ID]', code => `Value.fromStringArray(${code})
|
|
198
|
-
['Array<BigDecimal>', '[BigDecimal]', code => `Value.fromBigDecimalArray(${code})
|
|
199
|
-
['Array<string>', /\[.*\]/, code => `Value.fromStringArray(${code})
|
|
200
|
-
['Array<string | null>', null, code => `Value.fromStringArray(${code})
|
|
191
|
+
['Array<Address>', '[Bytes]', code => `Value.fromBytesArray(${code})`],
|
|
192
|
+
['Array<Bytes>', '[Bytes]', code => `Value.fromBytesArray(${code})`],
|
|
193
|
+
['Array<boolean>', '[Boolean]', code => `Value.fromBooleanArray(${code})`],
|
|
194
|
+
['Array<i32>', '[Int]', code => `Value.fromI32Array(${code})`],
|
|
195
|
+
['Array<BigInt>', '[BigInt]', code => `Value.fromBigIntArray(${code})`],
|
|
196
|
+
['Array<string>', '[String]', code => `Value.fromStringArray(${code})`],
|
|
197
|
+
['Array<string>', '[ID]', code => `Value.fromStringArray(${code})`],
|
|
198
|
+
['Array<BigDecimal>', '[BigDecimal]', code => `Value.fromBigDecimalArray(${code})`],
|
|
199
|
+
['Array<string>', /\[.*\]/, code => `Value.fromStringArray(${code})`],
|
|
200
|
+
['Array<string | null>', null, code => `Value.fromStringArray(${code})`],
|
|
201
201
|
|
|
202
202
|
// Scalar values
|
|
203
203
|
|
|
204
|
-
['Address', 'Bytes', code => `Value.fromBytes(${code})
|
|
205
|
-
['Bytes', 'Bytes', code => `Value.fromBytes(${code})
|
|
206
|
-
['boolean', 'Boolean', code => `Value.fromBoolean(${code})
|
|
207
|
-
['i32', 'Int', code => `Value.fromI32(${code})
|
|
208
|
-
['BigInt', 'BigInt', code => `Value.fromBigInt(${code})
|
|
209
|
-
['string', 'String', code => `Value.fromString(${code})
|
|
210
|
-
['string', 'ID', code => `Value.fromString(${code})
|
|
211
|
-
['BigDecimal', 'BigDecimal', code => `Value.fromBigDecimal(${code})
|
|
212
|
-
['string', /.*/, code => `Value.fromString(${code})
|
|
204
|
+
['Address', 'Bytes', code => `Value.fromBytes(${code})`],
|
|
205
|
+
['Bytes', 'Bytes', code => `Value.fromBytes(${code})`],
|
|
206
|
+
['boolean', 'Boolean', code => `Value.fromBoolean(${code})`],
|
|
207
|
+
['i32', 'Int', code => `Value.fromI32(${code})`],
|
|
208
|
+
['BigInt', 'BigInt', code => `Value.fromBigInt(${code})`],
|
|
209
|
+
['string', 'String', code => `Value.fromString(${code})`],
|
|
210
|
+
['string', 'ID', code => `Value.fromString(${code})`],
|
|
211
|
+
['BigDecimal', 'BigDecimal', code => `Value.fromBigDecimal(${code})`],
|
|
212
|
+
['string', /.*/, code => `Value.fromString(${code})`],
|
|
213
213
|
]
|
|
214
214
|
|
|
215
215
|
/**
|
|
@@ -66,27 +66,6 @@ const findConversionToType = (fromTypeSystem, toTypeSystem, toType) => {
|
|
|
66
66
|
return objectifyConversion(fromTypeSystem, toTypeSystem, conversion)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const findInitializationForType = (fromTypeSystem, toTypeSystem, ascType) => {
|
|
70
|
-
const conversions = conversionsForTypeSystems(fromTypeSystem, toTypeSystem)
|
|
71
|
-
|
|
72
|
-
const conversion = conversions.find(conversion =>
|
|
73
|
-
typeof conversion.get(0) === 'string'
|
|
74
|
-
? conversion.get(0) === ascType
|
|
75
|
-
: ascType.match(conversion.get(0)),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
if (conversion === undefined) {
|
|
79
|
-
throw new Error(
|
|
80
|
-
`Conversion from '${fromTypeSystem}' to '${toTypeSystem}' for ` +
|
|
81
|
-
`target type '${ascType}' is not supported`,
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const conversionObj = objectifyConversion(fromTypeSystem, toTypeSystem, conversion)
|
|
86
|
-
|
|
87
|
-
return conversionObj.get('convert')(conversion.get(3))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
69
|
// High-level type system API
|
|
91
70
|
|
|
92
71
|
const ascTypeForProtocol = (protocol, protocolType) =>
|
|
@@ -120,9 +99,6 @@ const valueToAsc = (code, valueType) =>
|
|
|
120
99
|
const valueFromAsc = (code, valueType) =>
|
|
121
100
|
findConversionToType('AssemblyScript', 'Value', valueType).get('convert')(code)
|
|
122
101
|
|
|
123
|
-
const initializedValueFromAsc = ascType =>
|
|
124
|
-
findInitializationForType('AssemblyScript', 'Value', ascType)
|
|
125
|
-
|
|
126
102
|
module.exports = {
|
|
127
103
|
// protocol <-> AssemblyScript
|
|
128
104
|
ascTypeForProtocol,
|
|
@@ -138,5 +114,4 @@ module.exports = {
|
|
|
138
114
|
valueTypeForAsc,
|
|
139
115
|
valueToAsc,
|
|
140
116
|
valueFromAsc,
|
|
141
|
-
initializedValueFromAsc,
|
|
142
117
|
}
|
|
@@ -449,26 +449,4 @@ describe('AssemblyScript -> Value', () => {
|
|
|
449
449
|
expect(codegen.valueFromAsc('x', '[String]')).toBe('Value.fromStringArray(x)')
|
|
450
450
|
expect(codegen.valueTypeForAsc('Array<string>')).toBe('[String]')
|
|
451
451
|
})
|
|
452
|
-
|
|
453
|
-
describe('Initialization for zero/empty values', () => {
|
|
454
|
-
test('Array<string>', () => {
|
|
455
|
-
expect(codegen.initializedValueFromAsc('Array<string>')).toBe('Value.fromStringArray(new Array(0))')
|
|
456
|
-
})
|
|
457
|
-
|
|
458
|
-
test('Array<string | null>', () => {
|
|
459
|
-
expect(codegen.initializedValueFromAsc('Array<string | null>')).toBe('Value.fromStringArray(new Array(0))')
|
|
460
|
-
})
|
|
461
|
-
|
|
462
|
-
test('i32', () => {
|
|
463
|
-
expect(codegen.initializedValueFromAsc('i32')).toBe('Value.fromI32(0)')
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
test('string', () => {
|
|
467
|
-
expect(codegen.initializedValueFromAsc('string')).toBe("Value.fromString('')")
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
test('BigInt', () => {
|
|
471
|
-
expect(codegen.initializedValueFromAsc('BigInt')).toBe('Value.fromBigInt(BigInt.zero())')
|
|
472
|
-
})
|
|
473
|
-
})
|
|
474
452
|
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { withSpinner } = require('./spinner')
|
|
2
|
+
const fetch = require('node-fetch')
|
|
3
|
+
const immutable = require('immutable')
|
|
4
|
+
|
|
5
|
+
const loadAbiFromEtherscan = async (ABI, network, address) =>
|
|
6
|
+
await withSpinner(
|
|
7
|
+
`Fetching ABI from Etherscan`,
|
|
8
|
+
`Failed to fetch ABI from Etherscan`,
|
|
9
|
+
`Warnings while fetching ABI from Etherscan`,
|
|
10
|
+
async spinner => {
|
|
11
|
+
const scanApiUrl = getEtherscanLikeAPIUrl(network);
|
|
12
|
+
let result = await fetch(
|
|
13
|
+
`${scanApiUrl}?module=contract&action=getabi&address=${address}`,
|
|
14
|
+
)
|
|
15
|
+
let json = await result.json()
|
|
16
|
+
|
|
17
|
+
// Etherscan returns a JSON object that has a `status`, a `message` and
|
|
18
|
+
// a `result` field. The `status` is '0' in case of errors and '1' in
|
|
19
|
+
// case of success
|
|
20
|
+
if (json.status === '1') {
|
|
21
|
+
return new ABI('Contract', undefined, immutable.fromJS(JSON.parse(json.result)))
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error('ABI not found, try loading it from a local file')
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const loadAbiFromBlockScout = async (ABI, network, address) =>
|
|
29
|
+
await withSpinner(
|
|
30
|
+
`Fetching ABI from BlockScout`,
|
|
31
|
+
`Failed to fetch ABI from BlockScout`,
|
|
32
|
+
`Warnings while fetching ABI from BlockScout`,
|
|
33
|
+
async spinner => {
|
|
34
|
+
let result = await fetch(
|
|
35
|
+
`https://blockscout.com/${
|
|
36
|
+
network.replace('-', '/')
|
|
37
|
+
}/api?module=contract&action=getabi&address=${address}`,
|
|
38
|
+
)
|
|
39
|
+
let json = await result.json()
|
|
40
|
+
|
|
41
|
+
// BlockScout returns a JSON object that has a `status`, a `message` and
|
|
42
|
+
// a `result` field. The `status` is '0' in case of errors and '1' in
|
|
43
|
+
// case of success
|
|
44
|
+
if (json.status === '1') {
|
|
45
|
+
return new ABI('Contract', undefined, immutable.fromJS(JSON.parse(json.result)))
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error('ABI not found, try loading it from a local file')
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const getEtherscanLikeAPIUrl = (network) => {
|
|
53
|
+
switch (network) {
|
|
54
|
+
case "mainnet": return `https://api.etherscan.io/api`
|
|
55
|
+
case "arbitrum-one": return `https://api.arbiscan.io/api`
|
|
56
|
+
case "bsc": return `https://api.bscscan.com/api`
|
|
57
|
+
case "matic": return `https://api.polygonscan.com/api`
|
|
58
|
+
case "mumbai": return `https://api-testnet.polygonscan.com/api`
|
|
59
|
+
case "aurora": return `https://api.aurorascan.dev/api`
|
|
60
|
+
case "aurora-testnet": return `https://api-testnet.aurorascan.dev/api`
|
|
61
|
+
case "optimism-kovan": return `https://api-kovan-optimistic.etherscan.io/api`
|
|
62
|
+
default: return `https://api-${network}.etherscan.io/api`
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
loadAbiFromEtherscan,
|
|
68
|
+
loadAbiFromBlockScout,
|
|
69
|
+
}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
const fs = require('fs-extra')
|
|
2
2
|
const path = require('path')
|
|
3
|
+
const prettier = require('prettier')
|
|
4
|
+
const yaml = require('yaml')
|
|
3
5
|
|
|
4
6
|
const { step } = require('./spinner')
|
|
5
7
|
const Scaffold = require('../scaffold')
|
|
8
|
+
const { generateEventIndexingHandlers } = require('../scaffold/mapping')
|
|
9
|
+
const { generateEventType, abiEvents } = require('../scaffold/schema')
|
|
10
|
+
const { strings } = require('gluegun')
|
|
11
|
+
const { Map } = require('immutable')
|
|
12
|
+
|
|
13
|
+
const generateDataSource = async (protocol, contractName, network, contractAddress, abi) => {
|
|
14
|
+
const protocolManifest = protocol.getManifestScaffold()
|
|
15
|
+
|
|
16
|
+
return Map.of(
|
|
17
|
+
'kind', protocol.name,
|
|
18
|
+
'name', contractName,
|
|
19
|
+
'network', network,
|
|
20
|
+
'source', yaml.parse(prettier.format(protocolManifest.source({contract: contractAddress, contractName}),
|
|
21
|
+
{parser: 'yaml'})),
|
|
22
|
+
'mapping', yaml.parse(prettier.format(protocolManifest.mapping({abi, contractName}),
|
|
23
|
+
{parser: 'yaml'}))
|
|
24
|
+
).asMutable()
|
|
25
|
+
}
|
|
6
26
|
|
|
7
27
|
const generateScaffold = async (
|
|
8
28
|
{
|
|
@@ -13,6 +33,7 @@ const generateScaffold = async (
|
|
|
13
33
|
subgraphName,
|
|
14
34
|
indexEvents,
|
|
15
35
|
contractName = 'Contract',
|
|
36
|
+
dataSourceName,
|
|
16
37
|
node,
|
|
17
38
|
},
|
|
18
39
|
spinner,
|
|
@@ -27,6 +48,7 @@ const generateScaffold = async (
|
|
|
27
48
|
network,
|
|
28
49
|
contractName,
|
|
29
50
|
subgraphName,
|
|
51
|
+
dataSourceName,
|
|
30
52
|
node,
|
|
31
53
|
})
|
|
32
54
|
|
|
@@ -59,8 +81,53 @@ const writeScaffold = async (scaffold, directory, spinner) => {
|
|
|
59
81
|
await writeScaffoldDirectory(scaffold, directory, spinner)
|
|
60
82
|
}
|
|
61
83
|
|
|
84
|
+
const writeABI = async (abi, contractName, abiPath = `./abis/${contractName}.json`) => {
|
|
85
|
+
let data = prettier.format(JSON.stringify(abi.data), {
|
|
86
|
+
parser: 'json',
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
await fs.writeFile(abiPath, data, { encoding: 'utf-8' })
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const writeSchema = async (abi, protocol, schemaPath, entities) => {
|
|
93
|
+
const events = protocol.hasEvents()
|
|
94
|
+
? abiEvents(abi).filter(event => entities.indexOf(event.get('name')) === -1).toJS()
|
|
95
|
+
: []
|
|
96
|
+
|
|
97
|
+
let data = prettier.format(
|
|
98
|
+
events.map(
|
|
99
|
+
event => generateEventType(event, protocol.name)
|
|
100
|
+
).join('\n\n'),
|
|
101
|
+
{
|
|
102
|
+
parser: 'graphql',
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
await fs.appendFile(schemaPath, data, { encoding: 'utf-8' })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const writeMapping = async (abi, protocol, contractName, entities) => {
|
|
110
|
+
const events = protocol.hasEvents()
|
|
111
|
+
? abiEvents(abi).filter(event => entities.indexOf(event.get('name')) === -1).toJS()
|
|
112
|
+
: []
|
|
113
|
+
|
|
114
|
+
let mapping = prettier.format(
|
|
115
|
+
generateEventIndexingHandlers(
|
|
116
|
+
events,
|
|
117
|
+
contractName,
|
|
118
|
+
),
|
|
119
|
+
{ parser: 'typescript', semi: false },
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
await fs.writeFile(`./src/${strings.kebabCase(contractName)}.ts`, mapping, { encoding: 'utf-8' })
|
|
123
|
+
}
|
|
124
|
+
|
|
62
125
|
module.exports = {
|
|
63
126
|
...module.exports,
|
|
64
127
|
generateScaffold,
|
|
65
128
|
writeScaffold,
|
|
129
|
+
generateDataSource,
|
|
130
|
+
writeABI,
|
|
131
|
+
writeSchema,
|
|
132
|
+
writeMapping,
|
|
66
133
|
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const chalk = require('chalk')
|
|
2
|
+
const toolbox = require('gluegun/toolbox')
|
|
3
|
+
const immutable = require('immutable')
|
|
4
|
+
const { withSpinner } = require('../command-helpers/spinner')
|
|
5
|
+
const Subgraph = require('../subgraph')
|
|
6
|
+
const Protocol = require('../protocols')
|
|
7
|
+
const DataSourcesExtractor = require('../command-helpers/data-sources')
|
|
8
|
+
const { generateDataSource, writeABI, writeSchema, writeMapping } = require('../command-helpers/scaffold')
|
|
9
|
+
const { loadAbiFromEtherscan, loadAbiFromBlockScout } = require('../command-helpers/abi')
|
|
10
|
+
const EthereumABI = require('../protocols/ethereum/abi')
|
|
11
|
+
const { fixParameters } = require('../command-helpers/gluegun')
|
|
12
|
+
|
|
13
|
+
const HELP = `
|
|
14
|
+
${chalk.bold('graph add')} <address> [<subgraph-manifest default: "./subgraph.yaml">]
|
|
15
|
+
|
|
16
|
+
${chalk.dim('Options:')}
|
|
17
|
+
|
|
18
|
+
--abi <path> Path to the contract ABI (default: download from Etherscan)
|
|
19
|
+
--contract-name Name of the contract (default: Contract)
|
|
20
|
+
--merge-entities Whether to merge entities with the same name (default: false)
|
|
21
|
+
-h, --help Show usage information
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
description: 'Creates a new subgraph with basic scaffolding',
|
|
26
|
+
run: async toolbox => {
|
|
27
|
+
// Obtain tools
|
|
28
|
+
let { print, system } = toolbox
|
|
29
|
+
|
|
30
|
+
// Read CLI parameters
|
|
31
|
+
let {
|
|
32
|
+
abi,
|
|
33
|
+
contractName,
|
|
34
|
+
h,
|
|
35
|
+
help,
|
|
36
|
+
mergeEntities
|
|
37
|
+
} = toolbox.parameters.options
|
|
38
|
+
|
|
39
|
+
let address = toolbox.parameters.first
|
|
40
|
+
let manifestPath = toolbox.parameters.second || './subgraph.yaml'
|
|
41
|
+
contractName = contractName || 'Contract'
|
|
42
|
+
|
|
43
|
+
// Validate the address
|
|
44
|
+
if (!address) {
|
|
45
|
+
print.error('No contract address provided')
|
|
46
|
+
process.exitCode = 1
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
fixParameters(toolbox.parameters, {
|
|
52
|
+
h,
|
|
53
|
+
help,
|
|
54
|
+
mergeEntities,
|
|
55
|
+
})
|
|
56
|
+
} catch (e) {
|
|
57
|
+
print.error(e.message)
|
|
58
|
+
process.exitCode = 1
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Show help text if requested
|
|
63
|
+
if (help || h) {
|
|
64
|
+
print.info(HELP)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const dataSourcesAndTemplates = await DataSourcesExtractor.fromFilePath(manifestPath)
|
|
69
|
+
let protocol = Protocol.fromDataSources(dataSourcesAndTemplates)
|
|
70
|
+
let manifest = await Subgraph.load(manifestPath, { protocol })
|
|
71
|
+
let network = manifest.result.get('dataSources').get(0).get('network')
|
|
72
|
+
let result = manifest.result.asMutable()
|
|
73
|
+
|
|
74
|
+
let entities = getEntities(manifest)
|
|
75
|
+
let contractNames = getContractNames(manifest)
|
|
76
|
+
if (contractNames.indexOf(contractName) !== -1) {
|
|
77
|
+
print.error(
|
|
78
|
+
`Datasource or template with name ${contractName} already exists, please choose a different name`,
|
|
79
|
+
)
|
|
80
|
+
process.exitCode = 1
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let ethabi = null
|
|
85
|
+
if (abi) {
|
|
86
|
+
ethabi = EthereumABI.load(contractName, abi)
|
|
87
|
+
} else {
|
|
88
|
+
if (network === 'poa-core') {
|
|
89
|
+
ethabi = await loadAbiFromBlockScout(EthereumABI, network, address)
|
|
90
|
+
} else {
|
|
91
|
+
ethabi = await loadAbiFromEtherscan(EthereumABI, network, address)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let { collisionEntities, onlyCollisions, abiData } = updateEventNamesOnCollision(ethabi, entities, contractName, mergeEntities)
|
|
96
|
+
ethabi.data = abiData
|
|
97
|
+
|
|
98
|
+
await writeABI(ethabi, contractName, abi)
|
|
99
|
+
await writeSchema(ethabi, protocol, result.getIn(['schema', 'file']), collisionEntities)
|
|
100
|
+
await writeMapping(ethabi, protocol, contractName, collisionEntities)
|
|
101
|
+
|
|
102
|
+
let dataSources = result.get('dataSources')
|
|
103
|
+
let dataSource = await generateDataSource(protocol,
|
|
104
|
+
contractName, network, address, ethabi)
|
|
105
|
+
|
|
106
|
+
// Handle the collisions edge case by copying another data source yaml data
|
|
107
|
+
if (mergeEntities && onlyCollisions) {
|
|
108
|
+
let firstDataSource = dataSources.get(0)
|
|
109
|
+
let source = dataSource.get('source')
|
|
110
|
+
let mapping = firstDataSource.get('mapping').asMutable()
|
|
111
|
+
|
|
112
|
+
// Save the address of the new data source
|
|
113
|
+
source.abi = firstDataSource.get('source').get('abi')
|
|
114
|
+
|
|
115
|
+
dataSource.set('mapping', mapping)
|
|
116
|
+
dataSource.set('source', source)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
result.set('dataSources', dataSources.push(dataSource))
|
|
120
|
+
|
|
121
|
+
await Subgraph.write(result, manifestPath)
|
|
122
|
+
|
|
123
|
+
// Detect Yarn and/or NPM
|
|
124
|
+
let yarn = await system.which('yarn')
|
|
125
|
+
let npm = await system.which('npm')
|
|
126
|
+
if (!yarn && !npm) {
|
|
127
|
+
print.error(
|
|
128
|
+
`Neither Yarn nor NPM were found on your system. Please install one of them.`,
|
|
129
|
+
)
|
|
130
|
+
process.exitCode = 1
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await withSpinner(
|
|
135
|
+
'Running codegen',
|
|
136
|
+
'Failed to run codegen',
|
|
137
|
+
'Warning during codegen',
|
|
138
|
+
async spinner => {
|
|
139
|
+
await system.run(yarn ? 'yarn codegen' : 'npm run codegen')
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const getEntities = (manifest) => {
|
|
146
|
+
let dataSources = manifest.result.get('dataSources', immutable.List())
|
|
147
|
+
let templates = manifest.result.get('templates', immutable.List())
|
|
148
|
+
|
|
149
|
+
return dataSources
|
|
150
|
+
.concat(templates)
|
|
151
|
+
.map(dataSource => dataSource.getIn(['mapping', 'entities']))
|
|
152
|
+
.flatten()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const getContractNames = (manifest) => {
|
|
156
|
+
let dataSources = manifest.result.get('dataSources', immutable.List())
|
|
157
|
+
let templates = manifest.result.get('templates', immutable.List())
|
|
158
|
+
|
|
159
|
+
return dataSources
|
|
160
|
+
.concat(templates)
|
|
161
|
+
.map(dataSource => dataSource.get('name'))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const updateEventNamesOnCollision = (ethabi, entities, contractName, mergeEntities) => {
|
|
165
|
+
let abiData = ethabi.data
|
|
166
|
+
let { print } = toolbox
|
|
167
|
+
let collisionEntities = []
|
|
168
|
+
let onlyCollisions = true
|
|
169
|
+
|
|
170
|
+
for (let i = 0; i < abiData.size; i++) {
|
|
171
|
+
let dataRow = abiData.get(i).asMutable()
|
|
172
|
+
|
|
173
|
+
if (dataRow.get('type') === 'event'){
|
|
174
|
+
if (entities.indexOf(dataRow.get('name')) !== -1) {
|
|
175
|
+
if (entities.indexOf(`${contractName}${dataRow.get('name')}`) !== -1) {
|
|
176
|
+
print.error(`Contract name ('${contractName}')
|
|
177
|
+
+ event name ('${dataRow.get('name')}') entity already exists.`)
|
|
178
|
+
process.exitCode = 1
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (mergeEntities) {
|
|
183
|
+
collisionEntities.push(dataRow.get('name'))
|
|
184
|
+
abiData = abiData.asImmutable().delete(i) // needs to be immutable when deleting, yes you read that right - https://github.com/immutable-js/immutable-js/issues/1901
|
|
185
|
+
i-- // deletion also shifts values to the left
|
|
186
|
+
continue
|
|
187
|
+
} else {
|
|
188
|
+
dataRow.set('name', `${contractName}${dataRow.get('name')}`)
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
onlyCollisions = false
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
abiData = abiData.asMutable().set(i, dataRow)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { abiData, collisionEntities, onlyCollisions }
|
|
198
|
+
}
|
package/src/commands/init.js
CHANGED
|
@@ -16,6 +16,7 @@ const { initNetworksConfig } = require('../command-helpers/network')
|
|
|
16
16
|
const { withSpinner, step } = require('../command-helpers/spinner')
|
|
17
17
|
const { fixParameters } = require('../command-helpers/gluegun')
|
|
18
18
|
const { chooseNodeUrl } = require('../command-helpers/node')
|
|
19
|
+
const { loadAbiFromEtherscan, loadAbiFromBlockScout } = require('../command-helpers/abi')
|
|
19
20
|
const { generateScaffold, writeScaffold } = require('../command-helpers/scaffold')
|
|
20
21
|
const { abiEvents } = require('../scaffold/schema')
|
|
21
22
|
const { validateContract } = require('../validation')
|
|
@@ -57,6 +58,11 @@ ${chalk.dim.underline('NEAR:')}
|
|
|
57
58
|
|
|
58
59
|
--network <${availableNetworks.get('near').join('|')}>
|
|
59
60
|
Selects the network the contract is deployed to
|
|
61
|
+
|
|
62
|
+
${chalk.dim.underline('Cosmos:')}
|
|
63
|
+
|
|
64
|
+
--network <${availableNetworks.get('cosmos').join('|')}>
|
|
65
|
+
Selects the network the contract is deployed to
|
|
60
66
|
`
|
|
61
67
|
|
|
62
68
|
const processInitForm = async (
|
|
@@ -169,6 +175,11 @@ const processInitForm = async (
|
|
|
169
175
|
return value
|
|
170
176
|
},
|
|
171
177
|
},
|
|
178
|
+
// TODO:
|
|
179
|
+
//
|
|
180
|
+
// protocols that don't support contract
|
|
181
|
+
// - arweave
|
|
182
|
+
// - tendermint
|
|
172
183
|
{
|
|
173
184
|
type: 'input',
|
|
174
185
|
name: 'contract',
|
|
@@ -176,10 +187,10 @@ const processInitForm = async (
|
|
|
176
187
|
ProtocolContract = protocolInstance.getContract()
|
|
177
188
|
return `Contract ${ProtocolContract.identifierName()}`
|
|
178
189
|
},
|
|
179
|
-
skip: fromExample !== undefined,
|
|
190
|
+
skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
|
|
180
191
|
initial: contract,
|
|
181
192
|
validate: async value => {
|
|
182
|
-
if (fromExample !== undefined) {
|
|
193
|
+
if (fromExample !== undefined || !protocolInstance.hasContract()) {
|
|
183
194
|
return true
|
|
184
195
|
}
|
|
185
196
|
|
|
@@ -237,11 +248,11 @@ const processInitForm = async (
|
|
|
237
248
|
name: 'contractName',
|
|
238
249
|
message: 'Contract Name',
|
|
239
250
|
initial: contractName || 'Contract',
|
|
240
|
-
skip: () => fromExample !== undefined,
|
|
251
|
+
skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
|
|
241
252
|
validate: value => value && value.length > 0,
|
|
242
253
|
result: value => {
|
|
243
|
-
contractName = value
|
|
244
|
-
return value
|
|
254
|
+
contractName = value
|
|
255
|
+
return value
|
|
245
256
|
}
|
|
246
257
|
},
|
|
247
258
|
]
|
|
@@ -254,67 +265,6 @@ const processInitForm = async (
|
|
|
254
265
|
}
|
|
255
266
|
}
|
|
256
267
|
|
|
257
|
-
const loadAbiFromBlockScout = async (ABI, network, address) =>
|
|
258
|
-
await withSpinner(
|
|
259
|
-
`Fetching ABI from BlockScout`,
|
|
260
|
-
`Failed to fetch ABI from BlockScout`,
|
|
261
|
-
`Warnings while fetching ABI from BlockScout`,
|
|
262
|
-
async spinner => {
|
|
263
|
-
let result = await fetch(
|
|
264
|
-
`https://blockscout.com/${
|
|
265
|
-
network.replace('-', '/')
|
|
266
|
-
}/api?module=contract&action=getabi&address=${address}`,
|
|
267
|
-
)
|
|
268
|
-
let json = await result.json()
|
|
269
|
-
|
|
270
|
-
// BlockScout returns a JSON object that has a `status`, a `message` and
|
|
271
|
-
// a `result` field. The `status` is '0' in case of errors and '1' in
|
|
272
|
-
// case of success
|
|
273
|
-
if (json.status === '1') {
|
|
274
|
-
return new ABI('Contract', undefined, immutable.fromJS(JSON.parse(json.result)))
|
|
275
|
-
} else {
|
|
276
|
-
throw new Error('ABI not found, try loading it from a local file')
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
const getEtherscanLikeAPIUrl = (network) => {
|
|
282
|
-
switch(network){
|
|
283
|
-
case "mainnet": return `https://api.etherscan.io/api`;
|
|
284
|
-
case "arbitrum-one": return `https://api.arbiscan.io/api`;
|
|
285
|
-
case "bsc": return `https://api.bscscan.com/api`;
|
|
286
|
-
case "matic": return `https://api.polygonscan.com/api`;
|
|
287
|
-
case "mumbai": return `https://api-testnet.polygonscan.com/api`;
|
|
288
|
-
case "aurora": return `https://api.aurorascan.dev/api`;
|
|
289
|
-
case "aurora-testnet": return `https://api-testnet.aurorascan.dev/api`;
|
|
290
|
-
case "optimism-kovan": return `https://api-kovan-optimistic.etherscan.io/api`;
|
|
291
|
-
default: return `https://api-${network}.etherscan.io/api`;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const loadAbiFromEtherscan = async (ABI, network, address) =>
|
|
296
|
-
await withSpinner(
|
|
297
|
-
`Fetching ABI from Etherscan`,
|
|
298
|
-
`Failed to fetch ABI from Etherscan`,
|
|
299
|
-
`Warnings while fetching ABI from Etherscan`,
|
|
300
|
-
async spinner => {
|
|
301
|
-
const scanApiUrl = getEtherscanLikeAPIUrl(network);
|
|
302
|
-
let result = await fetch(
|
|
303
|
-
`${scanApiUrl}?module=contract&action=getabi&address=${address}`,
|
|
304
|
-
)
|
|
305
|
-
let json = await result.json()
|
|
306
|
-
|
|
307
|
-
// Etherscan returns a JSON object that has a `status`, a `message` and
|
|
308
|
-
// a `result` field. The `status` is '0' in case of errors and '1' in
|
|
309
|
-
// case of success
|
|
310
|
-
if (json.status === '1') {
|
|
311
|
-
return new ABI('Contract', undefined, immutable.fromJS(JSON.parse(json.result)))
|
|
312
|
-
} else {
|
|
313
|
-
throw new Error('ABI not found, try loading it from a local file')
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
)
|
|
317
|
-
|
|
318
268
|
const loadAbiFromFile = async (ABI, filename) => {
|
|
319
269
|
let exists = await toolbox.filesystem.exists(filename)
|
|
320
270
|
|
|
@@ -768,6 +718,7 @@ const initSubgraphFromContract = async (
|
|
|
768
718
|
contract,
|
|
769
719
|
indexEvents,
|
|
770
720
|
contractName,
|
|
721
|
+
dataSourceName,
|
|
771
722
|
node,
|
|
772
723
|
studio,
|
|
773
724
|
product,
|
|
@@ -822,6 +773,7 @@ const initSubgraphFromContract = async (
|
|
|
822
773
|
contract,
|
|
823
774
|
indexEvents,
|
|
824
775
|
contractName,
|
|
776
|
+
dataSourceName,
|
|
825
777
|
node,
|
|
826
778
|
},
|
|
827
779
|
spinner,
|
|
@@ -835,11 +787,13 @@ const initSubgraphFromContract = async (
|
|
|
835
787
|
return
|
|
836
788
|
}
|
|
837
789
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
790
|
+
if (protocolInstance.hasContract()) {
|
|
791
|
+
let identifierName = protocolInstance.getContract().identifierName()
|
|
792
|
+
let networkConf = await initNetworksConfig(toolbox, directory, identifierName)
|
|
793
|
+
if (networkConf !== true) {
|
|
794
|
+
process.exitCode = 1
|
|
795
|
+
return
|
|
796
|
+
}
|
|
843
797
|
}
|
|
844
798
|
|
|
845
799
|
// Initialize a fresh Git repository
|
|
@@ -6,7 +6,7 @@ const { loadManifest } = require('./util/load-manifest')
|
|
|
6
6
|
// Spec version to 0.0.2 uses top level templates. graph-cli no longer supports
|
|
7
7
|
// 0.0.1 which used nested templates.
|
|
8
8
|
module.exports = {
|
|
9
|
-
name: 'Bump
|
|
9
|
+
name: 'Bump manifest specVersion from 0.0.1 to 0.0.2',
|
|
10
10
|
predicate: async ({ sourceDir, manifestFile }) => {
|
|
11
11
|
let manifest = await loadManifest(manifestFile)
|
|
12
12
|
return (
|