@taqueria/plugin-contract-types 0.0.0-pr-91-a8fa7172

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.
Files changed (51) hide show
  1. package/Readme.md +212 -0
  2. package/example/contracts/example-contract-1.tz +1409 -0
  3. package/example/contracts/example-contract-2.tz +798 -0
  4. package/example/contracts/example-contract-3.json +7080 -0
  5. package/example/contracts/example-contract-4.tz +1374 -0
  6. package/example/contracts/example-contract-5.tz +1424 -0
  7. package/example/contracts/example-contract-6.tz +675 -0
  8. package/example/contracts/example-contract-7.tz +384 -0
  9. package/example/contracts/example-contract-8.tz +28 -0
  10. package/example/contracts/example-contract-9.tz +1010 -0
  11. package/example/example-usage-type-utilities.ts +35 -0
  12. package/example/example-usage.ts +410 -0
  13. package/example/types-file/example-contract-1.code.ts +6 -0
  14. package/example/types-file/example-contract-1.types.ts +87 -0
  15. package/example/types-file/example-contract-2.code.ts +6 -0
  16. package/example/types-file/example-contract-2.types.ts +122 -0
  17. package/example/types-file/example-contract-4.code.ts +6 -0
  18. package/example/types-file/example-contract-4.types.ts +97 -0
  19. package/example/types-file/example-contract-5.code.ts +6 -0
  20. package/example/types-file/example-contract-5.types.ts +173 -0
  21. package/example/types-file/example-contract-6.code.ts +6 -0
  22. package/example/types-file/example-contract-6.types.ts +122 -0
  23. package/example/types-file/example-contract-7.code.ts +6 -0
  24. package/example/types-file/example-contract-7.types.ts +78 -0
  25. package/example/types-file/example-contract-8.code.ts +6 -0
  26. package/example/types-file/example-contract-8.types.ts +86 -0
  27. package/example/types-file/example-contract-9.code.ts +6 -0
  28. package/example/types-file/example-contract-9.types.ts +29 -0
  29. package/example/types-file/type-aliases.ts +81 -0
  30. package/example/types-file/type-utils.ts +36 -0
  31. package/index.js +969 -0
  32. package/index.js.map +1 -0
  33. package/index.ts +32 -0
  34. package/package.json +55 -0
  35. package/run.ts +3 -0
  36. package/src/cli-process.ts +111 -0
  37. package/src/cli.ts +34 -0
  38. package/src/generator/common.ts +21 -0
  39. package/src/generator/contract-name.ts +6 -0
  40. package/src/generator/contract-parser.ts +358 -0
  41. package/src/generator/process.ts +66 -0
  42. package/src/generator/schema-output.ts +54 -0
  43. package/src/generator/typescript-output.ts +239 -0
  44. package/src/taquito-contract-type-generator.ts +4 -0
  45. package/src/type-aliases-file-content.ts +83 -0
  46. package/src/type-aliases.ts +80 -0
  47. package/src/type-utils-file-content.ts +38 -0
  48. package/src/type-utils.ts +35 -0
  49. package/tasks.ts +127 -0
  50. package/test/generator.spec.ts +69 -0
  51. package/tsconfig.json +13 -0
