@graphprotocol/graph-cli 0.37.4 → 0.37.5
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/CHANGELOG.md +8 -0
- package/dist/bin.js +6 -0
- package/dist/cli.js +26 -0
- package/dist/codegen/schema.js +244 -0
- package/dist/codegen/schema.test.js +455 -0
- package/dist/codegen/template.js +66 -0
- package/dist/codegen/types/conversions.js +335 -0
- package/dist/codegen/types/index.js +71 -0
- package/dist/codegen/types/index.test.js +563 -0
- package/dist/codegen/typescript.js +200 -0
- package/dist/codegen/util.js +40 -0
- package/dist/codegen/util.test.js +93 -0
- package/dist/command-helpers/abi.js +76 -0
- package/dist/command-helpers/auth.js +76 -0
- package/dist/command-helpers/compiler.js +66 -0
- package/dist/command-helpers/data-sources.js +29 -0
- package/dist/command-helpers/fs.js +9 -0
- package/dist/command-helpers/gluegun.js +48 -0
- package/dist/command-helpers/ipfs.js +5 -0
- package/dist/command-helpers/jsonrpc.js +27 -0
- package/dist/command-helpers/network.js +90 -0
- package/dist/command-helpers/network.test.js +86 -0
- package/dist/command-helpers/node.js +40 -0
- package/dist/command-helpers/scaffold.js +110 -0
- package/dist/command-helpers/spinner.js +80 -0
- package/dist/command-helpers/studio.js +14 -0
- package/dist/command-helpers/studio.test.js +41 -0
- package/dist/command-helpers/subgraph.js +22 -0
- package/dist/command-helpers/version.js +66 -0
- package/dist/command-helpers/version.test.js +186 -0
- package/dist/commands/add.js +187 -0
- package/dist/commands/auth.js +122 -0
- package/dist/commands/build.js +136 -0
- package/dist/commands/codegen.js +128 -0
- package/dist/commands/create.js +94 -0
- package/dist/commands/deploy.js +335 -0
- package/dist/commands/init.js +800 -0
- package/dist/commands/local.js +380 -0
- package/dist/commands/remove.js +95 -0
- package/dist/commands/test.js +293 -0
- package/dist/compiler/asc.js +83 -0
- package/dist/compiler/index.js +466 -0
- package/dist/debug.js +16 -0
- package/dist/migrations/mapping_api_version_0_0_1.js +83 -0
- package/dist/migrations/mapping_api_version_0_0_2.js +83 -0
- package/dist/migrations/mapping_api_version_0_0_3.js +83 -0
- package/dist/migrations/mapping_api_version_0_0_4.js +85 -0
- package/dist/migrations/mapping_api_version_0_0_5.js +85 -0
- package/dist/migrations/spec_version_0_0_2.js +45 -0
- package/dist/migrations/spec_version_0_0_3.js +50 -0
- package/dist/migrations/util/load-manifest.js +16 -0
- package/dist/migrations/util/versions.js +28 -0
- package/dist/migrations.js +54 -0
- package/dist/protocols/arweave/manifest.graphql +57 -0
- package/dist/protocols/arweave/scaffold/manifest.js +18 -0
- package/dist/protocols/arweave/scaffold/mapping.js +52 -0
- package/dist/protocols/arweave/subgraph.js +23 -0
- package/dist/protocols/contract.js +2 -0
- package/dist/protocols/cosmos/manifest.graphql +76 -0
- package/dist/protocols/cosmos/scaffold/manifest.js +15 -0
- package/dist/protocols/cosmos/scaffold/mapping.js +38 -0
- package/dist/protocols/cosmos/subgraph.js +28 -0
- package/dist/protocols/ethereum/abi.js +138 -0
- package/dist/protocols/ethereum/codegen/abi.js +474 -0
- package/dist/protocols/ethereum/codegen/abi.test.js +325 -0
- package/dist/protocols/ethereum/codegen/template.js +52 -0
- package/dist/protocols/ethereum/contract.js +22 -0
- package/dist/protocols/ethereum/manifest.graphql +94 -0
- package/dist/protocols/ethereum/scaffold/manifest.js +32 -0
- package/dist/protocols/ethereum/scaffold/mapping.js +65 -0
- package/dist/protocols/ethereum/subgraph.js +172 -0
- package/dist/protocols/ethereum/type-generator.js +123 -0
- package/dist/protocols/index.js +266 -0
- package/dist/protocols/ipfs/codegen/file_template.js +53 -0
- package/dist/protocols/near/contract.js +41 -0
- package/dist/protocols/near/manifest.graphql +64 -0
- package/dist/protocols/near/scaffold/manifest.js +16 -0
- package/dist/protocols/near/scaffold/mapping.js +38 -0
- package/dist/protocols/near/subgraph.js +23 -0
- package/dist/protocols/subgraph.js +2 -0
- package/dist/protocols/substreams/manifest.graphql +45 -0
- package/dist/protocols/substreams/scaffold/manifest.js +12 -0
- package/dist/protocols/substreams/subgraph.js +23 -0
- package/dist/scaffold/cosmos.test.js +82 -0
- package/dist/scaffold/ethereum.test.js +496 -0
- package/dist/scaffold/index.js +133 -0
- package/dist/scaffold/mapping.js +57 -0
- package/dist/scaffold/near.test.js +85 -0
- package/dist/scaffold/schema.js +86 -0
- package/dist/scaffold/tests.js +179 -0
- package/dist/schema.js +54 -0
- package/dist/subgraph.js +243 -0
- package/dist/type-generator.js +225 -0
- package/dist/validation/contract.js +44 -0
- package/dist/validation/index.js +10 -0
- package/dist/validation/manifest.js +266 -0
- package/dist/validation/schema.js +768 -0
- package/dist/validation/schema.test.js +35 -0
- package/dist/watcher.js +79 -0
- package/package.json +1 -1
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const immutable_1 = __importDefault(require("immutable"));
|
|
31
|
+
const sync_request_1 = __importDefault(require("sync-request"));
|
|
32
|
+
const web3_eth_abi_1 = __importDefault(require("web3-eth-abi"));
|
|
33
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
34
|
+
const typesCodegen = __importStar(require("../../../codegen/types"));
|
|
35
|
+
const tsCodegen = __importStar(require("../../../codegen/typescript"));
|
|
36
|
+
const util = __importStar(require("../../../codegen/util"));
|
|
37
|
+
const doFixtureCodegen = fs_1.default.existsSync('./fixtures.yaml');
|
|
38
|
+
class AbiCodeGenerator {
|
|
39
|
+
abi;
|
|
40
|
+
constructor(abi) {
|
|
41
|
+
this.abi = abi;
|
|
42
|
+
this.abi = abi;
|
|
43
|
+
}
|
|
44
|
+
generateModuleImports() {
|
|
45
|
+
const imports = [
|
|
46
|
+
tsCodegen.moduleImports([
|
|
47
|
+
// Ethereum integration
|
|
48
|
+
'ethereum',
|
|
49
|
+
// Base classes
|
|
50
|
+
'JSONValue',
|
|
51
|
+
'TypedMap',
|
|
52
|
+
'Entity',
|
|
53
|
+
// AssemblyScript types
|
|
54
|
+
'Bytes',
|
|
55
|
+
'Address',
|
|
56
|
+
'BigInt',
|
|
57
|
+
], '@graphprotocol/graph-ts'),
|
|
58
|
+
];
|
|
59
|
+
if (doFixtureCodegen) {
|
|
60
|
+
imports.push(tsCodegen.moduleImports(['newMockEvent'], 'matchstick-as/assembly/index'));
|
|
61
|
+
}
|
|
62
|
+
return imports;
|
|
63
|
+
}
|
|
64
|
+
generateTypes() {
|
|
65
|
+
return [
|
|
66
|
+
...this._generateEventTypes(),
|
|
67
|
+
...this._generateSmartContractClass(),
|
|
68
|
+
...this._generateCallTypes(),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
_generateCallTypes() {
|
|
72
|
+
let callFunctions = util.disambiguateNames({
|
|
73
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
74
|
+
values: this.abi.callFunctions(),
|
|
75
|
+
getName: fn =>
|
|
76
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
77
|
+
fn.get('name') || (fn.get('type') === 'constructor' ? 'constructor' : 'default'),
|
|
78
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
79
|
+
setName: (fn, name) => fn.set('_alias', name),
|
|
80
|
+
});
|
|
81
|
+
callFunctions = callFunctions.map(fn => {
|
|
82
|
+
const fnAlias = fn.get('_alias');
|
|
83
|
+
const fnClassName = `${fnAlias.charAt(0).toUpperCase()}${fnAlias.slice(1)}Call`;
|
|
84
|
+
const tupleClasses = [];
|
|
85
|
+
// First, generate a class with the input getters
|
|
86
|
+
const inputsClassName = fnClassName + '__Inputs';
|
|
87
|
+
const inputsClass = tsCodegen.klass(inputsClassName, { export: true });
|
|
88
|
+
inputsClass.addMember(tsCodegen.klassMember('_call', fnClassName));
|
|
89
|
+
inputsClass.addMethod(tsCodegen.method(`constructor`, [tsCodegen.param(`call`, fnClassName)], null, `this._call = call`));
|
|
90
|
+
// Generate getters and classes for function inputs
|
|
91
|
+
util
|
|
92
|
+
.disambiguateNames({
|
|
93
|
+
values: fn.get('inputs', immutable_1.default.List()),
|
|
94
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
95
|
+
getName: (input, index) => input.get('name') || `value${index}`,
|
|
96
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
97
|
+
setName: (input, name) => input.set('name', name),
|
|
98
|
+
})
|
|
99
|
+
.forEach((input, index) => {
|
|
100
|
+
const callInput = this._generateInputOrOutput(input, index, fnClassName, `call`, `inputValues`);
|
|
101
|
+
inputsClass.addMethod(callInput.getter);
|
|
102
|
+
tupleClasses.push(...callInput.classes);
|
|
103
|
+
});
|
|
104
|
+
// Second, generate a class with the output getters
|
|
105
|
+
const outputsClassName = fnClassName + '__Outputs';
|
|
106
|
+
const outputsClass = tsCodegen.klass(outputsClassName, { export: true });
|
|
107
|
+
outputsClass.addMember(tsCodegen.klassMember('_call', fnClassName));
|
|
108
|
+
outputsClass.addMethod(tsCodegen.method(`constructor`, [tsCodegen.param(`call`, fnClassName)], null, `this._call = call`));
|
|
109
|
+
// Generate getters and classes for function outputs
|
|
110
|
+
util
|
|
111
|
+
.disambiguateNames({
|
|
112
|
+
values: fn.get('outputs', immutable_1.default.List()),
|
|
113
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
114
|
+
getName: (output, index) => output.get('name') || `value${index}`,
|
|
115
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
116
|
+
setName: (output, name) => output.set('name', name),
|
|
117
|
+
})
|
|
118
|
+
.forEach((output, index) => {
|
|
119
|
+
const callInput = this._generateInputOrOutput(output, index, fnClassName, `call`, `outputValues`);
|
|
120
|
+
outputsClass.addMethod(callInput.getter);
|
|
121
|
+
tupleClasses.push(...callInput.classes);
|
|
122
|
+
});
|
|
123
|
+
// Then, generate the event class itself
|
|
124
|
+
const klass = tsCodegen.klass(fnClassName, {
|
|
125
|
+
export: true,
|
|
126
|
+
extends: 'ethereum.Call',
|
|
127
|
+
});
|
|
128
|
+
klass.addMethod(tsCodegen.method(`get inputs`, [], tsCodegen.namedType(inputsClassName), `return new ${inputsClassName}(this)`));
|
|
129
|
+
klass.addMethod(tsCodegen.method(`get outputs`, [], tsCodegen.namedType(outputsClassName), `return new ${outputsClassName}(this)`));
|
|
130
|
+
return [klass, inputsClass, outputsClass, ...tupleClasses];
|
|
131
|
+
});
|
|
132
|
+
return callFunctions.reduce(
|
|
133
|
+
// flatten the array
|
|
134
|
+
(array, classes) => array.concat(classes), []);
|
|
135
|
+
}
|
|
136
|
+
_generateEventTypes() {
|
|
137
|
+
// Enumerate events with duplicate names
|
|
138
|
+
let events = util.disambiguateNames({
|
|
139
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
140
|
+
values: this.abi.data.filter(member => member.get('type') === 'event'),
|
|
141
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
142
|
+
getName: event => event.get('name'),
|
|
143
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
144
|
+
setName: (event, name) => event.set('_alias', name),
|
|
145
|
+
});
|
|
146
|
+
events = events.map(event => {
|
|
147
|
+
const eventClassName = event.get('_alias');
|
|
148
|
+
const tupleClasses = [];
|
|
149
|
+
// First, generate a class with the param getters
|
|
150
|
+
const paramsClassName = eventClassName + '__Params';
|
|
151
|
+
const paramsClass = tsCodegen.klass(paramsClassName, { export: true });
|
|
152
|
+
paramsClass.addMember(tsCodegen.klassMember('_event', eventClassName));
|
|
153
|
+
paramsClass.addMethod(tsCodegen.method(`constructor`, [tsCodegen.param(`event`, eventClassName)], null, `this._event = event`));
|
|
154
|
+
// Enumerate inputs with duplicate names
|
|
155
|
+
const inputs = util.disambiguateNames({
|
|
156
|
+
values: event.get('inputs'),
|
|
157
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
158
|
+
getName: (input, index) => input.get('name') || `param${index}`,
|
|
159
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
160
|
+
setName: (input, name) => input.set('name', name),
|
|
161
|
+
});
|
|
162
|
+
const namesAndTypes = [];
|
|
163
|
+
inputs.forEach((input, index) => {
|
|
164
|
+
// Generate getters and classes for event params
|
|
165
|
+
const paramObject = this._generateInputOrOutput(input, index, eventClassName, `event`, `parameters`);
|
|
166
|
+
paramsClass.addMethod(paramObject.getter);
|
|
167
|
+
// Fixture generation
|
|
168
|
+
if (doFixtureCodegen) {
|
|
169
|
+
let ethType = typesCodegen.ethereumTypeForAsc(String(paramObject.getter.returnType));
|
|
170
|
+
if (typeof ethType !== 'string' && (ethType.test('int256') || ethType.test('uint256'))) {
|
|
171
|
+
ethType = 'int32';
|
|
172
|
+
}
|
|
173
|
+
namesAndTypes.push({ name: paramObject.getter.name.slice(4), type: ethType });
|
|
174
|
+
}
|
|
175
|
+
tupleClasses.push(...paramObject.classes);
|
|
176
|
+
});
|
|
177
|
+
// Then, generate the event class itself
|
|
178
|
+
const klass = tsCodegen.klass(eventClassName, {
|
|
179
|
+
export: true,
|
|
180
|
+
extends: 'ethereum.Event',
|
|
181
|
+
});
|
|
182
|
+
klass.addMethod(tsCodegen.method(`get params`, [], tsCodegen.namedType(paramsClassName), `return new ${paramsClassName}(this)`));
|
|
183
|
+
// Fixture generation
|
|
184
|
+
if (doFixtureCodegen) {
|
|
185
|
+
const args = yaml_1.default.parse(fs_1.default.readFileSync('./fixtures.yaml', 'utf8'));
|
|
186
|
+
const blockNumber = args['blockNumber'];
|
|
187
|
+
const contractAddr = args['contractAddr'];
|
|
188
|
+
const topic0 = args['topic0'];
|
|
189
|
+
const apiKey = args['apiKey'];
|
|
190
|
+
const url = `https://api.etherscan.io/api?module=logs&action=getLogs&fromBlock=${blockNumber}&toBlock=${blockNumber}&address=${contractAddr}&${topic0}=topic0&apikey=${apiKey}`;
|
|
191
|
+
const resp = (0, sync_request_1.default)('GET', url);
|
|
192
|
+
const body = JSON.parse(resp.getBody('utf8'));
|
|
193
|
+
if (body.status === '0') {
|
|
194
|
+
throw new Error(body.result);
|
|
195
|
+
}
|
|
196
|
+
const res = web3_eth_abi_1.default
|
|
197
|
+
// @ts-expect-error decodeLog seems to exist on Web3EthAbi
|
|
198
|
+
.decodeLog(namesAndTypes, body.result[0].data, []);
|
|
199
|
+
let stmnts = '';
|
|
200
|
+
for (let i = 0; i < namesAndTypes.length; i++) {
|
|
201
|
+
let code = '"' + res[i] + '"';
|
|
202
|
+
if (namesAndTypes[i].type.toString() == 'address') {
|
|
203
|
+
code = `Address.fromString(${code})`;
|
|
204
|
+
}
|
|
205
|
+
stmnts = stmnts.concat(`event.parameters.push(new ethereum.EventParam("${namesAndTypes[i].name}", ${typesCodegen.ethereumFromAsc(code, namesAndTypes[i].type)}));`, `\n`);
|
|
206
|
+
}
|
|
207
|
+
klass.addMethod(tsCodegen.staticMethod(`mock${eventClassName}`, [], tsCodegen.namedType(eventClassName), `
|
|
208
|
+
let event = changetype<${eventClassName}>(newMockEvent());
|
|
209
|
+
${stmnts}
|
|
210
|
+
return event;
|
|
211
|
+
`));
|
|
212
|
+
}
|
|
213
|
+
return [klass, paramsClass, ...tupleClasses];
|
|
214
|
+
});
|
|
215
|
+
return events.reduce(
|
|
216
|
+
// flatten the array
|
|
217
|
+
(array, classes) => array.concat(classes), []);
|
|
218
|
+
}
|
|
219
|
+
_generateInputOrOutput(inputOrOutput, index, parentClass, parentType, parentField) {
|
|
220
|
+
// Get name and type of the param, adjusting for indexed params and missing names
|
|
221
|
+
let name = inputOrOutput.get('name');
|
|
222
|
+
const valueType = parentType === 'event' && inputOrOutput.get('indexed')
|
|
223
|
+
? this._indexedInputType(inputOrOutput.get('type'))
|
|
224
|
+
: inputOrOutput.get('type');
|
|
225
|
+
if (name === undefined || name === null || name === '') {
|
|
226
|
+
name = parentType === 'event' ? `param${index}` : `value${index}`;
|
|
227
|
+
}
|
|
228
|
+
// Generate getters and classes for the param (classes only created for Ethereum tuple types)
|
|
229
|
+
return util.containsTupleType(valueType)
|
|
230
|
+
? this._generateTupleType(inputOrOutput, index, parentClass, parentType, parentField)
|
|
231
|
+
: {
|
|
232
|
+
name: [],
|
|
233
|
+
getter: tsCodegen.method(`get ${name}`, [], typesCodegen.ascTypeForEthereum(valueType), `
|
|
234
|
+
return ${typesCodegen.ethereumToAsc(util.isTupleType(parentType)
|
|
235
|
+
? `this[${index}]`
|
|
236
|
+
: `this._${parentType}.${parentField}[${index}].value`, valueType)}
|
|
237
|
+
`),
|
|
238
|
+
classes: [],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
_tupleTypeName(inputOrOutput, index, parentClass, parentType) {
|
|
242
|
+
return this._generateTupleType(inputOrOutput, index, parentClass, parentType, '').name;
|
|
243
|
+
}
|
|
244
|
+
_generateTupleType(inputOrOutput, index, parentClass, parentType, parentField) {
|
|
245
|
+
const type = inputOrOutput.get('type');
|
|
246
|
+
let name = inputOrOutput.get('name');
|
|
247
|
+
if (name === undefined || name === null || name === '') {
|
|
248
|
+
name = parentType === 'event' ? `param${index}` : `value${index}`;
|
|
249
|
+
}
|
|
250
|
+
const tupleIdentifier = parentClass + tsCodegen.namedType(name).capitalize();
|
|
251
|
+
const tupleClassName = tupleIdentifier + 'Struct';
|
|
252
|
+
let tupleClasses = [];
|
|
253
|
+
const isTupleType = util.isTupleType(type);
|
|
254
|
+
const returnValue = typesCodegen.ethereumToAsc(parentType === 'tuple'
|
|
255
|
+
? `this[${index}]`
|
|
256
|
+
: `this._${parentType}.${parentField}[${index}].value`, type, tupleClassName);
|
|
257
|
+
// Generate getter for parent class
|
|
258
|
+
const tupleGetter = tsCodegen.method(`get ${name}`, [], util.isTupleMatrixType(type)
|
|
259
|
+
? `Array<Array<${tupleClassName}>>`
|
|
260
|
+
: util.isTupleArrayType(type)
|
|
261
|
+
? `Array<${tupleClassName}>`
|
|
262
|
+
: tupleClassName, `
|
|
263
|
+
return ${isTupleType ? `changetype<${tupleClassName}>(${returnValue})` : String(returnValue)}
|
|
264
|
+
`);
|
|
265
|
+
// Generate tuple class
|
|
266
|
+
const baseTupleClass = tsCodegen.klass(tupleClassName, {
|
|
267
|
+
export: true,
|
|
268
|
+
extends: 'ethereum.Tuple',
|
|
269
|
+
});
|
|
270
|
+
// Add param getters to tuple class and generate classes for each tuple parameter
|
|
271
|
+
inputOrOutput.get('components').forEach((component, index) => {
|
|
272
|
+
const paramObject = this._generateInputOrOutput(component, index, tupleIdentifier, `tuple`);
|
|
273
|
+
baseTupleClass.addMethod(paramObject.getter);
|
|
274
|
+
tupleClasses = tupleClasses.concat(paramObject.classes);
|
|
275
|
+
});
|
|
276
|
+
// Combine all tuple classes generated
|
|
277
|
+
tupleClasses.unshift(baseTupleClass);
|
|
278
|
+
return {
|
|
279
|
+
name: tupleClassName,
|
|
280
|
+
getter: tupleGetter,
|
|
281
|
+
classes: tupleClasses,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
_generateSmartContractClass() {
|
|
285
|
+
const klass = tsCodegen.klass(this.abi.name, {
|
|
286
|
+
export: true,
|
|
287
|
+
extends: 'ethereum.SmartContract',
|
|
288
|
+
});
|
|
289
|
+
let types = immutable_1.default.List();
|
|
290
|
+
klass.addMethod(tsCodegen.staticMethod('bind',
|
|
291
|
+
// TODO: add support for iterable staticMethod params
|
|
292
|
+
immutable_1.default.List([
|
|
293
|
+
tsCodegen.param('address', typesCodegen.ascTypeForEthereum('address')),
|
|
294
|
+
]), tsCodegen.namedType(this.abi.name), `
|
|
295
|
+
return new ${this.abi.name}('${this.abi.name}', address);
|
|
296
|
+
`));
|
|
297
|
+
// Get view/pure functions from the contract
|
|
298
|
+
let functions = this.callableFunctions();
|
|
299
|
+
// Disambiguate functions with duplicate names
|
|
300
|
+
functions = util.disambiguateNames({
|
|
301
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
302
|
+
values: functions,
|
|
303
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
304
|
+
getName: fn => fn.get('name'),
|
|
305
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
306
|
+
setName: (fn, name) => fn.set('_alias', name),
|
|
307
|
+
});
|
|
308
|
+
functions.forEach(member => {
|
|
309
|
+
const fnName = member.get('name');
|
|
310
|
+
const fnAlias = member.get('_alias');
|
|
311
|
+
const fnSignature = this.abi.functionSignature(member);
|
|
312
|
+
// Generate a type for the result of calling the function
|
|
313
|
+
let returnType = undefined;
|
|
314
|
+
let simpleReturnType = true;
|
|
315
|
+
const tupleResultParentType = this.abi.name + '__' + fnAlias + 'Result';
|
|
316
|
+
// Disambiguate outputs with duplicate names
|
|
317
|
+
const outputs = util.disambiguateNames({
|
|
318
|
+
values: member.get('outputs', immutable_1.default.List()),
|
|
319
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
320
|
+
getName: (input, index) => input.get('name') || `value${index}`,
|
|
321
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
322
|
+
setName: (input, name) => input.set('name', name),
|
|
323
|
+
});
|
|
324
|
+
if (member.get('outputs', immutable_1.default.List()).size > 1) {
|
|
325
|
+
simpleReturnType = false;
|
|
326
|
+
// Create a type dedicated to holding the return values
|
|
327
|
+
returnType = tsCodegen.klass(this.abi.name + '__' + fnAlias + 'Result', {
|
|
328
|
+
export: true,
|
|
329
|
+
});
|
|
330
|
+
// Add a constructor to this type
|
|
331
|
+
returnType.addMethod(tsCodegen.method('constructor', outputs.map((output, index) => tsCodegen.param(`value${index}`, this._getTupleParamType(output, index, tupleResultParentType))), null, outputs
|
|
332
|
+
.map((_output, index) => `this.value${index} = value${index}`)
|
|
333
|
+
.join('\n')));
|
|
334
|
+
// Add a `toMap(): TypedMap<string,ethereum.Value>` function to the return type
|
|
335
|
+
returnType.addMethod(tsCodegen.method('toMap', [], tsCodegen.namedType('TypedMap<string,ethereum.Value>'), `
|
|
336
|
+
let map = new TypedMap<string,ethereum.Value>();
|
|
337
|
+
${outputs
|
|
338
|
+
.map((output, index) => `map.set('value${index}', ${typesCodegen.ethereumFromAsc(`this.value${index}`, output.get('type'))})`)
|
|
339
|
+
.join(';')}
|
|
340
|
+
return map;
|
|
341
|
+
`));
|
|
342
|
+
// Add value0, value1 etc. members to the type
|
|
343
|
+
outputs
|
|
344
|
+
.map((output, index) => tsCodegen.klassMember(`value${index}`, this._getTupleParamType(output, index, tupleResultParentType)))
|
|
345
|
+
.forEach((member) => returnType.addMember(member));
|
|
346
|
+
// Add getters to the type
|
|
347
|
+
outputs
|
|
348
|
+
.map((output, index) => !!output.get('name') &&
|
|
349
|
+
tsCodegen.method(`get${output.get('name')[0].toUpperCase()}${output.get('name').slice(1)}`, [], this._getTupleParamType(output, index, tupleResultParentType), `return this.value${index};`))
|
|
350
|
+
.forEach((method) => !!method && returnType.addMethod(method));
|
|
351
|
+
// Create types for Tuple outputs
|
|
352
|
+
outputs.forEach((output, index) => {
|
|
353
|
+
if (util.containsTupleType(output.get('type'))) {
|
|
354
|
+
types = types.concat(this._generateTupleType(output, index, tupleResultParentType, 'function', this.abi.name).classes);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// Add the type to the types we'll create
|
|
358
|
+
types = types.push(returnType);
|
|
359
|
+
returnType = tsCodegen.namedType(returnType.name);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
const type = outputs.get(0).get('type');
|
|
363
|
+
if (util.containsTupleType(type)) {
|
|
364
|
+
// Add the Tuple type to the types we'll create
|
|
365
|
+
const tuple = this._generateTupleType(outputs.get(0), 0, tupleResultParentType, 'function', this.abi.name);
|
|
366
|
+
types = types.concat(tuple.classes);
|
|
367
|
+
returnType = util.isTupleType(type)
|
|
368
|
+
? tsCodegen.namedType(tuple.name)
|
|
369
|
+
: `Array<${tsCodegen.namedType(tuple.name)}>`;
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
returnType = tsCodegen.namedType(typesCodegen.ascTypeForEthereum(type));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Disambiguate inputs with duplicate names
|
|
376
|
+
const inputs = util.disambiguateNames({
|
|
377
|
+
values: member.get('inputs', immutable_1.default.List()),
|
|
378
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
379
|
+
getName: (input, index) => input.get('name') || `param${index}`,
|
|
380
|
+
// @ts-expect-error improve typings of disambiguateNames to handle iterables
|
|
381
|
+
setName: (input, name) => input.set('name', name),
|
|
382
|
+
});
|
|
383
|
+
// Generate a type prefix to identify the Tuple inputs to a function
|
|
384
|
+
const tupleInputParentType = this.abi.name + '__' + fnAlias + 'Input';
|
|
385
|
+
// Create types for Tuple inputs
|
|
386
|
+
inputs.forEach((input, index) => {
|
|
387
|
+
if (util.containsTupleType(input.get('type'))) {
|
|
388
|
+
types = types.concat(this._generateTupleType(input, index, tupleInputParentType, 'function', this.abi.name)
|
|
389
|
+
.classes);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
// Generate and add a method that implements calling the function on
|
|
393
|
+
// the smart contract
|
|
394
|
+
const params = inputs.map((input, index) => tsCodegen.param(input.get('name'), this._getTupleParamType(input, index, tupleInputParentType)));
|
|
395
|
+
const superInputs = `
|
|
396
|
+
'${fnName}',
|
|
397
|
+
'${fnSignature}',
|
|
398
|
+
[${inputs.size > 0
|
|
399
|
+
? inputs
|
|
400
|
+
.map((input) => typesCodegen.ethereumFromAsc(input.get('name'), input.get('type')))
|
|
401
|
+
.map((coercion) => coercion.toString())
|
|
402
|
+
.join(', ')
|
|
403
|
+
: ''}]`;
|
|
404
|
+
const methodCallBody = (isTry) => {
|
|
405
|
+
const methodBody = `
|
|
406
|
+
${isTry
|
|
407
|
+
? `
|
|
408
|
+
let result = super.tryCall(${superInputs})
|
|
409
|
+
if (result.reverted) {
|
|
410
|
+
return new ethereum.CallResult()
|
|
411
|
+
}
|
|
412
|
+
let value = result.value
|
|
413
|
+
return ethereum.CallResult.fromValue(`
|
|
414
|
+
: `
|
|
415
|
+
let result = super.call(${superInputs})
|
|
416
|
+
|
|
417
|
+
return (`}`;
|
|
418
|
+
const returnVal = simpleReturnType
|
|
419
|
+
? typesCodegen.ethereumToAsc(isTry ? 'value[0]' : 'result[0]', outputs.get(0).get('type'), util.isTupleArrayType(outputs.get(0).get('type'))
|
|
420
|
+
? this._tupleTypeName(outputs.get(0), 0, tupleResultParentType, this.abi.name)
|
|
421
|
+
: '')
|
|
422
|
+
: `new ${returnType.name}(
|
|
423
|
+
${outputs
|
|
424
|
+
.map((output, index) => {
|
|
425
|
+
const val = typesCodegen.ethereumToAsc(isTry ? `value[${index}]` : `result[${index}]`, output.get('type'), util.isTupleArrayType(output.get('type'))
|
|
426
|
+
? this._tupleTypeName(output, index, tupleResultParentType, this.abi.name)
|
|
427
|
+
: '');
|
|
428
|
+
return util.isTupleType(output.get('type'))
|
|
429
|
+
? `changetype<${this._tupleTypeName(output, index, tupleResultParentType, this.abi.name)}>(${val})`
|
|
430
|
+
: val;
|
|
431
|
+
})
|
|
432
|
+
.join(', ')}
|
|
433
|
+
)`;
|
|
434
|
+
const isTuple = util.isTupleType(outputs.get(0).get('type'));
|
|
435
|
+
return `${methodBody} ${isTuple ? `changetype<${returnType}>(${returnVal})` : returnVal})`;
|
|
436
|
+
};
|
|
437
|
+
// Generate method with an without `try_`.
|
|
438
|
+
klass.addMethod(tsCodegen.method(fnAlias, params, returnType, methodCallBody(false)));
|
|
439
|
+
klass.addMethod(tsCodegen.method('try_' + fnAlias, params, 'ethereum.CallResult<' + returnType + '>', methodCallBody(true)));
|
|
440
|
+
});
|
|
441
|
+
return [...types, klass];
|
|
442
|
+
}
|
|
443
|
+
_getTupleParamType(inputOrOutput, index, tupleParentType) {
|
|
444
|
+
const type = inputOrOutput.get('type');
|
|
445
|
+
return util.isTupleType(type)
|
|
446
|
+
? this._tupleTypeName(inputOrOutput, index, tupleParentType, this.abi.name)
|
|
447
|
+
: util.isTupleMatrixType(type)
|
|
448
|
+
? `Array<Array<${this._tupleTypeName(inputOrOutput, index, tupleParentType, this.abi.name)}>>`
|
|
449
|
+
: util.isTupleArrayType(type)
|
|
450
|
+
? `Array<${this._tupleTypeName(inputOrOutput, index, tupleParentType, this.abi.name)}>`
|
|
451
|
+
: typesCodegen.ascTypeForEthereum(type);
|
|
452
|
+
}
|
|
453
|
+
_indexedInputType(inputType) {
|
|
454
|
+
// strings, bytes and arrays are encoded and hashed to a bytes32 value
|
|
455
|
+
if (inputType === 'string' ||
|
|
456
|
+
inputType === 'bytes' ||
|
|
457
|
+
inputType === 'tuple' ||
|
|
458
|
+
// the following matches arrays of the forms `uint256[]` and `uint256[12356789]`;
|
|
459
|
+
// the value type name doesn't matter here, just that the type name ends with
|
|
460
|
+
// brackets and, optionally, a number inside the brackets
|
|
461
|
+
inputType.match(/\[[0-9]*\]$/g)) {
|
|
462
|
+
return 'bytes32';
|
|
463
|
+
}
|
|
464
|
+
return inputType;
|
|
465
|
+
}
|
|
466
|
+
callableFunctions() {
|
|
467
|
+
const allowedMutability = ['view', 'pure', 'nonpayable', 'constant'];
|
|
468
|
+
return this.abi.data.filter(member => member.get('type') === 'function' &&
|
|
469
|
+
member.get('outputs', immutable_1.default.List()).size !== 0 &&
|
|
470
|
+
(allowedMutability.includes(member.get('stateMutability')) ||
|
|
471
|
+
(member.get('stateMutability') === undefined && !member.get('payable', false))));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
exports.default = AbiCodeGenerator;
|