@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.
- package/Readme.md +212 -0
- package/example/contracts/example-contract-1.tz +1409 -0
- package/example/contracts/example-contract-2.tz +798 -0
- package/example/contracts/example-contract-3.json +7080 -0
- package/example/contracts/example-contract-4.tz +1374 -0
- package/example/contracts/example-contract-5.tz +1424 -0
- package/example/contracts/example-contract-6.tz +675 -0
- package/example/contracts/example-contract-7.tz +384 -0
- package/example/contracts/example-contract-8.tz +28 -0
- package/example/contracts/example-contract-9.tz +1010 -0
- package/example/example-usage-type-utilities.ts +35 -0
- package/example/example-usage.ts +410 -0
- package/example/types-file/example-contract-1.code.ts +6 -0
- package/example/types-file/example-contract-1.types.ts +87 -0
- package/example/types-file/example-contract-2.code.ts +6 -0
- package/example/types-file/example-contract-2.types.ts +122 -0
- package/example/types-file/example-contract-4.code.ts +6 -0
- package/example/types-file/example-contract-4.types.ts +97 -0
- package/example/types-file/example-contract-5.code.ts +6 -0
- package/example/types-file/example-contract-5.types.ts +173 -0
- package/example/types-file/example-contract-6.code.ts +6 -0
- package/example/types-file/example-contract-6.types.ts +122 -0
- package/example/types-file/example-contract-7.code.ts +6 -0
- package/example/types-file/example-contract-7.types.ts +78 -0
- package/example/types-file/example-contract-8.code.ts +6 -0
- package/example/types-file/example-contract-8.types.ts +86 -0
- package/example/types-file/example-contract-9.code.ts +6 -0
- package/example/types-file/example-contract-9.types.ts +29 -0
- package/example/types-file/type-aliases.ts +81 -0
- package/example/types-file/type-utils.ts +36 -0
- package/index.js +969 -0
- package/index.js.map +1 -0
- package/index.ts +32 -0
- package/package.json +55 -0
- package/run.ts +3 -0
- package/src/cli-process.ts +111 -0
- package/src/cli.ts +34 -0
- package/src/generator/common.ts +21 -0
- package/src/generator/contract-name.ts +6 -0
- package/src/generator/contract-parser.ts +358 -0
- package/src/generator/process.ts +66 -0
- package/src/generator/schema-output.ts +54 -0
- package/src/generator/typescript-output.ts +239 -0
- package/src/taquito-contract-type-generator.ts +4 -0
- package/src/type-aliases-file-content.ts +83 -0
- package/src/type-aliases.ts +80 -0
- package/src/type-utils-file-content.ts +38 -0
- package/src/type-utils.ts +35 -0
- package/tasks.ts +127 -0
- package/test/generator.spec.ts +69 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { assertExhaustive, GenerateApiError, reduceFlatMap } from './common';
|
|
2
|
+
import { TypedStorage, TypedMethod, TypedType, TypedVar } from './contract-parser';
|
|
3
|
+
|
|
4
|
+
export type TypescriptCodeOutput = {
|
|
5
|
+
typesFileContent: string;
|
|
6
|
+
contractCodeFileContent: string;
|
|
7
|
+
storage: string;
|
|
8
|
+
methods: string;
|
|
9
|
+
methodsObject: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type TypeAliasData = {
|
|
13
|
+
mode: 'local',
|
|
14
|
+
fileContent?: string,
|
|
15
|
+
} | {
|
|
16
|
+
mode: 'file' | 'library',
|
|
17
|
+
importPath?: string,
|
|
18
|
+
} | {
|
|
19
|
+
mode: 'simple',
|
|
20
|
+
};
|
|
21
|
+
export type TypeUtilsData = {
|
|
22
|
+
importPath: string,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const toTypescriptCode = (storage: TypedStorage, methods: TypedMethod[], contractName: string, parsedContract: unknown, protocol: { name: string, key: string }, typeAliasData: TypeAliasData, typeUtilsData: TypeUtilsData): TypescriptCodeOutput => {
|
|
26
|
+
type TypeAlias = { aliasType: string, simpleTypeDefinition: string, simpleTypeImports?: { name: string, isDefault?: boolean, from: string }[] };
|
|
27
|
+
const usedStrictTypes = [] as TypeAlias[];
|
|
28
|
+
const addTypeAlias = (strictType: TypeAlias) => {
|
|
29
|
+
if (!usedStrictTypes.some(x => x.aliasType === strictType.aliasType)) {
|
|
30
|
+
usedStrictTypes.push(strictType);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Not really tabs :)
|
|
35
|
+
const tabs = (indent: number) => Array(indent).fill(` `).join(``);
|
|
36
|
+
const toIndentedItems = (indent: number, delimeters: { afterItem?: string, beforeItem?: string }, items: string[]) => {
|
|
37
|
+
return `
|
|
38
|
+
${tabs(indent + 1)}${items.join(`${delimeters.afterItem ?? ``}
|
|
39
|
+
${tabs(indent + 1)}${delimeters.beforeItem ?? ``}`)}
|
|
40
|
+
${tabs(indent)}`;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const typeToCode = (t: TypedType, indent: number): string => {
|
|
44
|
+
if (t.kind === `value`) {
|
|
45
|
+
// return `${t.typescriptType}`;
|
|
46
|
+
|
|
47
|
+
const prim = `prim` in t.raw ? t.raw.prim : `unknown`;
|
|
48
|
+
|
|
49
|
+
// Strict mode
|
|
50
|
+
if (t.typescriptType === `boolean`
|
|
51
|
+
|| t.typescriptType === `string` && prim === `string`
|
|
52
|
+
) {
|
|
53
|
+
return `${t.typescriptType}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (t.typescriptType === 'number') {
|
|
57
|
+
const simpleBaseType = `string | BigNumber | number`;
|
|
58
|
+
const typeAlias: TypeAlias = { aliasType: prim, simpleTypeDefinition: `type ${prim} = ${simpleBaseType};`, simpleTypeImports: [{ name: 'BigNumber', isDefault: true, from: 'bignumber.js' }] };
|
|
59
|
+
addTypeAlias(typeAlias);
|
|
60
|
+
|
|
61
|
+
return typeAlias.aliasType;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const simpleBaseType = t.typescriptType === 'Date' ? 'Date | string' : t.typescriptType;
|
|
65
|
+
const typeAlias: TypeAlias = { aliasType: prim, simpleTypeDefinition: `type ${prim} = ${simpleBaseType};` };
|
|
66
|
+
addTypeAlias(typeAlias);
|
|
67
|
+
|
|
68
|
+
return typeAlias.aliasType;
|
|
69
|
+
}
|
|
70
|
+
if (t.kind === `array`) {
|
|
71
|
+
return `Array<${typeToCode(t.array.item, indent)}>`;
|
|
72
|
+
}
|
|
73
|
+
if (t.kind === `map`) {
|
|
74
|
+
|
|
75
|
+
const typeAlias: TypeAlias = t.map.isBigMap
|
|
76
|
+
? { aliasType: `BigMap`, simpleTypeDefinition: 'type BigMap<K, T> = MichelsonMap<K, T>;', simpleTypeImports: [{ name: 'MichelsonMap', from: '@taquito/taquito' }] }
|
|
77
|
+
: { aliasType: `MMap`, simpleTypeDefinition: 'type MMap<K, T> = MichelsonMap<K, T>;', simpleTypeImports: [{ name: 'MichelsonMap', from: '@taquito/taquito' }] };
|
|
78
|
+
addTypeAlias(typeAlias);
|
|
79
|
+
|
|
80
|
+
return `${typeAlias.aliasType}<${typeToCode(t.map.key, indent)}, ${typeToCode(t.map.value, indent)}>`;
|
|
81
|
+
}
|
|
82
|
+
if (t.kind === `object`) {
|
|
83
|
+
return `{${toIndentedItems(indent, {},
|
|
84
|
+
t.fields.map((a, i) => varToCode(a, i, indent + 1) + `;`),
|
|
85
|
+
)}}`;
|
|
86
|
+
}
|
|
87
|
+
if (t.kind === `union`) {
|
|
88
|
+
|
|
89
|
+
const getUnionItem = (a: TypedVar, i: number) => {
|
|
90
|
+
const itemCode = `${varToCode(a, i, indent + 1)}`;
|
|
91
|
+
|
|
92
|
+
// Keep on single line if already on single line
|
|
93
|
+
if (!itemCode.includes(`\n`)) {
|
|
94
|
+
return `{ ${itemCode} }`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Indent if multi-line (and remake with extra indent)
|
|
98
|
+
return `{${toIndentedItems(indent + 1, {}, [`${varToCode(a, i, indent + 2)}`])}}`;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return `(${toIndentedItems(indent, { beforeItem: `| ` },
|
|
102
|
+
t.union.map(getUnionItem),
|
|
103
|
+
)})`;
|
|
104
|
+
}
|
|
105
|
+
if (t.kind === `unit`) {
|
|
106
|
+
const typeAlias: TypeAlias = { aliasType: `unit`, simpleTypeDefinition: `type unit = (true | undefined);`, };
|
|
107
|
+
addTypeAlias(typeAlias);
|
|
108
|
+
return typeAlias.aliasType;
|
|
109
|
+
}
|
|
110
|
+
if (t.kind === `never`) {
|
|
111
|
+
return `never`;
|
|
112
|
+
}
|
|
113
|
+
if (t.kind === `unknown`) {
|
|
114
|
+
return `unknown`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
assertExhaustive(t, `Unknown type`);
|
|
118
|
+
throw new GenerateApiError(`Unknown type node`, { t });
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const varToCode = (t: TypedVar, i: number, indent: number): string => {
|
|
122
|
+
return `${t.name ?? i}${t.type.optional ? `?` : ``}: ${typeToCode(t.type, indent)}`;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const argsToCode = (args: TypedVar[], indent: number, asObject: boolean): string => {
|
|
126
|
+
if (args.length === 1) {
|
|
127
|
+
if (args[0].type.kind === `unit`) { return ``; }
|
|
128
|
+
return `${args[0].name ?? `param`}: ${typeToCode(args[0].type, indent + 1)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = `${toIndentedItems(indent, {},
|
|
132
|
+
args.filter(x => x.name || x.type.kind !== `unit`).map((a, i) => varToCode(a, i, indent + 1) + `,`),
|
|
133
|
+
)}`;
|
|
134
|
+
|
|
135
|
+
if(asObject){
|
|
136
|
+
return `params: {${result}}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return result;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const methodsToCode = (indent: number) => {
|
|
143
|
+
const methodFields = methods.map(x => {
|
|
144
|
+
const methodCode = `${x.name}: (${argsToCode(x.args, indent + 1, false)}) => Promise<void>;`;
|
|
145
|
+
return methodCode;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const methodsTypeCode = `type Methods = {${toIndentedItems(indent, {}, methodFields)}};`;
|
|
149
|
+
return methodsTypeCode;
|
|
150
|
+
};
|
|
151
|
+
const methodsObjectToCode = (indent: number) => {
|
|
152
|
+
const methodFields = methods.map(x => {
|
|
153
|
+
const methodCode = `${x.name}: (${argsToCode(x.args, indent + 1, true)}) => Promise<void>;`;
|
|
154
|
+
return methodCode;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const methodsTypeCode = `type MethodsObject = {${toIndentedItems(indent, {}, methodFields)}};`;
|
|
158
|
+
return methodsTypeCode;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const storageToCode = (indent: number) => {
|
|
162
|
+
const storageTypeCode = `type Storage = ${typeToCode(storage.storage, indent)};`;
|
|
163
|
+
return storageTypeCode;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const methodsCode = methodsToCode(0);
|
|
167
|
+
const methodsObjectCode = methodsObjectToCode(0);
|
|
168
|
+
const storageCode = storageToCode(0);
|
|
169
|
+
|
|
170
|
+
// Simple type aliases
|
|
171
|
+
const simpleTypeMappingImportsAll = new Map(usedStrictTypes.map(x => x.simpleTypeImports ?? []).reduce(reduceFlatMap, []).map(x => [`${x?.from}:${x?.name}:${x?.isDefault}`, x]));
|
|
172
|
+
const simpleTypeMappingImportsFrom = [...simpleTypeMappingImportsAll.values()].reduce((out, x) => {
|
|
173
|
+
const entry = out[x.from] ?? (out[x.from] = { names: [] });
|
|
174
|
+
if (x.isDefault) {
|
|
175
|
+
entry.default = x.name;
|
|
176
|
+
} else {
|
|
177
|
+
entry.names.push(x.name);
|
|
178
|
+
}
|
|
179
|
+
entry.names.sort((a, b) => a.localeCompare(b));
|
|
180
|
+
return out;
|
|
181
|
+
}, {} as { [from: string]: { names: string[], default?: string } });
|
|
182
|
+
|
|
183
|
+
const simpleTypeMappingImportsText = Object.keys(simpleTypeMappingImportsFrom)
|
|
184
|
+
.map(k => {
|
|
185
|
+
const entry = simpleTypeMappingImportsFrom[k];
|
|
186
|
+
const items = [entry.default, entry.names.length ? `{ ${entry.names.join(', ')} }` : ''].filter(x => x);
|
|
187
|
+
return `import ${items.join(', ')} from '${k}';\n`;
|
|
188
|
+
})
|
|
189
|
+
.join('');
|
|
190
|
+
|
|
191
|
+
const simpleTypeMapping = usedStrictTypes
|
|
192
|
+
.sort((a, b) => a.aliasType.localeCompare(b.aliasType))
|
|
193
|
+
.map(x => x.simpleTypeDefinition).join(`\n`);
|
|
194
|
+
|
|
195
|
+
const typeUtilsDefinitions =
|
|
196
|
+
`import { ContractAbstractionFromContractType, WalletContractAbstractionFromContractType } from '${typeUtilsData.importPath}';`;
|
|
197
|
+
|
|
198
|
+
const typeAliasesDefinitions =
|
|
199
|
+
typeAliasData.mode === 'simple' ? `${simpleTypeMappingImportsText}${simpleTypeMapping}`
|
|
200
|
+
: typeAliasData.mode === 'local' ? typeAliasData.fileContent
|
|
201
|
+
: `import { ${usedStrictTypes.map(x => x.aliasType).join(`, `)} } from '${typeAliasData.importPath}';`;
|
|
202
|
+
|
|
203
|
+
const contractTypeName = `${contractName}ContractType`;
|
|
204
|
+
const walletTypeName = `${contractName}WalletType`;
|
|
205
|
+
const codeName = `${contractName}Code`;
|
|
206
|
+
|
|
207
|
+
const typesFileContent = `
|
|
208
|
+
${typeUtilsDefinitions}
|
|
209
|
+
${typeAliasesDefinitions}
|
|
210
|
+
|
|
211
|
+
${storageCode}
|
|
212
|
+
|
|
213
|
+
${methodsCode}
|
|
214
|
+
|
|
215
|
+
${methodsObjectCode}
|
|
216
|
+
|
|
217
|
+
type contractTypes = { methods: Methods, methodsObject: MethodsObject, storage: Storage, code: { __type: '${codeName}', protocol: string, code: object[] } };
|
|
218
|
+
export type ${contractTypeName} = ContractAbstractionFromContractType<contractTypes>;
|
|
219
|
+
export type ${walletTypeName} = WalletContractAbstractionFromContractType<contractTypes>;
|
|
220
|
+
`;
|
|
221
|
+
|
|
222
|
+
const contractCodeFileContent = `
|
|
223
|
+
export const ${codeName}: { __type: '${codeName}', protocol: string, code: object[] } = {
|
|
224
|
+
__type: '${codeName}',
|
|
225
|
+
protocol: '${protocol.key}',
|
|
226
|
+
code: JSON.parse(\`${JSON.stringify(parsedContract)}\`)
|
|
227
|
+
};
|
|
228
|
+
`;
|
|
229
|
+
return {
|
|
230
|
+
typesFileContent,
|
|
231
|
+
contractCodeFileContent,
|
|
232
|
+
storage: storageCode,
|
|
233
|
+
methods: methodsCode,
|
|
234
|
+
methodsObject: methodsObjectCode,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// This is required for copying the type aliases to a local file
|
|
2
|
+
export const typeAliasesFileContent = `
|
|
3
|
+
import { BigNumber } from 'bignumber.js';
|
|
4
|
+
import { MichelsonMap } from '@taquito/taquito';
|
|
5
|
+
|
|
6
|
+
export type unit = (true | undefined) & { __type: 'unit' };
|
|
7
|
+
|
|
8
|
+
export type address = string & { __type: 'address' };
|
|
9
|
+
export type bytes = string & { __type: 'bytes' };
|
|
10
|
+
export type contract = string & { __type: 'contract' };
|
|
11
|
+
export type operation = string & { __type: 'operation' };
|
|
12
|
+
export type key = string & { __type: 'key' };
|
|
13
|
+
export type key_hash = string & { __type: 'key_hash' };
|
|
14
|
+
export type signature = string & { __type: 'signature' };
|
|
15
|
+
export type ticket = string & { __type: 'ticket' };
|
|
16
|
+
|
|
17
|
+
export type timestamp = string & { __type: 'timestamp' };
|
|
18
|
+
|
|
19
|
+
export type int = BigNumber & { __type: 'int' };
|
|
20
|
+
export type nat = BigNumber & { __type: 'nat' };
|
|
21
|
+
|
|
22
|
+
export type mutez = BigNumber & { __type: 'mutez' };
|
|
23
|
+
export type tez = BigNumber & { __type: 'tez' };
|
|
24
|
+
|
|
25
|
+
export type MMap<K, V> = Omit<MichelsonMap<K, V>, 'get'> & { get: (key: K) => V };
|
|
26
|
+
export type BigMap<K, V> = Omit<MichelsonMap<K, V>, 'get'> & { get: (key: K) => Promise<V> };
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const createStringTypeTas = <T extends string>() => {
|
|
30
|
+
return (value: string): T => value as T;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const createBigNumberTypeTas = <T extends BigNumber>() => {
|
|
34
|
+
return (value: number | BigNumber | string): T => new BigNumber(value) as T;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type asMapParamOf<K, V> = K extends string ? { [key: string]: V } | Array<{ key: K, value: V }>
|
|
38
|
+
: K extends number ? { [key: number]: V } | Array<{ key: K, value: V }>
|
|
39
|
+
: Array<{ key: K, value: V }>;
|
|
40
|
+
|
|
41
|
+
function asMap<K, V>(value: asMapParamOf<K, V>): MMap<K, V> {
|
|
42
|
+
const m = new MichelsonMap<K, V>();
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
const vArray = value as Array<{ key: K, value: V }>;
|
|
45
|
+
vArray.forEach(x => m.set(x.key, x.value));
|
|
46
|
+
} else {
|
|
47
|
+
const vObject = value as { [key: string]: V };
|
|
48
|
+
Object.keys(vObject).forEach(key => m.set(key as unknown as K, vObject[key]));
|
|
49
|
+
}
|
|
50
|
+
return m as MMap<K, V>;
|
|
51
|
+
}
|
|
52
|
+
const asBigMap = <K, V>(value: asMapParamOf<K, V>) => asMap(value) as unknown as BigMap<K, V>;
|
|
53
|
+
|
|
54
|
+
function add<T extends BigNumber>(a: T, b: T): T {
|
|
55
|
+
return a.plus(b) as T;
|
|
56
|
+
}
|
|
57
|
+
function subtract<T extends BigNumber>(a: T, b: T): T {
|
|
58
|
+
return a.minus(b) as T;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** tas: Tezos 'as' casting for strict types */
|
|
62
|
+
export const tas = {
|
|
63
|
+
address: createStringTypeTas<address>(),
|
|
64
|
+
bytes: createStringTypeTas<bytes>(),
|
|
65
|
+
contract: createStringTypeTas<contract>(),
|
|
66
|
+
timestamp: (value: string | Date): timestamp => new Date(value).toISOString() as timestamp,
|
|
67
|
+
|
|
68
|
+
int: createBigNumberTypeTas<int>(),
|
|
69
|
+
nat: createBigNumberTypeTas<nat>(),
|
|
70
|
+
mutez: createBigNumberTypeTas<mutez>(),
|
|
71
|
+
tez: createBigNumberTypeTas<tez>(),
|
|
72
|
+
|
|
73
|
+
map: asMap,
|
|
74
|
+
bigMap: asBigMap,
|
|
75
|
+
|
|
76
|
+
// Operations
|
|
77
|
+
add,
|
|
78
|
+
subtract,
|
|
79
|
+
|
|
80
|
+
// To number
|
|
81
|
+
number: (value: string | BigNumber) => Number(value + ''),
|
|
82
|
+
};
|
|
83
|
+
`;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { BigNumber } from 'bignumber.js';
|
|
2
|
+
import { MichelsonMap } from '@taquito/taquito';
|
|
3
|
+
|
|
4
|
+
export type unit = (true | undefined) & { __type: 'unit' };
|
|
5
|
+
|
|
6
|
+
export type address = string & { __type: 'address' };
|
|
7
|
+
export type bytes = string & { __type: 'bytes' };
|
|
8
|
+
export type contract = string & { __type: 'contract' };
|
|
9
|
+
export type operation = string & { __type: 'operation' };
|
|
10
|
+
export type key = string & { __type: 'key' };
|
|
11
|
+
export type key_hash = string & { __type: 'key_hash' };
|
|
12
|
+
export type signature = string & { __type: 'signature' };
|
|
13
|
+
export type ticket = string & { __type: 'ticket' };
|
|
14
|
+
|
|
15
|
+
export type timestamp = Date & { __type: 'timestamp' };
|
|
16
|
+
|
|
17
|
+
export type int = BigNumber & { __type: 'int' };
|
|
18
|
+
export type nat = BigNumber & { __type: 'nat' };
|
|
19
|
+
|
|
20
|
+
export type mutez = BigNumber & { __type: 'mutez' };
|
|
21
|
+
export type tez = BigNumber & { __type: 'tez' };
|
|
22
|
+
|
|
23
|
+
export type MMap<K, V> = Omit<MichelsonMap<K, V>, 'get'> & { get: (key: K) => V };
|
|
24
|
+
export type BigMap<K, V> = Omit<MichelsonMap<K, V>, 'get'> & { get: (key: K) => Promise<V> };
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const createStringTypeTas = <T extends string>() => {
|
|
28
|
+
return (value: string): T => value as T;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const createBigNumberTypeTas = <T extends BigNumber>() => {
|
|
32
|
+
return (value: number | BigNumber | string): T => new BigNumber(value) as T;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type asMapParamOf<K, V> = K extends string ? { [key: string]: V } | Array<{ key: K, value: V }>
|
|
36
|
+
: K extends number ? { [key: number]: V } | Array<{ key: K, value: V }>
|
|
37
|
+
: Array<{ key: K, value: V }>;
|
|
38
|
+
|
|
39
|
+
function asMap<K, V>(value: asMapParamOf<K, V>): MMap<K, V> {
|
|
40
|
+
const m = new MichelsonMap<K, V>();
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
const vArray = value as Array<{ key: K, value: V }>;
|
|
43
|
+
vArray.forEach(x => m.set(x.key, x.value));
|
|
44
|
+
} else {
|
|
45
|
+
const vObject = value as { [key: string]: V };
|
|
46
|
+
Object.keys(vObject).forEach(key => m.set(key as unknown as K, vObject[key]));
|
|
47
|
+
}
|
|
48
|
+
return m as MMap<K, V>;
|
|
49
|
+
}
|
|
50
|
+
const asBigMap = <K, V>(value: asMapParamOf<K, V>) => asMap(value) as unknown as BigMap<K, V>;
|
|
51
|
+
|
|
52
|
+
function add<T extends BigNumber>(a: T, b: T): T {
|
|
53
|
+
return a.plus(b) as T;
|
|
54
|
+
}
|
|
55
|
+
function subtract<T extends BigNumber>(a: T, b: T): T {
|
|
56
|
+
return a.minus(b) as T;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** tas: Tezos 'as' casting for strict types */
|
|
60
|
+
export const tas = {
|
|
61
|
+
address: createStringTypeTas<address>(),
|
|
62
|
+
bytes: createStringTypeTas<bytes>(),
|
|
63
|
+
contract: createStringTypeTas<contract>(),
|
|
64
|
+
timestamp: (value: string | Date): timestamp => new Date(value) as timestamp,
|
|
65
|
+
|
|
66
|
+
int: createBigNumberTypeTas<int>(),
|
|
67
|
+
nat: createBigNumberTypeTas<nat>(),
|
|
68
|
+
mutez: createBigNumberTypeTas<mutez>(),
|
|
69
|
+
tez: createBigNumberTypeTas<tez>(),
|
|
70
|
+
|
|
71
|
+
map: asMap,
|
|
72
|
+
bigMap: asBigMap,
|
|
73
|
+
|
|
74
|
+
// Operations
|
|
75
|
+
add,
|
|
76
|
+
subtract,
|
|
77
|
+
|
|
78
|
+
// To number
|
|
79
|
+
number: (value: string | BigNumber) => Number(value + ''),
|
|
80
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// This is required for copying the type utils to a local file
|
|
2
|
+
export const typeUtilsFileContent = `
|
|
3
|
+
import { ContractAbstraction, ContractMethod, ContractMethodObject, ContractProvider, Wallet } from '@taquito/taquito';
|
|
4
|
+
|
|
5
|
+
type BaseContractType = { methods: unknown, methodsObject: unknown, storage: unknown };
|
|
6
|
+
|
|
7
|
+
type ContractMethodsOf<T extends ContractProvider | Wallet, TContract extends BaseContractType> = {
|
|
8
|
+
[M in keyof TContract['methods']]:
|
|
9
|
+
TContract['methods'][M] extends (...args: infer A) => unknown
|
|
10
|
+
? (...args: A) => ContractMethod<T>
|
|
11
|
+
: never
|
|
12
|
+
};
|
|
13
|
+
type ContractMethodsObjectsOf<T extends ContractProvider | Wallet, TContract extends BaseContractType> = {
|
|
14
|
+
[M in keyof TContract['methodsObject']]:
|
|
15
|
+
TContract['methodsObject'][M] extends (...args: infer A) => unknown
|
|
16
|
+
? (...args: A) => ContractMethodObject<T>
|
|
17
|
+
: never
|
|
18
|
+
};
|
|
19
|
+
type ContractStorageOf<TContract extends BaseContractType> = TContract['storage'];
|
|
20
|
+
|
|
21
|
+
export type ContractAbstractionFromContractType<TContract extends BaseContractType> =
|
|
22
|
+
ContractAbstraction<ContractProvider,
|
|
23
|
+
ContractMethodsOf<ContractProvider, TContract>,
|
|
24
|
+
ContractMethodsObjectsOf<ContractProvider, TContract>,
|
|
25
|
+
{},
|
|
26
|
+
{},
|
|
27
|
+
ContractStorageOf<TContract>
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
export type WalletContractAbstractionFromContractType<TContract extends BaseContractType> =
|
|
31
|
+
ContractAbstraction<Wallet,
|
|
32
|
+
ContractMethodsOf<Wallet, TContract>,
|
|
33
|
+
ContractMethodsObjectsOf<Wallet, TContract>,
|
|
34
|
+
{},
|
|
35
|
+
{},
|
|
36
|
+
ContractStorageOf<TContract>
|
|
37
|
+
>;
|
|
38
|
+
`;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ContractAbstraction, ContractMethod, ContractMethodObject, ContractProvider, Wallet } from '@taquito/taquito';
|
|
2
|
+
|
|
3
|
+
type BaseContractType = { methods: unknown, methodsObject: unknown, storage: unknown };
|
|
4
|
+
|
|
5
|
+
type ContractMethodsOf<T extends ContractProvider | Wallet, TContract extends BaseContractType> = {
|
|
6
|
+
[M in keyof TContract['methods']]:
|
|
7
|
+
TContract['methods'][M] extends (...args: infer A) => unknown
|
|
8
|
+
? (...args: A) => ContractMethod<T>
|
|
9
|
+
: never
|
|
10
|
+
};
|
|
11
|
+
type ContractMethodsObjectsOf<T extends ContractProvider | Wallet, TContract extends BaseContractType> = {
|
|
12
|
+
[M in keyof TContract['methodsObject']]:
|
|
13
|
+
TContract['methodsObject'][M] extends (...args: infer A) => unknown
|
|
14
|
+
? (...args: A) => ContractMethodObject<T>
|
|
15
|
+
: never
|
|
16
|
+
};
|
|
17
|
+
type ContractStorageOf<TContract extends BaseContractType> = TContract['storage'];
|
|
18
|
+
|
|
19
|
+
export type ContractAbstractionFromContractType<TContract extends BaseContractType> =
|
|
20
|
+
ContractAbstraction<ContractProvider,
|
|
21
|
+
ContractMethodsOf<ContractProvider, TContract>,
|
|
22
|
+
ContractMethodsObjectsOf<ContractProvider, TContract>,
|
|
23
|
+
{},
|
|
24
|
+
{},
|
|
25
|
+
ContractStorageOf<TContract>
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
export type WalletContractAbstractionFromContractType<TContract extends BaseContractType> =
|
|
29
|
+
ContractAbstraction<Wallet,
|
|
30
|
+
ContractMethodsOf<Wallet, TContract>,
|
|
31
|
+
ContractMethodsObjectsOf<Wallet, TContract>,
|
|
32
|
+
{},
|
|
33
|
+
{},
|
|
34
|
+
ContractStorageOf<TContract>
|
|
35
|
+
>;
|
package/tasks.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { SanitizedArgs, ActionResponse, Failure, LikeAPromise, ProxyAction } from "taqueria-sdk/types";
|
|
2
|
+
import glob from 'fast-glob'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { generateContractTypesProcessContractFiles } from "./src/cli-process";
|
|
5
|
+
|
|
6
|
+
type PluginOpts = {
|
|
7
|
+
// TODO: Document these
|
|
8
|
+
typescriptDir: string,
|
|
9
|
+
typeAliasMode?: 'local' | 'file' | 'library' | 'simple',
|
|
10
|
+
};
|
|
11
|
+
type Opts = SanitizedArgs & Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
const getContractAbspath = (contractFilename: string, parsedArgs: Opts) =>
|
|
14
|
+
join(parsedArgs.artifactsDir, /\.tz$/.test(contractFilename) ? contractFilename : `${contractFilename}.tz`)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const generateContractTypes = (parsedArgs: Opts & PluginOpts) => async (contractFilename: string) : Promise<string> => {
|
|
18
|
+
const contractAbspath = getContractAbspath(contractFilename, parsedArgs);
|
|
19
|
+
await generateContractTypesProcessContractFiles({
|
|
20
|
+
inputTzContractDirectory: parsedArgs.artifactsDir,
|
|
21
|
+
inputFiles: [contractAbspath],
|
|
22
|
+
outputTypescriptDirectory: parsedArgs.typescriptDir,
|
|
23
|
+
format: 'tz',
|
|
24
|
+
typeAliasMode: parsedArgs.typeAliasMode ?? 'file',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return `${contractFilename}: Types generated`;
|
|
28
|
+
|
|
29
|
+
// TODO: Generate contract michelson
|
|
30
|
+
// TODO: Generate types from michelson
|
|
31
|
+
|
|
32
|
+
// throw new Error('Not Implemented');
|
|
33
|
+
|
|
34
|
+
// // TODO: Should getting the default environment be provided by the SDK or the framework?
|
|
35
|
+
// const currentEnv = parsedArgs.env
|
|
36
|
+
// ? (parsedArgs.env as string)
|
|
37
|
+
// : (
|
|
38
|
+
// parsedArgs.config.environment
|
|
39
|
+
// ? parsedArgs.config.environment.default
|
|
40
|
+
// : 'development'
|
|
41
|
+
// )
|
|
42
|
+
// const env = parsedArgs.config.environment && parsedArgs.config.environment[currentEnv]
|
|
43
|
+
// ? parsedArgs.config.environment[currentEnv]
|
|
44
|
+
// : undefined
|
|
45
|
+
|
|
46
|
+
// // Has storage been provided for this contract?
|
|
47
|
+
// if (env && env.storage) {
|
|
48
|
+
// try {
|
|
49
|
+
// const tezos = new TezosToolkit(env.rpcUrl)
|
|
50
|
+
// const contractData = await readFile(contractAbspath, "utf-8")
|
|
51
|
+
|
|
52
|
+
// // TODO: Generate contract michelson
|
|
53
|
+
// // TODO: Generate types from michelson
|
|
54
|
+
|
|
55
|
+
// throw new Error('Not Implemented');
|
|
56
|
+
// // await importKey(tezos, env.faucet.email, env.faucet.password, env.faucet.mnemonic.join(' '), env.faucet.activation_code)
|
|
57
|
+
// // return tezos.contract.originate({
|
|
58
|
+
// // code: contractData,
|
|
59
|
+
// // storage: env.storage[contractFilename]
|
|
60
|
+
// // })
|
|
61
|
+
// // .then(operation => `${contractFilename}: ${operation.contractAddress}`)
|
|
62
|
+
// }
|
|
63
|
+
// catch (err) {
|
|
64
|
+
// return Promise.reject({
|
|
65
|
+
// status: 'failed',
|
|
66
|
+
// stdout: "",
|
|
67
|
+
// stderr: err
|
|
68
|
+
// })
|
|
69
|
+
// }
|
|
70
|
+
// }
|
|
71
|
+
|
|
72
|
+
// return Promise.reject({
|
|
73
|
+
// status: 'failed',
|
|
74
|
+
// stderr: `No storage configured in your configuration file for ${contractFilename}`,
|
|
75
|
+
// stdout: ""
|
|
76
|
+
// })
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const generateContractTypesAll = (parsedArgs: Opts & PluginOpts) : Promise<string[]> =>
|
|
80
|
+
glob("**/*.tz", {cwd: parsedArgs.artifactsDir})
|
|
81
|
+
.then(files => Promise.all(files.map(generateContractTypes(parsedArgs))))
|
|
82
|
+
|
|
83
|
+
export const generateTypes = <T>(parsedArgs: Opts): LikeAPromise<ActionResponse, Failure<T>> => {
|
|
84
|
+
if(!parsedArgs.typescriptDir){
|
|
85
|
+
return Promise.reject({
|
|
86
|
+
status: 'failed',
|
|
87
|
+
stderr: `No typescriptDir configured`,
|
|
88
|
+
stdout: ""
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// WORKAROUND: Redirect console.log
|
|
93
|
+
const strOutLog = [] as string[];
|
|
94
|
+
const consoleLogOrig = console.log;
|
|
95
|
+
console.log = (message:string, data?:unknown) => {
|
|
96
|
+
strOutLog.push(`${message}${data?`\n${JSON.stringify(data,null,2)}`:''}`);
|
|
97
|
+
}
|
|
98
|
+
console.log('generateTypes', {
|
|
99
|
+
typescriptDir: parsedArgs.typescriptDir
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// console.log = consoleLogOrig;
|
|
103
|
+
// return Promise.resolve({
|
|
104
|
+
// status: 'success',
|
|
105
|
+
// stdout: `${strOutLog.join('\n')}`,
|
|
106
|
+
// stderr: ""
|
|
107
|
+
// });
|
|
108
|
+
|
|
109
|
+
const argsTyped = parsedArgs as Opts & PluginOpts;
|
|
110
|
+
|
|
111
|
+
const p = argsTyped.contract
|
|
112
|
+
? generateContractTypes(argsTyped) (argsTyped.contract as string)
|
|
113
|
+
: generateContractTypesAll(argsTyped)
|
|
114
|
+
|
|
115
|
+
return p.then(data => {
|
|
116
|
+
console.log = consoleLogOrig;
|
|
117
|
+
return ({
|
|
118
|
+
status: 'success',
|
|
119
|
+
stdout: `${strOutLog.join('\n')}${Array.isArray(data) ? data.join("\n") : data}`,
|
|
120
|
+
stderr: ""
|
|
121
|
+
});
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const tasks = {
|
|
126
|
+
generateTypes,
|
|
127
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
import { generateContractTypesFromMichelsonCode } from '../src/generator/process';
|
|
5
|
+
import { normalizeContractName } from '../src/generator/contract-name';
|
|
6
|
+
import { TypeAliasData } from '../src/generator/typescript-output';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const readFileText = async (filePath: string): Promise<string> => {
|
|
10
|
+
return fs.readFile(filePath, { encoding: 'utf8' });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('Generate Example Contracts', () => {
|
|
14
|
+
|
|
15
|
+
const typeAliasDataLibrary: TypeAliasData = { mode: 'library', importPath: '@taquito/contract-type-generator' };
|
|
16
|
+
const typeAliasDataSimple: TypeAliasData = { mode: 'simple' };
|
|
17
|
+
|
|
18
|
+
const exampleDir = path.resolve(__dirname, `../example`);
|
|
19
|
+
|
|
20
|
+
const testContractTypeGeneration = async (contractFileName: string, format: 'tz' | 'json', mode: 'library' | 'simple') => {
|
|
21
|
+
const contractRaw = await readFileText(`${exampleDir}/contracts/${contractFileName}.${format}`);
|
|
22
|
+
const expectedTypeFileContent = await readFileText(`${exampleDir}/types${mode === 'simple' ? '-simple' : ''}/${contractFileName}.types.ts`);
|
|
23
|
+
const expectedCodeFileContent = await readFileText(`${exampleDir}/types${mode === 'simple' ? '-simple' : ''}/${contractFileName}.code.ts`);
|
|
24
|
+
const contractName = normalizeContractName(contractFileName);
|
|
25
|
+
const typeAliasData = mode === 'library' ? typeAliasDataLibrary : typeAliasDataSimple;
|
|
26
|
+
const { typescriptCodeOutput: { typesFileContent: actualTypesFileContent, contractCodeFileContent: actualCodeFileContent } } = generateContractTypesFromMichelsonCode(contractRaw, contractName, format, typeAliasData);
|
|
27
|
+
expect(actualTypesFileContent.trim()).toEqual(expectedTypeFileContent.trim());
|
|
28
|
+
expect(actualCodeFileContent.trim()).toEqual(expectedCodeFileContent.trim());
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
it('Generate Types 01 - tz library', async () => {
|
|
32
|
+
await testContractTypeGeneration('example-contract-1', 'tz', 'library');
|
|
33
|
+
});
|
|
34
|
+
it('Generate Types 01 - tz simple', async () => {
|
|
35
|
+
await testContractTypeGeneration('example-contract-1', 'tz', 'simple');
|
|
36
|
+
});
|
|
37
|
+
it('Generate Types 02 - tz library', async () => {
|
|
38
|
+
await testContractTypeGeneration('example-contract-2', 'tz', 'library');
|
|
39
|
+
});
|
|
40
|
+
it('Generate Types 02 - tz simple', async () => {
|
|
41
|
+
await testContractTypeGeneration('example-contract-2', 'tz', 'simple');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('Generate Types 03 - json library', async () => {
|
|
45
|
+
await testContractTypeGeneration('example-contract-3', 'json', 'library');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('Generate Types 04 - newer protocol', async () => {
|
|
49
|
+
await testContractTypeGeneration('example-contract-4', 'tz', 'library');
|
|
50
|
+
});
|
|
51
|
+
it('Generate Types 04 - tz simple', async () => {
|
|
52
|
+
await testContractTypeGeneration('example-contract-4', 'tz', 'simple');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// it(`Generate Types - All`, async () => {
|
|
56
|
+
// const allExampleContracts = (await fs.readdir(`${exampleDir}/contracts/`)).filter(f=>f.endsWith('.tz') || f.endsWith('.json'));
|
|
57
|
+
|
|
58
|
+
// for(const f of allExampleContracts){
|
|
59
|
+
// const fRelative = f.replace(exampleDir,'');
|
|
60
|
+
// const m = fRelative.match(/^(.*)\.(tz|json)$/);
|
|
61
|
+
// if(!m){ return; }
|
|
62
|
+
// const [_,filename, ext] = m;
|
|
63
|
+
|
|
64
|
+
// await testContractTypeGeneration(filename, ext as 'tz'|'json', 'simple');
|
|
65
|
+
// }
|
|
66
|
+
// });
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
});
|