@@ -0,0 +1,358 @@
1
+ import * as M from '@taquito/michel-codec';
2
+ import { assertExhaustive, GenerateApiError, reduceFlatMap } from './common';
3
+
4
+ export type TypedStorage = {
5
+ storage: {
6
+ kind: 'object';
7
+ raw: M.MichelsonType;
8
+ fields: TypedVar[];
9
+ };
10
+ };
11
+ export type TypedParameter = {
12
+ methods: TypedMethod[];
13
+ };
14
+ export type TypedMethod = {
15
+ name: string;
16
+ args: TypedVar[];
17
+ };
18
+ export type TypedVar = {
19
+ name?: string;
20
+ type: TypedType;
21
+ };
22
+ export type TypedType = {
23
+ raw: M.MichelsonType;
24
+ optional?: boolean;
25
+ } & (
26
+ {
27
+ kind: 'unit';
28
+ } | {
29
+ kind: 'never';
30
+ } | {
31
+ kind: 'unknown';
32
+ } | {
33
+ kind: 'value';
34
+ value: string;
35
+ typescriptType: 'string' | 'boolean' | 'number' | 'Date';
36
+ } | {
37
+ kind: 'union';
38
+ union: TypedVar[];
39
+ } | {
40
+ kind: 'object';
41
+ fields: TypedVar[];
42
+ } | {
43
+ kind: 'array';
44
+ array: { item: TypedType };
45
+ } | {
46
+ kind: 'map';
47
+ map: { key: TypedType, value: TypedType, isBigMap: boolean };
48
+ }
49
+ );
50
+
51
+ const toDebugSource = (node: M.MichelsonType) => {
52
+ return JSON.stringify(node);
53
+ };
54
+
55
+ export const parseContractStorage = (storage: M.MichelsonContractStorage): TypedStorage => {
56
+ const fields = storage.args
57
+ .map(x => visitVar(x))
58
+ .reduce(reduceFlatMap, []);
59
+
60
+ const fieldsSimple = fields.length === 1 && !fields[0].name && fields[0].type.kind === 'object' ? fields[0].type.fields : fields;
61
+
62
+ return {
63
+ storage: {
64
+ kind: `object`,
65
+ raw: storage as unknown as M.MichelsonType,
66
+ fields: fieldsSimple,
67
+ },
68
+ };
69
+ };
70
+
71
+ export const parseContractParameter = (parameter: M.MichelsonContractParameter): TypedParameter => {
72
+ return {
73
+ methods: parameter.args
74
+ .map(x => visitContractParameterEndpoint(x as MMethod))
75
+ .reduce(reduceFlatMap, []),
76
+ };
77
+ };
78
+
79
+
80
+ type MMethod = M.MichelsonTypeOr<[M.MichelsonType, M.MichelsonType]>;
81
+ const visitContractParameterEndpoint = (node: MMethod): TypedMethod[] => {
82
+
83
+ // console.log('visitContractParameterEndpoint', { node });
84
+
85
+ // Sub endpoints (i.e. admin endpoints that are imported)
86
+ if (node.prim === `or`) {
87
+ return node.args.map(x => visitContractParameterEndpoint(x as MMethod)).reduce(reduceFlatMap, []);
88
+ }
89
+
90
+ // Sub endpoints as a list with a single or (i.e. admin endpoints that are imported)
91
+ if (node.prim === `list` && node.args.length as number === 1 && (node.args[0] as MMethod)?.prim === `or`) {
92
+ return node.args.map(x => visitContractParameterEndpoint(x as MMethod)).reduce(reduceFlatMap, []);
93
+ }
94
+
95
+ const nameRaw = node.annots?.[0];
96
+ const name = nameRaw?.startsWith('%') ? nameRaw.substr(1) : null;
97
+
98
+ if (!name) {
99
+ console.warn(`Unknown method: ${node.prim as string}`, { node, args: node.args });
100
+ return [];
101
+ }
102
+
103
+ const nodeType = visitType(node, { ignorePairName: node.prim === 'pair' });
104
+
105
+ // Method args are usually objects
106
+ if (nodeType.kind === 'object') {
107
+ return [{ name, args: nodeType.fields }];
108
+ }
109
+
110
+ // Simple methods can have a single unnamed argument
111
+ return [{
112
+ name,
113
+ args: [{ type: nodeType }],
114
+ }];
115
+ };
116
+
117
+ // type PrimOf<T extends M.MichelsonType> = T extends { prim: infer U } ? U : never;
118
+ // type WithPrim<T extends M.MichelsonType, P extends PrimOf<T>> = T extends { prim: P } ? T : never;
119
+ // const isPrimType = <TPrim extends PrimOf<M.MichelsonType>>(node: undefined | null | M.MichelsonType, prim: TPrim): node is WithPrim<M.MichelsonType, TPrim> => {
120
+ // return (node && 'prim' in node && node.prim === prim) || false;
121
+ // };
122
+
123
+ type MVarArgs = M.MichelsonType;
124
+ const visitVar = (node: MVarArgs): TypedVar[] => {
125
+ const name = `annots` in node && node.annots?.length === 1 ? node.annots[0].substr(1) : undefined;
126
+ const type = visitType(node);
127
+
128
+ return [{
129
+ name,
130
+ type,
131
+ }];
132
+ };
133
+
134
+ type MType = M.MichelsonType;
135
+ const visitType = (node: MType, options?: { ignorePairName?: boolean }): TypedType => {
136
+ // console.log('visitType', { node });
137
+ // const debug_source = toDebugSource(node);
138
+
139
+ // if (typeof node === `string`) {
140
+ // return { kind: `value`, raw: node, value: node, typescriptType: `string` };
141
+ // }
142
+
143
+ if (!(`prim` in node)) {
144
+ // Unknown
145
+ console.error(`visitType no prim`, { node });
146
+ return { kind: `unknown`, raw: node };
147
+ }
148
+
149
+ // Union
150
+ if (node.prim === `or`) {
151
+ const unionVars = node.args.map(x => visitVar(x)).reduce(reduceFlatMap, []).map(x => x);
152
+
153
+ // Flatten with child unions
154
+ const union = unionVars.map(x => !x.name && x.type.kind === 'union' ? x.type.union : [x]).reduce(reduceFlatMap, []);
155
+ // const union = unionVars.map(x=>x.type);
156
+
157
+ // const union = unionVars.map(x => x.type);
158
+
159
+ // Flatten with child unions
160
+
161
+ // const rightSide = union[1];
162
+ // if (rightSide.kind === `union`) {
163
+ // union.pop();
164
+ // union.push(...rightSide.union);
165
+ // }
166
+
167
+ if (union.some(x => !x)) {
168
+ throw new GenerateApiError(`or: Some fields are null`, { node });
169
+ }
170
+ return {
171
+ kind: `union`,
172
+ raw: node,
173
+ union,
174
+ };
175
+ }
176
+
177
+ // Intersect
178
+ if (node.prim === `pair`) {
179
+ const fields = node.args.map(x => visitVar(x)).reduce(reduceFlatMap, []);
180
+ if (fields.some(x => !x)) {
181
+ throw new GenerateApiError(`pair: Some fields are null`, { node, args: node.args, fields });
182
+ }
183
+ if (fields.length !== 2) {
184
+ throw new GenerateApiError(`pair: Expected 2 items`, { node, length: fields.length, fields });
185
+ }
186
+
187
+ // Flatten with unnamed child pairs
188
+ const fieldsFlat = fields.map(x => (!x.name || options?.ignorePairName) && x.type.kind === 'object' ? x.type.fields : [x]).reduce(reduceFlatMap, []);
189
+
190
+ return {
191
+ kind: `object`,
192
+ raw: node,
193
+ fields: fieldsFlat,
194
+ };
195
+ }
196
+
197
+ // list
198
+ if (node.prim === `list`
199
+ || node.prim === `set`
200
+ ) {
201
+ if (node.args.length !== 1) {
202
+ throw new GenerateApiError(`list does not have 1 arg`, { node, args: node.args });
203
+ }
204
+
205
+ const arrayItem = visitType(node.args[0]);
206
+ if (!arrayItem) {
207
+ throw new GenerateApiError(`arrayItem are null`, { node, args: node.args, arrayItem });
208
+ }
209
+ return {
210
+ kind: `array`,
211
+ raw: node,
212
+ array: { item: arrayItem },
213
+ };
214
+ }
215
+
216
+ // map
217
+ if (node.prim === `map`
218
+ || node.prim === `big_map`
219
+ ) {
220
+ if (node.args.length !== 2) {
221
+ throw new GenerateApiError(`map does not have 2 args`, { node, args: node.args });
222
+ }
223
+
224
+ const mapKey = visitType(node.args[0]);
225
+ const mapValue = visitType(node.args[1]);
226
+ if (!mapKey || !mapValue) {
227
+ throw new GenerateApiError(`map is missing key or value`, { node, args: node.args, mapKey, mapValue });
228
+ }
229
+ return {
230
+ kind: `map`,
231
+ raw: node,
232
+ map: {
233
+ key: mapKey,
234
+ value: mapValue,
235
+ isBigMap: node.prim === `big_map`,
236
+ },
237
+ };
238
+ }
239
+
240
+ // option
241
+ if (node.prim === `option`) {
242
+ return {
243
+ ...visitType(node.args[0]),
244
+ optional: true,
245
+ };
246
+ }
247
+
248
+ // boolean
249
+ if (node.prim === `bool`) {
250
+ return {
251
+ kind: `value`,
252
+ raw: node,
253
+ value: node.prim,
254
+ typescriptType: `boolean`,
255
+ };
256
+ }
257
+
258
+ // numbers
259
+ if (node.prim === `nat`
260
+ || node.prim === `int`
261
+ || node.prim === `mutez`
262
+ ) {
263
+ return {
264
+ kind: `value`,
265
+ raw: node,
266
+ value: node.prim,
267
+ typescriptType: `number`,
268
+ };
269
+ }
270
+
271
+ // Date
272
+ if (node.prim === `timestamp`
273
+
274
+ ) {
275
+ return {
276
+ kind: `value`,
277
+ raw: node,
278
+ value: node.prim,
279
+ typescriptType: `Date`,
280
+ };
281
+ }
282
+
283
+ // strings
284
+ if (node.prim === `address`
285
+ || node.prim === `key`
286
+ || node.prim === `key_hash`
287
+ || node.prim === `chain_id`
288
+ || node.prim === `string`
289
+ || node.prim === `signature`
290
+ || node.prim === `ticket`
291
+ || node.prim === `bls12_381_fr`
292
+ || node.prim === `bls12_381_g1`
293
+ || node.prim === `bls12_381_g2`
294
+ || node.prim === `sapling_state`
295
+ || node.prim === `sapling_transaction`
296
+ || node.prim === `contract`
297
+ ) {
298
+ return {
299
+ kind: `value`,
300
+ raw: node,
301
+ value: node.prim,
302
+ typescriptType: `string`,
303
+ };
304
+ }
305
+
306
+
307
+ // void
308
+ if (node.prim === `unit`) {
309
+ return {
310
+ kind: `unit`,
311
+ raw: node,
312
+ };
313
+ }
314
+
315
+ // bytes?
316
+ if (node.prim === `bytes`) {
317
+ return {
318
+ kind: `value`,
319
+ raw: node,
320
+ value: node.prim,
321
+ typescriptType: `string`,
322
+ };
323
+ }
324
+
325
+ // misc?
326
+ if (node.prim === `lambda`
327
+ || node.prim === `operation`
328
+ ) {
329
+ return {
330
+ kind: `value`,
331
+ raw: node,
332
+ value: node.prim,
333
+ typescriptType: `string`,
334
+ };
335
+ }
336
+
337
+ // chest
338
+ if(node.prim === 'chest'){
339
+ throw new Error('Not Implemented: chest');
340
+ }
341
+ if(node.prim === 'chest_key'){
342
+ throw new Error('Not Implemented: chest_key');
343
+ }
344
+
345
+ // never
346
+ if (node.prim === `never`
347
+ ) {
348
+ return {
349
+ kind: `never`,
350
+ raw: node,
351
+ };
352
+ }
353
+
354
+
355
+ // Unknown
356
+ assertExhaustive(node, `Unknown type`);
357
+ throw new GenerateApiError(`Unknown type`, { node });
358
+ };
@@ -0,0 +1,66 @@
1
+ import * as M from '@taquito/michel-codec';
2
+ import { GenerateApiError } from './common';
3
+ import { parseContractStorage, parseContractParameter } from './contract-parser';
4
+ import { SchemaOutput, toSchema } from './schema-output';
5
+ import { TypescriptCodeOutput, toTypescriptCode, TypeAliasData, TypeUtilsData } from './typescript-output';
6
+
7
+ const parseContractWithMinimalProtocolLevel = (contractScript: string, format: 'tz' | 'json', contractLevelIndex: number): { contract: M.MichelsonContract, protocol: { name: string, key: string } } => {
8
+ const contractLevels = [
9
+ { name: 'PsDELPH1', key: M.Protocol.PsDELPH1 },
10
+ { name: 'PtEdo2Zk', key: M.Protocol.PtEdo2Zk },
11
+ { name: 'PsFLorena', key: M.Protocol.PsFLorena },
12
+ ];
13
+
14
+ const protocol = contractLevels[contractLevelIndex];
15
+ if (!protocol) {
16
+ throw new GenerateApiError(`Could not parse contract script`, contractScript);
17
+ }
18
+
19
+ const p = new M.Parser({ protocol: protocol.key });
20
+
21
+ try {
22
+ const contract = (format === 'tz' ? p.parseScript(contractScript) : p.parseJSON(JSON.parse(contractScript))) as M.MichelsonContract;
23
+ if (contract) {
24
+ return {
25
+ contract,
26
+ protocol,
27
+ };
28
+ }
29
+ } catch {
30
+ // Ignore parse errors
31
+ }
32
+
33
+ // Try again with next level
34
+ return parseContractWithMinimalProtocolLevel(contractScript, format, contractLevelIndex + 1);
35
+ };
36
+
37
+ export const generateContractTypesFromMichelsonCode = (contractScript: string, contractName: string, format: 'tz' | 'json', typeAliasData: TypeAliasData, typeUtilsData: TypeUtilsData): {
38
+ schema: SchemaOutput;
39
+ typescriptCodeOutput: TypescriptCodeOutput;
40
+ parsedContract: M.MichelsonContract;
41
+ minimalProtocol: string;
42
+ } => {
43
+
44
+ const p = new M.Parser({ protocol: M.Protocol.PsFLorena });
45
+
46
+ const { contract, protocol } = parseContractWithMinimalProtocolLevel(contractScript, format, 0);
47
+
48
+ const contractStorage = contract.find(x => x.prim === `storage`) as undefined | M.MichelsonContractStorage;
49
+ const contractParameter = contract.find(x => x.prim === `parameter`) as undefined | M.MichelsonContractParameter;
50
+
51
+ const storageResult = contractStorage && parseContractStorage(contractStorage);
52
+ const storage = storageResult ?? { storage: { kind: `object`, raw: { prim: `never` } as M.MichelsonType, fields: [] } };
53
+
54
+ const parameterResult = contractParameter && parseContractParameter(contractParameter);
55
+ const methods = parameterResult?.methods ?? [];
56
+ const schemaOutput = toSchema(methods);
57
+
58
+ const typescriptCode = toTypescriptCode(storage, methods, contractName, contract, protocol, typeAliasData, typeUtilsData);
59
+
60
+ return {
61
+ schema: schemaOutput,
62
+ typescriptCodeOutput: typescriptCode,
63
+ parsedContract: contract,
64
+ minimalProtocol: protocol.key,
65
+ };
66
+ };
@@ -0,0 +1,54 @@
1
+ import { GenerateApiError } from './common';
2
+ import { TypedMethod, TypedVar, TypedType } from './contract-parser';
3
+
4
+ type SchemaObjectType = { [name: string]: SchemaType };
5
+ type SchemaType = string | SchemaType[] | SchemaObjectType;
6
+ type SchemaMethods = {
7
+ [name: string]: {
8
+ params: SchemaType;
9
+ };
10
+ };
11
+ export type SchemaOutput = {
12
+ schemaMethods: SchemaMethods;
13
+ };
14
+
15
+ export const toSchema = (methods: TypedMethod[]): SchemaOutput => {
16
+
17
+ const getSchemaObjectType = (vars: TypedVar[]) => {
18
+ // console.log('getSchemaObjectType', { vars });
19
+
20
+ if (vars.some(x => !x)) {
21
+ throw new GenerateApiError(`getSchemaObjectType has null vars`, { vars });
22
+ }
23
+
24
+ return vars.reduce((out, x, i) => {
25
+ out[x.name ?? i] = getSchemaType(x.type);
26
+ return out;
27
+ }, {} as SchemaObjectType);
28
+ };
29
+
30
+ const getSchemaType = (t: TypedType): SchemaType => {
31
+ // console.log('getSchemaType', { t });
32
+
33
+ return (t.kind === `value` && t.value ? t.value : null)
34
+ ?? (t.kind === `array` && t.array ? [getSchemaType(t.array.item)] : null)
35
+ ?? (t.kind === `map` && t.map ? [`map`, getSchemaType(t.map.key), getSchemaType(t.map.value)] : null)
36
+ ?? (t.kind === `object` && t.fields ? getSchemaObjectType(t.fields) : null)
37
+ ?? (t.kind === `unit` ? `unit` : null)
38
+ ?? (t.kind === `never` ? `never` : null)
39
+ ?? `${t.raw as unknown as string}`;
40
+ };
41
+
42
+ const schemaMethods = methods.reduce((out, x) => {
43
+ // console.log('schemaMethods', { x });
44
+
45
+ out[x.name] = {
46
+ params: x.args.length === 1 && !x.args[0].name ? getSchemaType(x.args[0].type) : getSchemaObjectType(x.args ?? []),
47
+ };
48
+ return out;
49
+ }, {} as SchemaMethods);
50
+
51
+ return {
52
+ schemaMethods,
53
+ };
54
+ };