gen-typescript-from-tolk-dev 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +53 -27
  2. package/bin/generator.js +1 -13
  3. package/dist/abi-types.d.ts +169 -0
  4. package/dist/abi-types.js +8 -0
  5. package/dist/abi.d.ts +53 -0
  6. package/dist/abi.js +13 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.js +122 -0
  9. package/dist/codegen-ctx.d.ts +66 -0
  10. package/dist/codegen-ctx.js +108 -0
  11. package/dist/dynamic-ctx.d.ts +17 -0
  12. package/dist/dynamic-ctx.js +39 -0
  13. package/dist/dynamic-debug-print.d.ts +9 -0
  14. package/{src/dynamic-debug-print.ts → dist/dynamic-debug-print.js} +57 -65
  15. package/dist/dynamic-get-methods.d.ts +43 -0
  16. package/dist/dynamic-get-methods.js +451 -0
  17. package/dist/dynamic-serialization.d.ts +22 -0
  18. package/dist/dynamic-serialization.js +453 -0
  19. package/dist/dynamic-validation.d.ts +8 -0
  20. package/{src/dynamic-validation.ts → dist/dynamic-validation.js} +19 -18
  21. package/dist/emit-field-defs.d.ts +5 -0
  22. package/dist/emit-field-defs.js +61 -0
  23. package/dist/emit-pack-unpack.d.ts +9 -0
  24. package/dist/emit-pack-unpack.js +296 -0
  25. package/dist/emit-stack-rw.d.ts +4 -0
  26. package/dist/emit-stack-rw.js +232 -0
  27. package/dist/emit-ts-types.d.ts +6 -0
  28. package/dist/emit-ts-types.js +74 -0
  29. package/dist/formatting.d.ts +15 -0
  30. package/{src/formatting.ts → dist/formatting.js} +26 -28
  31. package/dist/generate-ts-wrappers.d.ts +7 -0
  32. package/dist/generate-ts-wrappers.js +444 -0
  33. package/dist/index.d.ts +4 -0
  34. package/dist/index.js +11 -0
  35. package/dist/out-template.generated.d.ts +1 -0
  36. package/dist/out-template.generated.js +6 -0
  37. package/dist/tolk-to-abi.d.ts +2 -0
  38. package/dist/tolk-to-abi.js +22 -0
  39. package/dist/types-kernel.d.ts +10 -0
  40. package/dist/types-kernel.js +212 -0
  41. package/dist/unsupported-errors.d.ts +28 -0
  42. package/{src/unsupported-errors.ts → dist/unsupported-errors.js} +33 -39
  43. package/package.json +73 -27
  44. package/src/abi-types.ts +0 -157
  45. package/src/abi.ts +0 -132
  46. package/src/cli-generate-from-abi-json.ts +0 -21
  47. package/src/codegen-ctx.ts +0 -115
  48. package/src/dynamic-ctx.ts +0 -55
  49. package/src/dynamic-get-methods.ts +0 -454
  50. package/src/dynamic-serialization.ts +0 -430
  51. package/src/emit-field-defs.ts +0 -60
  52. package/src/emit-pack-unpack.ts +0 -280
  53. package/src/emit-stack-rw.ts +0 -239
  54. package/src/emit-ts-types.ts +0 -66
  55. package/src/generate-from-abi-json.ts +0 -22
  56. package/src/generate-ts-wrappers.ts +0 -477
  57. package/src/out-template.ts +0 -514
  58. package/src/tolk-to-abi.ts +0 -5
  59. package/src/types-kernel.ts +0 -215
  60. package/tsconfig.json +0 -13
@@ -1,48 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OutBuf = void 0;
4
+ exports.safeJsIdent = safeJsIdent;
5
+ exports.safeFieldDecl = safeFieldDecl;
6
+ exports.safeFieldRead = safeFieldRead;
1
7
  // OutBuf helps to generate TypeScript output with proper indentation.
2
- export class OutBuf {
3
- static TAB = ' '
4
-
5
- static onNewLine(s: string): string {
8
+ class OutBuf {
9
+ constructor() {
10
+ this.lines = [];
11
+ this.indentLevel = 0;
12
+ }
13
+ static onNewLine(s) {
6
14
  return `{indent}${s}{outdent}`;
7
15
  }
8
-
9
- private static indentNested(input: string, indentLevel: number): string {
16
+ static indentNested(input, indentLevel) {
10
17
  let result = '';
11
18
  let i = 0;
12
-
13
19
  while (i < input.length) {
14
20
  if (input.startsWith('{indent}', i)) {
15
21
  result += '\n' + ' '.repeat(++indentLevel);
16
22
  i += 8;
17
- } else if (input.startsWith('{outdent}', i)) {
23
+ }
24
+ else if (input.startsWith('{outdent}', i)) {
18
25
  result += '\n' + ' '.repeat(--indentLevel);
19
26
  i += 9;
20
- } else if (input[i] === '\n') {
27
+ }
28
+ else if (input[i] === '\n') {
21
29
  result += '\n' + ' '.repeat(indentLevel);
22
30
  i++;
23
- } else {
31
+ }
32
+ else {
24
33
  result += input[i];
25
34
  i++;
26
35
  }
27
36
  }
28
-
29
37
  return result;
30
38
  }
31
-
32
- private lines = [] as string[];
33
- private indentLevel = 0;
34
-
35
39
  indent() { this.indentLevel++; }
36
40
  outdent() { this.indentLevel--; }
37
41
  emptyLine() { this.lines.push(''); }
38
-
39
- push(line: string) {
42
+ push(line) {
40
43
  if (line.includes('{indent}') || line.includes('\n')) {
41
44
  line = OutBuf.indentNested(line.trimEnd(), this.indentLevel);
42
45
  line = line.replace(/\s+\n/g, '\n');
43
46
  line = line.replace(/\n\s+,/g, ',');
44
47
  }
45
-
46
48
  if (line.startsWith('}') || line.startsWith(']') || line.startsWith(')')) {
47
49
  this.outdent();
48
50
  }
@@ -52,47 +54,43 @@ export class OutBuf {
52
54
  }
53
55
  return this;
54
56
  }
55
-
56
57
  toString() {
57
58
  return this.lines.join('\n');
58
59
  }
59
60
  }
60
-
61
+ exports.OutBuf = OutBuf;
62
+ OutBuf.TAB = ' ';
61
63
  // In Tolk, similar to Kotlin, identifiers (structs, fields, etc.) can be embraced:
62
64
  // > struct `foo-bar` { `a()`: int }
63
65
  // Such names are incorrect in TS/JS and require special handling.
64
- function isSafeJsIdent(tolkStructName: string): boolean {
66
+ function isSafeJsIdent(tolkStructName) {
65
67
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(tolkStructName);
66
68
  }
67
-
68
69
  // In TS, `interface` and `const` can not contain '-' and other invalid symbols,
69
70
  // so we mangle them: "foo-bar@" -> "foo_bar_".
70
71
  // NB! Keywords, like 'await' or 'function', are also invalid identifiers,
71
72
  // but since structs and type aliases are recommended to be named CamelCase in Tolk contracts,
72
73
  // we don't expect collisions with JS keywords here.
73
- export function safeJsIdent(tolkIdentifier: string): string {
74
+ function safeJsIdent(tolkIdentifier) {
74
75
  if (isSafeJsIdent(tolkIdentifier)) {
75
76
  return tolkIdentifier;
76
77
  }
77
-
78
78
  let safe = tolkIdentifier.replace(/[^a-zA-Z0-9_$]/g, '_');
79
79
  if (/^[0-9]/.test(safe)) {
80
80
  safe = '_' + safe;
81
81
  }
82
82
  return safe;
83
83
  }
84
-
85
84
  // In object literals, 'foo-bar' can be used (without mangling to '_') if quoted:
86
85
  // > interface A { foo: number, "foo-bar": number }
87
86
  // > return { foo: 0, "foo-bar": 123 }
88
87
  // Also, `__proto__` requires special handling, but I decided not to take it into account.
89
- export function safeFieldDecl(fieldName: string): string {
88
+ function safeFieldDecl(fieldName) {
90
89
  return isSafeJsIdent(fieldName) ? fieldName : JSON.stringify(fieldName);
91
90
  }
92
-
93
91
  // When accessing object fields, use ["syntax"] for invalid identifiers:
94
92
  // > obj.foo
95
93
  // > obj["foo-bar"]
96
- export function safeFieldRead(fieldName: string): string {
94
+ function safeFieldRead(fieldName) {
97
95
  return isSafeJsIdent(fieldName) ? `.${fieldName}` : `[${JSON.stringify(fieldName)}]`;
98
96
  }
@@ -0,0 +1,7 @@
1
+ import type { ContractABI } from './abi';
2
+ /**
3
+ * The main function: accepts top-level declarations from Tolk sources (converted to ABI)
4
+ * and returns the full TypeScript wrapper with all serializers, send, and get methods.
5
+ * @throws CantGenerateWrappersAtAll
6
+ */
7
+ export declare function generateTypeScriptFileForContract(contract: ContractABI): string;
@@ -0,0 +1,444 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateTypeScriptFileForContract = generateTypeScriptFileForContract;
4
+ const codegen_ctx_1 = require("./codegen-ctx");
5
+ const formatting_1 = require("./formatting");
6
+ const emit_ts_types_1 = require("./emit-ts-types");
7
+ const emit_pack_unpack_1 = require("./emit-pack-unpack");
8
+ const emit_stack_rw_1 = require("./emit-stack-rw");
9
+ const emit_field_defs_1 = require("./emit-field-defs");
10
+ const unsupported_errors_1 = require("./unsupported-errors");
11
+ const types_kernel_1 = require("./types-kernel");
12
+ const out_template_generated_1 = require("./out-template.generated");
13
+ // Output a struct as a doc comment.
14
+ function renderStructDocComment(s) {
15
+ const genericTs = s.type_params ? `<${s.type_params.join(', ')}>` : '';
16
+ return [
17
+ '/**',
18
+ ` > struct${s.prefix ? ` (${s.prefix.prefix_str})` : ''} ${s.name}${genericTs} {`,
19
+ ...s.fields.map(f => ` > ${f.name}: ${(0, types_kernel_1.renderTy)(f.ty)}`),
20
+ ' > }',
21
+ ' */'
22
+ ].join('\n');
23
+ }
24
+ // Output an alias as a doc comment.
25
+ function renderAliasDocComment(a) {
26
+ const genericTs = a.type_params ? `<${a.type_params.join(', ')}>` : '';
27
+ return [
28
+ '/**',
29
+ ` > type ${a.name}${genericTs} = ${(0, types_kernel_1.renderTy)(a.target_ty)}`,
30
+ ' */'
31
+ ].join('\n');
32
+ }
33
+ // Output an enum as a doc comment.
34
+ function renderEnumDocComment(e) {
35
+ return [
36
+ '/**',
37
+ ` > enum ${e.name} { ${e.members.length} variants }`,
38
+ ' */'
39
+ ].join('\n');
40
+ }
41
+ // If a struct can't be serialized, then its `fromSlice`/`store` is just `throw` with description.
42
+ // Since Tolk code contains many structs (some are used for contract getters, for example),
43
+ // not all of them are intended for serialization, it's expected.
44
+ function renderThrowCantPackUnpack(structName, caughtEx, isPack) {
45
+ if (!(caughtEx instanceof unsupported_errors_1.CantGeneratePackUnpack))
46
+ throw caughtEx;
47
+ const action = isPack ? `pack '${structName}' to cell` : `unpack '${structName}' from cell`;
48
+ return `throw new Error(\`Can't ${action}, because ${caughtEx.message}\`);`;
49
+ }
50
+ // All-in-one: a struct with autogen serializers.
51
+ // Tolk `struct X` -> TS `interface X` { $: 'X'; ...fields } + `const X` with serializers.
52
+ function tolkStructToTypeScript(ctx, s, buf) {
53
+ const ps = s.type_params;
54
+ const genericTs = ps ? `<${ps.join(', ')}>` : '';
55
+ const structName = (0, formatting_1.safeJsIdent)(s.name);
56
+ const structTy = { kind: 'StructRef', struct_name: s.name, type_args: s.type_params?.map(name_t => ({ kind: 'genericT', name_t })) };
57
+ const loadCallbackParams = ps ? ', ' + ps.map(p => `loadFn_${p}: LoadCallback<${p}>`).join(', ') : '';
58
+ const storeCallbackParams = ps ? ', ' + ps.map(p => `storeFn_${p}: StoreCallback<${p}>`).join(', ') : '';
59
+ buf.push(renderStructDocComment(s));
60
+ buf.push(`export interface ${structName}${genericTs} {`);
61
+ buf.push(`readonly $: '${s.name}'`);
62
+ for (const f of s.fields) {
63
+ let comment = f.default_value ? ` /* = ${(0, emit_field_defs_1.emitFieldDefaultInComment)(f.default_value)} */` : '';
64
+ buf.push(`${(0, formatting_1.safeFieldDecl)(f.name)}: ${(0, emit_ts_types_1.emitTsType)(ctx, f.ty)}${comment}`);
65
+ }
66
+ buf.push('}');
67
+ buf.emptyLine();
68
+ buf.push(`export const ${structName} = {`);
69
+ if (s.prefix) {
70
+ buf.push(`PREFIX: ${s.prefix.prefix_str},`);
71
+ buf.emptyLine();
72
+ }
73
+ let argsParam = s.fields.length ? 'args: ' + structParameterNoLabel(ctx, s.name) : '';
74
+ buf.push(`create${genericTs}(${argsParam}): ${structName}${genericTs} {`);
75
+ buf.push(`return {`);
76
+ buf.push(`$: '${s.name}',`);
77
+ for (let f of s.fields.filter(f => f.default_value && (0, emit_field_defs_1.isDefaultValueSupported)(f.ty))) {
78
+ let needAsAny = f.ty.kind === 'genericT';
79
+ buf.push(`${(0, formatting_1.safeFieldDecl)(f.name)}: ${(0, emit_field_defs_1.emitFieldDefault)(ctx, f.default_value)}${needAsAny ? ' as any' : ''},`);
80
+ }
81
+ if (s.fields.length > 0) {
82
+ buf.push('...args');
83
+ }
84
+ buf.push('}');
85
+ buf.push(`},`);
86
+ buf.push(`fromSlice${genericTs}(s: c.Slice${loadCallbackParams}): ${structName}${genericTs} {`);
87
+ if (s.custom_pack_unpack?.unpack_from_slice) {
88
+ buf.push(`return invokeCustomUnpackFromSlice<${structName}>('${s.name}', s);`);
89
+ }
90
+ else
91
+ try {
92
+ const fieldsLoads = s.fields.map(f => `${(0, formatting_1.safeFieldDecl)(f.name)}: ${(0, emit_pack_unpack_1.emitLoadExpr)(ctx, `${structName}${(0, formatting_1.safeFieldRead)(f.name)}`, f.ty)},`);
93
+ if (s.prefix && s.prefix.prefix_len === 32) {
94
+ buf.push(`${codegen_ctx_1.RUNTIME.loadAndCheckPrefix32}(s, ${s.prefix.prefix_str}, '${s.name}');`);
95
+ }
96
+ else if (s.prefix) {
97
+ ctx.has_non32Prefixes = true;
98
+ buf.push(`${codegen_ctx_1.RUNTIME.loadAndCheckPrefix}(s, ${s.prefix.prefix_str}, ${s.prefix.prefix_len}, '${s.name}');`);
99
+ }
100
+ buf.push(`return {`);
101
+ buf.push(`$: '${s.name}',`);
102
+ fieldsLoads.forEach(l => buf.push(l));
103
+ buf.push('}');
104
+ }
105
+ catch (ex) {
106
+ buf.push(renderThrowCantPackUnpack(structName, ex, false));
107
+ }
108
+ buf.push(`},`);
109
+ buf.push(`store${genericTs}(self: ${structName}${genericTs}, b: c.Builder${storeCallbackParams}): void {`);
110
+ if (s.custom_pack_unpack?.pack_to_builder) {
111
+ buf.push(`invokeCustomPackToBuilder<${structName}>('${s.name}', self, b);`);
112
+ }
113
+ else
114
+ try {
115
+ const fieldsStores = s.fields.map(f => (0, emit_pack_unpack_1.emitStoreStatement)(ctx, `self${(0, formatting_1.safeFieldRead)(f.name)}`, f.ty));
116
+ if (s.prefix) {
117
+ buf.push(`b.storeUint(${s.prefix.prefix_str}, ${s.prefix.prefix_len});`);
118
+ }
119
+ fieldsStores.forEach(l => buf.push(l));
120
+ }
121
+ catch (ex) {
122
+ buf.push(renderThrowCantPackUnpack(structName, ex, true));
123
+ }
124
+ buf.push(`},`);
125
+ buf.push(`toCell${genericTs}(self: ${structName}${genericTs}${storeCallbackParams}): c.Cell {`);
126
+ buf.push(`return ${(0, emit_pack_unpack_1.emitMakeCellFromExpr)(ctx, 'self', structTy, true)};`);
127
+ buf.push('}');
128
+ buf.push('}');
129
+ return buf.toString();
130
+ }
131
+ // All-in-one: an alias with autogen serializers.
132
+ // Tolk `type X = Y` -> TS `type X = Y` (interchangeable with its underlying type, like in Tolk).
133
+ function tolkAliasToTypeScript(ctx, a, buf) {
134
+ const ps = a.type_params;
135
+ const genericTs = ps ? `<${ps.join(', ')}>` : '';
136
+ const aliasName = (0, formatting_1.safeJsIdent)(a.name);
137
+ const aliasTy = { kind: 'AliasRef', alias_name: a.name, type_args: a.type_params?.map(name_t => ({ kind: 'genericT', name_t })) };
138
+ const loadCallbackParams = ps ? ', ' + ps.map(p => `loadFn_${p}: LoadCallback<${p}>`).join(', ') : '';
139
+ const storeCallbackParams = ps ? ', ' + ps.map(p => `storeFn_${p}: StoreCallback<${p}>`).join(', ') : '';
140
+ ctx.has_customPackUnpack || (ctx.has_customPackUnpack = a.custom_pack_unpack !== undefined);
141
+ buf.push(renderAliasDocComment(a));
142
+ if (a.target_ty.kind === 'union') {
143
+ buf.push(`export type ${aliasName}${genericTs} =`).indent();
144
+ for (const v of (0, types_kernel_1.createLabelsForUnion)(ctx.symbols, a.target_ty.variants)) {
145
+ buf.push('| ' + (0, emit_ts_types_1.emitUnionLabelAndValue)(v, (0, emit_ts_types_1.emitTsType)(ctx, v.variant_ty)));
146
+ }
147
+ buf.outdent();
148
+ }
149
+ else {
150
+ buf.push(`export type ${aliasName}${genericTs} = ${(0, emit_ts_types_1.emitTsType)(ctx, a.target_ty)}`);
151
+ }
152
+ buf.emptyLine();
153
+ buf.push(`export const ${aliasName} = {`);
154
+ buf.push(`fromSlice${genericTs}(s: c.Slice${loadCallbackParams}): ${aliasName}${genericTs} {`);
155
+ if (a.custom_pack_unpack?.unpack_from_slice) {
156
+ buf.push(`return invokeCustomUnpackFromSlice<${aliasName}>('${a.name}', s);`);
157
+ }
158
+ else
159
+ try {
160
+ buf.push(`return ${(0, emit_pack_unpack_1.emitLoadExpr)(ctx, aliasName, a.target_ty)};`);
161
+ }
162
+ catch (ex) {
163
+ buf.push(renderThrowCantPackUnpack(aliasName, ex, false));
164
+ }
165
+ buf.push(`},`);
166
+ buf.push(`store${genericTs}(self: ${aliasName}${genericTs}, b: c.Builder${storeCallbackParams}): void {`);
167
+ if (a.custom_pack_unpack?.pack_to_builder) {
168
+ buf.push(`invokeCustomPackToBuilder<${aliasName}>('${a.name}', self, b);`);
169
+ }
170
+ else
171
+ try {
172
+ buf.push((0, emit_pack_unpack_1.emitStoreStatement)(ctx, 'self', a.target_ty));
173
+ }
174
+ catch (ex) {
175
+ buf.push(renderThrowCantPackUnpack(aliasName, ex, true));
176
+ }
177
+ buf.push(`},`);
178
+ buf.push(`toCell${genericTs}(self: ${aliasName}${genericTs}${storeCallbackParams}): c.Cell {`);
179
+ buf.push(`return ${(0, emit_pack_unpack_1.emitMakeCellFromExpr)(ctx, 'self', aliasTy, true)};`);
180
+ buf.push('}');
181
+ buf.push('}');
182
+ return buf.toString();
183
+ }
184
+ // All-in-one: an enum with autogen serializers.
185
+ // Tolk `enum X` -> TS `type X = bigint` + `const X` with members and serializers.
186
+ function tolkEnumToTypeScript(ctx, e, buf) {
187
+ const enumName = (0, formatting_1.safeJsIdent)(e.name);
188
+ const enumTy = { kind: 'EnumRef', enum_name: e.name };
189
+ buf.push(renderEnumDocComment(e));
190
+ buf.push(`export type ${enumName} = bigint`);
191
+ buf.emptyLine();
192
+ buf.push(`export const ${enumName} = {`);
193
+ for (const v of e.members) {
194
+ let memberName = v.name;
195
+ if (memberName === 'toCell' || memberName === 'store' || memberName === 'fromSlice') {
196
+ memberName += '_';
197
+ }
198
+ buf.push(`${(0, formatting_1.safeFieldDecl)(memberName)}: ${v.value}n,`);
199
+ }
200
+ buf.emptyLine();
201
+ buf.push(`fromSlice(s: c.Slice): ${enumName} {`);
202
+ if (e.custom_pack_unpack?.unpack_from_slice) {
203
+ buf.push(`return invokeCustomUnpackFromSlice<${enumName}>('${e.name}', s);`);
204
+ }
205
+ else {
206
+ buf.push(`return ${(0, emit_pack_unpack_1.emitLoadExpr)(ctx, e.name, e.encoded_as)};`);
207
+ }
208
+ buf.push(`},`);
209
+ buf.push(`store(self: ${enumName}, b: c.Builder): void {`);
210
+ if (e.custom_pack_unpack?.pack_to_builder) {
211
+ buf.push(`return invokeCustomPackToBuilder<${enumName}>('${e.name}', self, b);`);
212
+ }
213
+ else {
214
+ buf.push((0, emit_pack_unpack_1.emitStoreStatement)(ctx, 'self', e.encoded_as));
215
+ }
216
+ buf.push(`},`);
217
+ buf.push(`toCell(self: ${enumName}): c.Cell {`);
218
+ buf.push(`return ${(0, emit_pack_unpack_1.emitMakeCellFromExpr)(ctx, 'self', enumTy, true)};`);
219
+ buf.push('}');
220
+ buf.push('}');
221
+ return buf.toString();
222
+ }
223
+ // Convert "get_wallet_data" to "getWalletData".
224
+ function snakeCaseToCamelCase(snakeString) {
225
+ return snakeString.replace(/_([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
226
+ }
227
+ // For sendXXX contract methods, we generate not `(msg: XXX)` but `(body: { ...fields })`,
228
+ // so that on a caller side, one can use `contract.sendXXX({ queryId: 0n, ... })`
229
+ // instead of `contract.sendXXX(XXX.create({ queryId: 0n, ... }))` (although the latter is also acceptable).
230
+ // Note that if a field has a default value, it's declared as optional (`f?: T`, not `f: T`).
231
+ function structParameterNoLabel(ctx, structName, typeArgs) {
232
+ const structRef = ctx.symbols.getStruct(structName);
233
+ let declBuf = new formatting_1.OutBuf();
234
+ declBuf.push(`{`);
235
+ for (let f of structRef.fields) {
236
+ let comment = f.default_value ? ` /* = ${(0, emit_field_defs_1.emitFieldDefaultInComment)(f.default_value)} */` : '';
237
+ let fTy = typeArgs ? (0, types_kernel_1.instantiateGenerics)(f.ty, structRef.type_params, typeArgs) : f.ty;
238
+ declBuf.push(`${(0, formatting_1.safeFieldDecl)(f.name)}${f.default_value && (0, emit_field_defs_1.isDefaultValueSupported)(fTy) ? '?' : ''}: ${(0, emit_ts_types_1.emitTsType)(ctx, fTy)}${comment}`);
239
+ }
240
+ declBuf.push(`}`);
241
+ return declBuf.toString();
242
+ }
243
+ // Generate `static fromStorage()` method for a storage of type {ty}.
244
+ // Most likely, it's a struct, then a caller can just use `MyContract.fromStorage({ fields })`.
245
+ function generateFromStorageMethod(ctx, ty, contractName) {
246
+ if (ty == null || ty.kind === 'nullLiteral') { // means "no storage, don't emit a method"
247
+ return '';
248
+ }
249
+ let storageParamT = (0, emit_ts_types_1.emitTsType)(ctx, ty);
250
+ let bodyArg = 'emptyStorage';
251
+ if (ty.kind === 'StructRef') {
252
+ storageParamT = structParameterNoLabel(ctx, ty.struct_name, ty.type_args);
253
+ bodyArg = (0, emit_pack_unpack_1.emitCallToCreateMethodExpr)(ctx, 'emptyStorage', ty);
254
+ }
255
+ let buf = new formatting_1.OutBuf();
256
+ buf.push(`static fromStorage(emptyStorage: ${storageParamT}, deployedOptions?: DeployedAddrOptions) {`);
257
+ buf.push(`const initialState = {`);
258
+ buf.push(`code: deployedOptions?.overrideContractCode ?? ${contractName}.CodeCell,`);
259
+ buf.push(`data: ${(0, emit_pack_unpack_1.emitMakeCellFromExpr)(ctx, bodyArg, ty)},`);
260
+ buf.push(`};`);
261
+ buf.push(`const address = calculateDeployedAddress(initialState.code, initialState.data, deployedOptions ?? {});`);
262
+ buf.push(`return new ${contractName}(address, initialState);`);
263
+ buf.push(`}`);
264
+ return buf.toString();
265
+ }
266
+ // Generate a `createCellOfXXX` static method for a contract class.
267
+ // It's essentially a wrapper for `XXX.toCell(XXX.create(body))`, but quite handy for the end user,
268
+ // to make a cell for TON Connect. In practice, `toCell` is needed almost for messages only.
269
+ function generateCreateCellOfMessageMethod(ctx, ty) {
270
+ let functionName = snakeCaseToCamelCase((0, formatting_1.safeJsIdent)(`createCellOf_${(0, types_kernel_1.renderTy)(ty)}`));
271
+ let bodyParamT = (0, emit_ts_types_1.emitTsType)(ctx, ty);
272
+ let bodyArg = 'body';
273
+ if (ty.kind === 'StructRef') {
274
+ bodyParamT = structParameterNoLabel(ctx, ty.struct_name, ty.type_args);
275
+ bodyArg = (0, emit_pack_unpack_1.emitCallToCreateMethodExpr)(ctx, 'body', ty);
276
+ }
277
+ let buf = new formatting_1.OutBuf();
278
+ buf.push(`static ${functionName}(body: ${bodyParamT}) {`);
279
+ buf.push(`return ${(0, emit_pack_unpack_1.emitMakeCellFromExpr)(ctx, bodyArg, ty)};`);
280
+ buf.push(`}`);
281
+ return buf.toString();
282
+ }
283
+ // Generate a `sendXXX` method for a message of type {ty}.
284
+ // Most likely, it's a struct (an instantiated one also possible), but it can also be an alias for example.
285
+ function generateSendMethod(ctx, ty) {
286
+ let functionName = snakeCaseToCamelCase((0, formatting_1.safeJsIdent)(`send_${(0, types_kernel_1.renderTy)(ty)}`));
287
+ let bodyParamT = (0, emit_ts_types_1.emitTsType)(ctx, ty);
288
+ let bodyArg = 'body';
289
+ if (ty.kind === 'StructRef') {
290
+ bodyParamT = structParameterNoLabel(ctx, ty.struct_name, ty.type_args);
291
+ bodyArg = (0, emit_pack_unpack_1.emitCallToCreateMethodExpr)(ctx, 'body', ty);
292
+ }
293
+ let buf = new formatting_1.OutBuf();
294
+ buf.push(`async ${functionName}(provider: ContractProvider, via: Sender, msgValue: coins, body: ${bodyParamT}, extraOptions?: ${codegen_ctx_1.RUNTIME.ExtraSendOptions}) {`);
295
+ buf.push(`return provider.internal(via, {`);
296
+ buf.push(`value: msgValue,`);
297
+ buf.push(`body: ${(0, emit_pack_unpack_1.emitMakeCellFromExpr)(ctx, bodyArg, ty)},`);
298
+ buf.push(`...extraOptions`);
299
+ buf.push(`});`);
300
+ buf.push(`}`);
301
+ return buf.toString();
302
+ }
303
+ // Generate a `getXXX` method: a contract getter that works off-chain, via the stack.
304
+ function generateGetMethod(ctx, getM) {
305
+ let functionName = snakeCaseToCamelCase((0, formatting_1.safeJsIdent)('get_' + getM.name));
306
+ functionName = functionName.replace(/^getGet/, 'get');
307
+ let nParams = getM.parameters.length;
308
+ let paramNames = getM.parameters.map(p => {
309
+ let collision = Object.values(codegen_ctx_1.RUNTIME).some(n => p.name === n)
310
+ || p.name === 'c' // import @ton/core
311
+ || p.name === 'r' // StackReader in body
312
+ || p.name === 'provider'; // implicit parameter
313
+ return (0, formatting_1.safeJsIdent)(collision ? p.name + '_' : p.name);
314
+ });
315
+ let buf = new formatting_1.OutBuf();
316
+ let paramsStr = nParams ? `, ${getM.parameters.map((p, i) => `${paramNames[i]}: ${(0, emit_ts_types_1.emitTsType)(ctx, p.ty)}${p.default_value ? ` = ${(0, emit_field_defs_1.emitFieldDefault)(ctx, p.default_value)}` : ''}`).join(', ')}` : '';
317
+ if (getM.return_ty.kind === 'tensor') {
318
+ buf.push(`async ${functionName}(provider: ContractProvider${paramsStr}): Promise<[`);
319
+ for (let item of getM.return_ty.items) {
320
+ buf.push((0, emit_ts_types_1.emitTsType)(ctx, item) + ',');
321
+ }
322
+ buf.push(']> {');
323
+ }
324
+ else {
325
+ buf.push(`async ${functionName}(provider: ContractProvider${paramsStr}): Promise<${(0, emit_ts_types_1.emitTsType)(ctx, getM.return_ty)}> {`);
326
+ }
327
+ let stackW = (0, types_kernel_1.calcWidthOnStack)(ctx.symbols, getM.return_ty);
328
+ if (nParams) {
329
+ buf.push(`const r = ${codegen_ctx_1.RUNTIME.StackReader}.fromGetMethod(${stackW}, await provider.get('${getM.name}', [`);
330
+ let stackWrites = getM.parameters.flatMap((p, i) => (0, emit_stack_rw_1.emitStackWriteItems)(ctx, paramNames[i], p.ty));
331
+ for (let w of stackWrites) {
332
+ buf.push(w + ',');
333
+ }
334
+ buf.push(`]));`);
335
+ }
336
+ else {
337
+ buf.push(`const r = ${codegen_ctx_1.RUNTIME.StackReader}.fromGetMethod(${stackW}, await provider.get('${getM.name}', []));`);
338
+ }
339
+ buf.push(`return ${(0, emit_stack_rw_1.emitStackReadExpr)(ctx, 'result', getM.return_ty)};`);
340
+ buf.push(`}`);
341
+ return buf.toString();
342
+ }
343
+ // Render a template of a TypeScript wrapper.
344
+ function renderTsTemplate(tplStr, content) {
345
+ // {{if:...}} blocks: keep blank lines before it if true
346
+ const IF_BLOCK_RE = /(^(?:\s*\n)*)\s*\/\/\s*{{if:(\w+)}}[^\n]*\n([\s\S]+?)^\s*\/\/\s*{{\/if:\2}}[^\n]*/gm;
347
+ tplStr = tplStr.replace(IF_BLOCK_RE, (_m, leadingBlank, name, inner) => content.flags[name] ? leadingBlank + inner : '');
348
+ // {{slot}}: keep indentation, remove a commented marker
349
+ const SLOT_RE = /(^ *)(?:\/\/\s*)?{{(\w+)}}[^\n]*$/gm;
350
+ tplStr = tplStr.replace(SLOT_RE, (_m, indent, name) => content.slots[name].split('\n').map(l => l.length ? indent + l : '').join('\n'));
351
+ // {{slot}} inlined (without a preceding comment)
352
+ tplStr = tplStr.replace(/{{(\w+)}}/g, (_m, name) => content.slots[name]);
353
+ return tplStr
354
+ .replace(/\n\s*\n(\s*})/g, '\n$1') // remove an empty line before closing brace
355
+ .replace(/\n\s*\n(?:\s*\n)+/g, '\n\n') // merge 2+ blank lines
356
+ .replace(/CONTRACT_CLASS_NAME/g, content.contractClassName);
357
+ }
358
+ /**
359
+ * The main function: accepts top-level declarations from Tolk sources (converted to ABI)
360
+ * and returns the full TypeScript wrapper with all serializers, send, and get methods.
361
+ * @throws CantGenerateWrappersAtAll
362
+ */
363
+ function generateTypeScriptFileForContract(contract) {
364
+ let ctx = new codegen_ctx_1.CodegenCtx(contract.declarations);
365
+ const declarationsBuf = new formatting_1.OutBuf();
366
+ for (const n of contract.declarations) {
367
+ try {
368
+ if (n.kind === 'struct')
369
+ tolkStructToTypeScript(ctx, n, declarationsBuf);
370
+ else if (n.kind === 'alias')
371
+ tolkAliasToTypeScript(ctx, n, declarationsBuf);
372
+ else if (n.kind === 'enum')
373
+ tolkEnumToTypeScript(ctx, n, declarationsBuf);
374
+ declarationsBuf.emptyLine();
375
+ }
376
+ catch (ex) {
377
+ throw new unsupported_errors_1.CantGenerateWrappersAtAll(`Error while generating ${n.kind.toLowerCase()} '${n.name}'`, ex);
378
+ }
379
+ }
380
+ let packUnpackSerializers = declarationsBuf.toString();
381
+ let createCellsMethodsStr = contract.incoming_messages.map(msg => generateCreateCellOfMessageMethod(ctx, msg.body_ty));
382
+ let sendMethodsStr = contract.incoming_messages.map(msg => {
383
+ try {
384
+ return generateSendMethod(ctx, msg.body_ty);
385
+ }
386
+ catch (ex) {
387
+ throw new unsupported_errors_1.CantGenerateWrappersAtAll(`Error while generating send method '${(0, types_kernel_1.renderTy)(msg.body_ty)}'`, ex);
388
+ }
389
+ });
390
+ let getMethodsStr = contract.get_methods.map(getM => {
391
+ try {
392
+ return generateGetMethod(ctx, getM);
393
+ }
394
+ catch (ex) {
395
+ throw new unsupported_errors_1.CantGenerateWrappersAtAll(`Error while generating get method '${getM.name}'`, ex);
396
+ }
397
+ });
398
+ let intNAliasesStr = ctx.sortOccurred(ctx.intNOccurred).map(intN => `type ${intN} = bigint`);
399
+ let uintNAliasesStr = ctx.sortOccurred(ctx.uintNOccurred).map(uintN => `type ${uintN} = bigint`);
400
+ let varIntNAliasesStr = ctx.sortOccurred(ctx.varIntNOccurred).map(varIntN => `type ${varIntN} = bigint`);
401
+ let sliceAliasesStr = ctx.sortOccurred(ctx.bitsNOccurred).map(bitsN => `type ${bitsN} = c.Slice`);
402
+ let errorCodesStr = contract.thrown_errors.filter(t => t.name).map(t => `'${t.name}': ${t.err_code},`);
403
+ return renderTsTemplate(out_template_generated_1.OUT_TEMPLATE, {
404
+ contractClassName: contract.contract_name,
405
+ flags: {
406
+ 'has_sendDeploy': true,
407
+ 'has_bitsN': ctx.bitsNOccurred.size > 0,
408
+ 'has_varIntN': ctx.varIntNOccurred.size > 0,
409
+ 'has_remaining': ctx.has_RemainingBitsAndRefs,
410
+ 'has_customPackUnpack': ctx.has_customPackUnpack,
411
+ 'has_customDictV': ctx.has_customDictV,
412
+ 'has_implicitUnionPrefix': ctx.has_implicitUnionPrefix,
413
+ 'has_non32Prefixes': ctx.has_non32Prefixes,
414
+ 'has_addressAny': ctx.has_addressAny,
415
+ 'has_arrayOf': ctx.has_arrayOf,
416
+ 'has_lispListOf': ctx.has_lispListOf,
417
+ 'stackReadsUnknown': ctx.stackReadsUnknown,
418
+ 'stackReadsArrayOf': ctx.stackReadsArrayOf,
419
+ 'stackReadsLispListOf': ctx.stackReadsLispListOf,
420
+ 'stackReadsSnakeString': ctx.stackReadsSnakeString,
421
+ 'stackReadsTuple': ctx.stackReadsTuple,
422
+ 'stackReadsMapKV': ctx.stackReadsMapKV,
423
+ 'stackReadsBuilder': ctx.stackReadsBuilder,
424
+ 'stackReadsNullable': ctx.stackReadsNullable,
425
+ 'stackReadsWideNullable': ctx.stackReadsWideNullable,
426
+ 'stackReadsUnionType': ctx.stackReadsUnionType,
427
+ 'stackReadsCellRef': ctx.stackReadsCellRef,
428
+ 'stackReadsNullLiteral': ctx.stackReadsNullLiteral,
429
+ },
430
+ slots: {
431
+ 'intNAliases': intNAliasesStr.join('\n'),
432
+ 'uintNAliases': uintNAliasesStr.join('\n'),
433
+ 'varIntNAliases': varIntNAliasesStr.join('\n'),
434
+ 'sliceAliases': sliceAliasesStr.join('\n'),
435
+ 'packUnpackSerializers': packUnpackSerializers,
436
+ 'codeBoc64': contract.codeBoc64,
437
+ 'errorCodes': errorCodesStr.join('\n'),
438
+ 'fromStorageMethod': generateFromStorageMethod(ctx, contract.storage.storage_at_deployment_ty ?? contract.storage.storage_ty, contract.contract_name),
439
+ 'createCellsMethods': createCellsMethodsStr.join('\n\n'),
440
+ 'sendMethods': sendMethodsStr.join('\n\n'),
441
+ 'getMethods': getMethodsStr.join('\n\n'),
442
+ }
443
+ });
444
+ }
@@ -0,0 +1,4 @@
1
+ export { generateTypeScriptFileForContract } from './generate-ts-wrappers';
2
+ export type { ContractABI, ABIGetMethod } from './abi';
3
+ export type { ABIConstExpression, ABIAlias, ABIEnum, ABIStruct, Ty, } from './abi-types';
4
+ export { CantGeneratePackUnpack, CantGenerateWrappersAtAll, NonStandardDictKey, NotSupportedTypeOnStack, SymbolNotFound, } from './unsupported-errors';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SymbolNotFound = exports.NotSupportedTypeOnStack = exports.NonStandardDictKey = exports.CantGenerateWrappersAtAll = exports.CantGeneratePackUnpack = exports.generateTypeScriptFileForContract = void 0;
4
+ var generate_ts_wrappers_1 = require("./generate-ts-wrappers");
5
+ Object.defineProperty(exports, "generateTypeScriptFileForContract", { enumerable: true, get: function () { return generate_ts_wrappers_1.generateTypeScriptFileForContract; } });
6
+ var unsupported_errors_1 = require("./unsupported-errors");
7
+ Object.defineProperty(exports, "CantGeneratePackUnpack", { enumerable: true, get: function () { return unsupported_errors_1.CantGeneratePackUnpack; } });
8
+ Object.defineProperty(exports, "CantGenerateWrappersAtAll", { enumerable: true, get: function () { return unsupported_errors_1.CantGenerateWrappersAtAll; } });
9
+ Object.defineProperty(exports, "NonStandardDictKey", { enumerable: true, get: function () { return unsupported_errors_1.NonStandardDictKey; } });
10
+ Object.defineProperty(exports, "NotSupportedTypeOnStack", { enumerable: true, get: function () { return unsupported_errors_1.NotSupportedTypeOnStack; } });
11
+ Object.defineProperty(exports, "SymbolNotFound", { enumerable: true, get: function () { return unsupported_errors_1.SymbolNotFound; } });
@@ -0,0 +1 @@
1
+ export declare const OUT_TEMPLATE = "// AUTO-GENERATED, do not edit\n// it's a TypeScript wrapper for a CONTRACT_CLASS_NAME contract in Tolk\n/* eslint-disable */\n\nimport * as c from '@ton/core';\nimport { beginCell, ContractProvider, Sender, SendMode } from '@ton/core';\n\n// \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\n// predefined types and functions\n//\n\n// {{if:has_remaining}}\ntype RemainingBitsAndRefs = c.Slice\n// {{/if:has_remaining}}\n\n// {{if:has_addressAny}}\nexport type any_address = c.Address | c.ExternalAddress | 'none'\n// {{/if:has_addressAny}}\n\n// {{if:has_arrayOf}}\ntype array<T> = T[]\n// {{/if:has_arrayOf}}\n\n// {{if:has_lispListOf}}\n// TypeScript wrappers flatten a TVM linked list `[1 [2 [3 null]]]` to `[1 2 3]`\ntype lisp_list<T> = T[]\n// {{/if:has_lispListOf}}\n\ntype StoreCallback<T> = (obj: T, b: c.Builder) => void\ntype LoadCallback<T> = (s: c.Slice) => T\n\nexport type CellRef<T> = {\n ref: T\n}\n\nfunction makeCellFrom<T>(self: T, storeFn_T: StoreCallback<T>): c.Cell {\n let b = beginCell();\n storeFn_T(self, b);\n return b.endCell();\n}\n\nfunction loadAndCheckPrefix32(s: c.Slice, expected: number, structName: string): void {\n let prefix = s.loadUint(32);\n if (prefix !== expected) {\n throw new Error(`Incorrect prefix for '${structName}': expected 0x${expected.toString(16).padStart(8, '0')}, got 0x${prefix.toString(16).padStart(8, '0')}`);\n }\n}\n\n// {{if:has_non32Prefixes}}\nfunction formatPrefix(prefixNum: number, prefixLen: number): string {\n return prefixLen % 4 ? `0b${prefixNum.toString(2).padStart(prefixLen, '0')}` : `0x${prefixNum.toString(16).padStart(prefixLen / 4, '0')}`;\n}\n\nfunction loadAndCheckPrefix(s: c.Slice, expected: number, prefixLen: number, structName: string): void {\n let prefix = s.loadUint(prefixLen);\n if (prefix !== expected) {\n throw new Error(`Incorrect prefix for '${structName}': expected ${formatPrefix(expected, prefixLen)}, got ${formatPrefix(prefix, prefixLen)}`);\n }\n}\n// {{/if:has_non32Prefixes}}\n\nfunction lookupPrefix(s: c.Slice, expected: number, prefixLen: number): boolean {\n return s.remainingBits >= prefixLen && s.preloadUint(prefixLen) === expected;\n}\n\n// {{if:has_implicitUnionPrefix}}\nfunction lookupPrefixAndEat(s: c.Slice, expected: number, prefixLen: number): boolean {\n if (lookupPrefix(s, expected, prefixLen)) {\n s.skip(prefixLen);\n return true;\n }\n return false;\n}\n// {{/if:has_implicitUnionPrefix}}\n\nfunction throwNonePrefixMatch(fieldPath: string): never {\n throw new Error(`Incorrect prefix for '${fieldPath}': none of variants matched`);\n}\n\nfunction storeCellRef<T>(cell: CellRef<T>, b: c.Builder, storeFn_T: StoreCallback<T>): void {\n let b_ref = c.beginCell();\n storeFn_T(cell.ref, b_ref);\n b.storeRef(b_ref.endCell());\n}\n\nfunction loadCellRef<T>(s: c.Slice, loadFn_T: LoadCallback<T>): CellRef<T> {\n let s_ref = s.loadRef().beginParse();\n return { ref: loadFn_T(s_ref) };\n}\n\n// {{if:has_addressAny}}\nfunction storeTolkAddressAny(a: any_address, b: c.Builder): void {\n let maybe_addr = a === 'none' ? null : a;\n b.storeAddress(maybe_addr);\n}\n\nfunction loadTolkAddressAny(s: c.Slice): any_address {\n let maybe_addr = s.loadAddressAny();\n return maybe_addr === null ? 'none' : maybe_addr;\n}\n// {{/if:has_addressAny}}\n\n// {{if:has_bitsN}}\nfunction storeTolkBitsN(v: c.Slice, nBits: number, b: c.Builder): void {\n if (v.remainingBits !== nBits) { throw new Error(`expected ${nBits} bits, got ${v.remainingBits}`); }\n if (v.remainingRefs !== 0) { throw new Error(`expected 0 refs, got ${v.remainingRefs}`); }\n b.storeSlice(v);\n}\n\nfunction loadTolkBitsN(s: c.Slice, nBits: number): c.Slice {\n return new c.Slice(new c.BitReader(s.loadBits(nBits)), []);\n}\n// {{/if:has_bitsN}}\n\n// {{if:has_remaining}}\nfunction storeTolkRemaining(v: RemainingBitsAndRefs, b: c.Builder): void {\n b.storeSlice(v);\n}\n\nfunction loadTolkRemaining(s: c.Slice): RemainingBitsAndRefs {\n let rest = s.clone();\n s.loadBits(s.remainingBits);\n while (s.remainingRefs) {\n s.loadRef();\n }\n return rest;\n}\n// {{/if:has_remaining}}\n\nfunction storeTolkNullable<T>(v: T | null, b: c.Builder, storeFn_T: StoreCallback<T>): void {\n if (v === null) {\n b.storeUint(0, 1);\n } else {\n b.storeUint(1, 1);\n storeFn_T(v, b);\n }\n}\n\n// {{if:has_arrayOf}}\nfunction storeArrayOf<T>(v: array<T>, b: c.Builder, storeFn_T: StoreCallback<T>): void {\n // the compiler stores array<T> in chunks; in TypeScript, for simplicity, store \"1 elem = 1 ref\"\n let tail = null as c.Cell | null;\n for (let i = 0; i < v.length; ++i) {\n let chunkB = beginCell().storeMaybeRef(tail);\n storeFn_T(v[v.length - 1 - i], chunkB);\n tail = chunkB.endCell();\n }\n b.storeUint(v.length, 8);\n b.storeMaybeRef(tail);\n}\n\nfunction loadArrayOf<T>(s: c.Slice, loadFn_T: LoadCallback<T>): array<T> {\n let len = s.loadUint(8);\n let head = s.loadMaybeRef();\n let outArr = [] as array<T>;\n while (head != null) {\n let s = head.beginParse();\n head = s.loadMaybeRef();\n while (s.remainingBits || s.remainingRefs) {\n outArr.push(loadFn_T(s));\n }\n }\n if (len !== outArr.length) {\n throw new Error(`mismatch array binary data: expected ${len} elements, got ${outArr.length}`);\n }\n return outArr;\n}\n// {{/if:has_arrayOf}}\n\n// {{if:has_lispListOf}}\nfunction storeLispListOf<T>(v: lisp_list<T>, b: c.Builder, storeFn_T: StoreCallback<T>): void {\n let tail = c.Cell.EMPTY;\n for (let i = 0; i < v.length; ++i) {\n let itemB = beginCell();\n storeFn_T(v[i], itemB);\n tail = itemB.storeRef(tail).endCell();\n }\n b.storeRef(tail);\n}\n\nfunction loadLispListOf<T>(s: c.Slice, loadFn_T: LoadCallback<T>): lisp_list<T> {\n let outArr = [] as lisp_list<T>;\n let head = s.loadRef().beginParse();\n while (head.remainingRefs) {\n let tailSnaked = head.loadRef();\n let headValue = loadFn_T(head);\n head.endParse(); // ensure no data is present besides T\n outArr.unshift(headValue);\n head = tailSnaked.beginParse();\n }\n return outArr;\n}\n// {{/if:has_lispListOf}}\n\n// {{if:has_customDictV}}\nfunction createDictionaryValue<V>(loadFn_V: LoadCallback<V>, storeFn_V: StoreCallback<V>): c.DictionaryValue<V> {\n return {\n serialize(self: V, b: c.Builder) {\n storeFn_V(self, b);\n },\n parse(s: c.Slice): V {\n const value = loadFn_V(s);\n s.endParse();\n return value;\n }\n }\n}\n// {{/if:has_customDictV}}\n\n// \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\n// parse get methods result from a TVM stack\n//\n\nclass StackReader {\n constructor(private tuple: c.TupleItem[]) {\n }\n\n static fromGetMethod(expectedN: number, getMethodResult: { stack: c.TupleReader }): StackReader {\n let tuple = [] as c.TupleItem[];\n while (getMethodResult.stack.remaining) {\n tuple.push(getMethodResult.stack.pop());\n }\n if (tuple.length !== expectedN) {\n throw new Error(`expected ${expectedN} stack width, got ${tuple.length}`);\n }\n return new StackReader(tuple);\n }\n\n private popExpecting<ItemT>(itemType: string): ItemT {\n const item = this.tuple.shift();\n if (item?.type !== itemType) {\n throw new Error(`not '${itemType}' on a stack`);\n }\n return item as ItemT;\n }\n\n readBigInt(): bigint {\n return this.popExpecting<c.TupleItemInt>('int').value;\n }\n\n readBoolean(): boolean {\n return this.popExpecting<c.TupleItemInt>('int').value !== 0n;\n }\n\n readCell(): c.Cell {\n return this.popExpecting<c.TupleItemCell>('cell').cell;\n }\n\n readSlice(): c.Slice {\n return this.popExpecting<c.TupleItemSlice>('slice').cell.beginParse();\n }\n\n // {{if:stackReadsBuilder}}\n readBuilder(): c.Builder {\n return beginCell().storeSlice(this.popExpecting<c.TupleItemBuilder>('builder').cell.beginParse());\n }\n // {{/if:stackReadsBuilder}}\n\n // {{if:stackReadsUnknown}}\n readUnknown(): c.TupleItem {\n // `unknown` from Tolk is left as a raw tuple item\n return this.tuple.shift()!;\n }\n // {{/if:stackReadsUnknown}}\n\n // {{if:stackReadsArrayOf}}\n readArrayOf<T>(readFn_T: (nestedReader: StackReader) => T): T[] {\n const subItems = this.popExpecting<c.Tuple>('tuple').items;\n const subReader = new StackReader(subItems);\n // array len N => N subItems => N calls to readFn_T\n return [...subItems].map(_ => readFn_T(subReader));\n }\n // {{/if:stackReadsArrayOf}}\n\n // {{if:stackReadsLispListOf}}\n readLispListOf<T>(readFn_T: (nestedReader: StackReader) => T): T[] {\n // read `[1 [2 [3 null]]]` to `[1 2 3]`\n let pairReader: StackReader = this;\n let outArr = [] as T[];\n while (true) {\n if (pairReader.tuple[0].type === 'null') {\n pairReader.tuple.shift();\n break;\n }\n let headAndTail = pairReader.popExpecting<c.Tuple>('tuple').items;\n if (headAndTail.length !== 2) {\n throw new Error(`malformed lisp_list, expected 2 stack width, got ${headAndTail.length}`);\n }\n pairReader = new StackReader(headAndTail);\n outArr.push(readFn_T(pairReader));\n }\n return outArr;\n }\n // {{/if:stackReadsLispListOf}}\n\n // {{if:stackReadsSnakeString}}\n readSnakeString(): string {\n return this.readCell().beginParse().loadStringTail();\n }\n // {{/if:stackReadsSnakeString}}\n\n // {{if:stackReadsTuple}}\n readTuple<T>(expectedN: number, readFn_T: (nestedReader: StackReader) => T): T {\n const subItems = this.popExpecting<c.Tuple>('tuple').items;\n if (subItems.length !== expectedN) {\n throw new Error(`expected ${expectedN} items in a tuple, got ${subItems.length}`);\n }\n return readFn_T(new StackReader(subItems));\n }\n // {{/if:stackReadsTuple}}\n\n // {{if:stackReadsNullLiteral}}\n readNullLiteral(): null {\n this.popExpecting<c.TupleItemNull>('null');\n return null;\n }\n // {{/if:stackReadsNullLiteral}}\n\n // {{if:stackReadsNullable}}\n readNullable<T>(readFn_T: (r: StackReader) => T): T | null {\n if (this.tuple[0].type === 'null') {\n this.tuple.shift();\n return null;\n }\n return readFn_T(this);\n }\n // {{/if:stackReadsNullable}}\n\n // {{if:stackReadsWideNullable}}\n readWideNullable<T>(stackW: number, readFn_T: (r: StackReader) => T): T | null {\n const slotTypeId = this.tuple[stackW - 1];\n if (slotTypeId?.type !== 'int') {\n throw new Error(`not 'int' on a stack`);\n }\n if (slotTypeId.value === 0n) {\n this.tuple = this.tuple.slice(stackW);\n return null;\n }\n const valueT = readFn_T(this);\n this.tuple.shift();\n return valueT;\n }\n // {{/if:stackReadsWideNullable}}\n\n // {{if:stackReadsUnionType}}\n readUnionType<T>(stackW: number, infoForTypeId: Record<number, [number, string | null, (r: StackReader) => any]>): T {\n const slotTypeId = this.tuple[stackW - 1];\n if (slotTypeId?.type !== 'int') {\n throw new Error(`not 'int' on a stack`);\n }\n const info = infoForTypeId[Number(slotTypeId.value)]; // [stackWidth, label, readFn_T{i}]\n if (info == null) {\n throw new Error(`unexpected UTag=${slotTypeId.value}`);\n }\n const label = info[1];\n this.tuple = this.tuple.slice(stackW - 1 - info[0]);\n const valueT = info[2](this);\n this.tuple.shift();\n return label == null ? valueT : { $: label, value: valueT } as T;\n }\n // {{/if:stackReadsUnionType}}\n\n // {{if:stackReadsCellRef}}\n readCellRef<T>(loadFn_T: LoadCallback<T>): CellRef<T> {\n return { ref: loadFn_T(this.readCell().beginParse()) };\n }\n // {{/if:stackReadsCellRef}}\n\n // {{if:stackReadsMapKV}}\n readDictionary<K extends c.DictionaryKeyTypes, V>(keySerializer: c.DictionaryKey<K>, valueSerializer: c.DictionaryValue<V>): c.Dictionary<K, V> {\n if (this.tuple[0].type === 'null') {\n this.tuple.shift();\n return c.Dictionary.empty<K, V>(keySerializer, valueSerializer);\n }\n return c.Dictionary.loadDirect<K, V>(keySerializer, valueSerializer, this.readCell());\n }\n // {{/if:stackReadsMapKV}}\n}\n\n// {{if:has_customPackUnpack}}\n// \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\n// custom packToBuilder and unpackFromSlice\n//\n\ntype CustomPackToBuilderFn<T> = (self: T, b: c.Builder) => void\ntype CustomUnpackFromSliceFn<T> = (s: c.Slice) => T\n\nlet customSerializersRegistry: Map<string, [CustomPackToBuilderFn<any> | null, CustomUnpackFromSliceFn<any> | null]> = new Map;\n\nfunction ensureCustomSerializerRegistered(typeName: string) {\n if (!customSerializersRegistry.has(typeName)) {\n throw new Error(`Custom packToBuilder/unpackFromSlice was not registered for type 'CONTRACT_CLASS_NAME.${typeName}'.\\n(in Tolk code, they have custom logic \\`fun ${typeName}__packToBuilder\\`)\\nSteps to fix:\\n1) in your code, create and implement\\n > function ${typeName}__packToBuilder(self: ${typeName}, b: Builder): void { ... }\\n > function ${typeName}__unpackFromSlice(s: Slice): ${typeName} { ... }\\n2) register them in advance by calling\\n > CONTRACT_CLASS_NAME.registerCustomPackUnpack('${typeName}', ${typeName}__packToBuilder, ${typeName}__unpackFromSlice);`);\n }\n}\n\nfunction invokeCustomPackToBuilder<T>(typeName: string, self: T, b: c.Builder) {\n ensureCustomSerializerRegistered(typeName);\n customSerializersRegistry.get(typeName)![0]!(self, b);\n}\n\nfunction invokeCustomUnpackFromSlice<T>(typeName: string, s: c.Slice): T {\n ensureCustomSerializerRegistered(typeName);\n return customSerializersRegistry.get(typeName)![1]!(s);\n}\n// {{/if:has_customPackUnpack}}\n\n// \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\n// auto-generated serializers to/from cells\n//\n\ntype coins = bigint\n\n// {{intNAliases}}\n\n// {{uintNAliases}}\n\n// {{if:has_varIntN}}\n// {{varIntNAliases}}\n// {{/if:has_varIntN}}\n\n// {{if:has_bitsN}}\n// {{sliceAliases}}\n// {{/if:has_bitsN}}\n\n// {{packUnpackSerializers}}\n\n// \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\n// class CONTRACT_CLASS_NAME\n//\n\ninterface ExtraSendOptions {\n bounce?: boolean // default: false\n sendMode?: SendMode // default: SendMode.PAY_GAS_SEPARATELY\n extraCurrencies?: c.ExtraCurrency // default: empty dict\n}\n\ninterface DeployedAddrOptions {\n workchain?: number // default: 0 (basechain)\n toShard?: { fixedPrefixLength: number; closeTo: c.Address }\n overrideContractCode?: c.Cell\n}\n\nfunction calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedAddrOptions): c.Address {\n const stateInitCell = beginCell().store(c.storeStateInit({\n code,\n data,\n splitDepth: options.toShard?.fixedPrefixLength,\n special: null, // todo will somebody need special?\n libraries: null, // todo will somebody need libraries?\n })).endCell();\n\n let addrHash = stateInitCell.hash();\n if (options.toShard) {\n const shardDepth = options.toShard.fixedPrefixLength;\n addrHash = beginCell() // todo any way to do it better? N bits from closeTo + 256-N from stateInitCell\n .storeBits(new c.BitString(options.toShard.closeTo.hash, 0, shardDepth))\n .storeBits(new c.BitString(stateInitCell.hash(), shardDepth, 256 - shardDepth))\n .endCell()\n .beginParse().loadBuffer(32);\n }\n\n return new c.Address(options.workchain ?? 0, addrHash);\n}\n\nexport class CONTRACT_CLASS_NAME implements c.Contract {\n static CodeCell = c.Cell.fromBase64('{{codeBoc64}}');\n\n static Errors = {\n // {{errorCodes}}\n }\n\n readonly address: c.Address\n readonly init?: { code: c.Cell, data: c.Cell }\n\n private constructor(address: c.Address, init?: { code: c.Cell, data: c.Cell }) {\n this.address = address;\n this.init = init;\n }\n\n // {{if:has_customPackUnpack}}\n static registerCustomPackUnpack<T>(\n typeName: string,\n packToBuilderFn: CustomPackToBuilderFn<T> | null,\n unpackFromSliceFn: CustomUnpackFromSliceFn<T> | null,\n ) {\n if (customSerializersRegistry.has(typeName)) {\n throw new Error(`Custom pack/unpack for 'CONTRACT_CLASS_NAME.${typeName}' already registered`);\n }\n customSerializersRegistry.set(typeName, [packToBuilderFn, unpackFromSliceFn]);\n }\n // {{/if:has_customPackUnpack}}\n\n static fromAddress(address: c.Address) {\n return new CONTRACT_CLASS_NAME(address);\n }\n\n // {{fromStorageMethod}}\n\n // {{createCellsMethods}}\n\n // {{if:has_sendDeploy}}\n async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) {\n return provider.internal(via, {\n value: msgValue,\n body: c.Cell.EMPTY,\n ...extraOptions\n });\n }\n // {{/if:has_sendDeploy}}\n\n // {{sendMethods}}\n\n // {{getMethods}}\n}\n";