@opcat-labs/scrypt-ts-transpiler-opcat 1.0.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 (53) hide show
  1. package/dist/compile/compilerWrapper.d.ts +203 -0
  2. package/dist/compile/compilerWrapper.d.ts.map +1 -0
  3. package/dist/compile/compilerWrapper.js +1019 -0
  4. package/dist/compile/compilerWrapper.js.map +1 -0
  5. package/dist/compile/findCompiler.d.ts +3 -0
  6. package/dist/compile/findCompiler.d.ts.map +1 -0
  7. package/dist/compile/findCompiler.js +102 -0
  8. package/dist/compile/findCompiler.js.map +1 -0
  9. package/dist/compile/getBinary.d.ts +3 -0
  10. package/dist/compile/getBinary.d.ts.map +1 -0
  11. package/dist/compile/getBinary.js +94 -0
  12. package/dist/compile/getBinary.js.map +1 -0
  13. package/dist/debug.d.ts +25 -0
  14. package/dist/debug.d.ts.map +1 -0
  15. package/dist/debug.js +110 -0
  16. package/dist/debug.js.map +1 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +110 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/indexer.d.ts +52 -0
  22. package/dist/indexer.d.ts.map +1 -0
  23. package/dist/indexer.js +189 -0
  24. package/dist/indexer.js.map +1 -0
  25. package/dist/relinker.d.ts +44 -0
  26. package/dist/relinker.d.ts.map +1 -0
  27. package/dist/relinker.js +321 -0
  28. package/dist/relinker.js.map +1 -0
  29. package/dist/resolver.d.ts +3 -0
  30. package/dist/resolver.d.ts.map +1 -0
  31. package/dist/resolver.js +280 -0
  32. package/dist/resolver.js.map +1 -0
  33. package/dist/scryptParser.d.ts +31 -0
  34. package/dist/scryptParser.d.ts.map +1 -0
  35. package/dist/scryptParser.js +372 -0
  36. package/dist/scryptParser.js.map +1 -0
  37. package/dist/snippets.d.ts +39 -0
  38. package/dist/snippets.d.ts.map +1 -0
  39. package/dist/snippets.js +54 -0
  40. package/dist/snippets.js.map +1 -0
  41. package/dist/transpiler.d.ts +314 -0
  42. package/dist/transpiler.d.ts.map +1 -0
  43. package/dist/transpiler.js +4239 -0
  44. package/dist/transpiler.js.map +1 -0
  45. package/dist/types.d.ts +33 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +31 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/utils.d.ts +23 -0
  50. package/dist/utils.d.ts.map +1 -0
  51. package/dist/utils.js +352 -0
  52. package/dist/utils.js.map +1 -0
  53. package/package.json +43 -0
@@ -0,0 +1,4239 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Transpiler = void 0;
30
+ const typescript_1 = __importDefault(require("typescript"));
31
+ const tsquery_1 = require("@phenomnomnominal/tsquery");
32
+ const indexer_1 = require("./indexer");
33
+ const path = __importStar(require("path"));
34
+ const utils_1 = require("./utils");
35
+ const compilerWrapper_1 = require("./compile/compilerWrapper");
36
+ const findCompiler_1 = require("./compile/findCompiler");
37
+ const os_1 = require("os");
38
+ const fs_1 = require("fs");
39
+ const types_1 = require("./types");
40
+ const snippets_1 = require("./snippets");
41
+ const relinker_1 = require("./relinker");
42
+ const BUILDIN_PACKAGE_NAME = '@opcat-labs/scrypt-ts-opcat';
43
+ /**
44
+ * @ignore
45
+ */
46
+ const DEFAULT_AST_COMPILE_OPTS = Object.freeze({
47
+ ast: true,
48
+ asm: false,
49
+ hex: false,
50
+ debug: false,
51
+ artifact: false,
52
+ sourceMap: false,
53
+ outputToFiles: true,
54
+ cmdArgs: '--std',
55
+ });
56
+ /**
57
+ * @ignore
58
+ */
59
+ var DecoratorName;
60
+ (function (DecoratorName) {
61
+ DecoratorName["Prop"] = "prop";
62
+ DecoratorName["Method"] = "method";
63
+ })(DecoratorName || (DecoratorName = {}));
64
+ /**
65
+ * @ignore
66
+ */
67
+ class EmittedLine {
68
+ constructor(prefixTabs = 0, currentCol = 0, codeLines = '', sourceMap = []) {
69
+ this.prefixTabs = prefixTabs;
70
+ this.currentCol = currentCol;
71
+ this.code = codeLines;
72
+ this.sourceMap = sourceMap;
73
+ }
74
+ copy() {
75
+ return new EmittedLine(this.prefixTabs, this.currentCol, this.code, JSON.parse(JSON.stringify(this.sourceMap)));
76
+ }
77
+ findByCol(col) {
78
+ for (let i = 0; i < this.sourceMap.length; i++) {
79
+ const sourceMap = this.sourceMap[i];
80
+ if (col >= sourceMap[0]) {
81
+ const nextSourceMap = this.sourceMap[i + 1];
82
+ if (nextSourceMap && col <= nextSourceMap[0]) {
83
+ return sourceMap;
84
+ }
85
+ else if (!nextSourceMap) {
86
+ return sourceMap;
87
+ }
88
+ }
89
+ }
90
+ return this.sourceMap[0];
91
+ }
92
+ }
93
+ /**
94
+ * @ignore
95
+ */
96
+ class EmittedSection {
97
+ constructor(initialLine) {
98
+ this.lines = [];
99
+ this.errors = [];
100
+ if (initialLine)
101
+ this.lines.push(initialLine);
102
+ this.skipNextAppend = false;
103
+ }
104
+ static join(...sections) {
105
+ const completed = new EmittedSection();
106
+ sections.forEach((sec) => completed.concat(sec));
107
+ return completed;
108
+ }
109
+ getLastLine() {
110
+ return this.lines[this.lines.length - 1];
111
+ }
112
+ getCode() {
113
+ return this.lines.map((l) => l.code).join('\n');
114
+ }
115
+ getSourceMap() {
116
+ return this.lines.map((l) => l.sourceMap);
117
+ }
118
+ append(code, srcLocation) {
119
+ if (this.skipNextAppend) {
120
+ this.skipNextAppend = false;
121
+ return this;
122
+ }
123
+ let lastLine = this.getLastLine();
124
+ const inlineCode = code.replaceAll(/\n/g, '');
125
+ const startNewLine = code.startsWith('\n');
126
+ if (startNewLine || !lastLine) {
127
+ const prefixTab = lastLine?.prefixTabs ?? 0;
128
+ lastLine = new EmittedLine(prefixTab, prefixTab * 2, Array(prefixTab * 2)
129
+ .fill(' ')
130
+ .join('') + inlineCode);
131
+ this.lines.push(lastLine);
132
+ }
133
+ else {
134
+ lastLine.code += inlineCode;
135
+ }
136
+ if (srcLocation) {
137
+ // adjust col to skip prefix spaces
138
+ const skipSpaceCol = inlineCode.search(/[^ ]/);
139
+ const adjustCol = lastLine.currentCol + (skipSpaceCol > 0 ? skipSpaceCol : 0);
140
+ // sourcemap format: [ generatedCodeColumn, sourceIndex, sourceCodeLine, sourceCodeColumn, nameIndex? ]
141
+ lastLine.sourceMap.push([adjustCol, 0, srcLocation.line, srcLocation.character]);
142
+ }
143
+ lastLine.currentCol += inlineCode.length;
144
+ return this;
145
+ }
146
+ appendWith(ctx, updater, increaseTab = false) {
147
+ if (this.skipNextAppend) {
148
+ this.skipNextAppend = false;
149
+ return this;
150
+ }
151
+ try {
152
+ // use the last line of current section as the start line of upcoming section
153
+ const initialLine = (this.getLastLine() || new EmittedLine()).copy();
154
+ if (increaseTab) {
155
+ initialLine.prefixTabs += 1;
156
+ }
157
+ this.concat(updater.call(ctx, new EmittedSection(initialLine)), true);
158
+ if (increaseTab) {
159
+ this.getLastLine().prefixTabs -= 1;
160
+ }
161
+ }
162
+ catch (err) {
163
+ if (err instanceof types_1.TranspileError) {
164
+ this.errors.push(err);
165
+ }
166
+ else {
167
+ throw err;
168
+ }
169
+ }
170
+ return this;
171
+ }
172
+ concat(postSection, lastLineOverlays = false) {
173
+ if (lastLineOverlays)
174
+ this.lines.pop();
175
+ this.lines = this.lines.concat(postSection.lines);
176
+ this.errors = this.errors.concat(postSection.errors);
177
+ return this;
178
+ }
179
+ }
180
+ const SmartContractBuiltinMethods = [
181
+ 'buildChangeOutput',
182
+ 'appendStateOutput',
183
+ 'buildStateOutputs',
184
+ 'relTimeLock',
185
+ 'absTimeLock',
186
+ 'checkSig',
187
+ 'checkMultiSig',
188
+ 'checkPreimageAdvanced',
189
+ 'checkPreimageSigHashType',
190
+ 'checkPreimage',
191
+ 'insertCodeSeparator',
192
+ 'checkInputState',
193
+ 'backtraceToOutpoint',
194
+ 'backtraceToScript',
195
+ 'checkOutputs',
196
+ ];
197
+ /**
198
+ * @ignore
199
+ */
200
+ class Transpiler {
201
+ constructor(sourceFile, host, checker, tsRootDir, scryptOutDir, indexer, compilerOptions) {
202
+ this.scComponents = [];
203
+ // public publicMethods: ts.MethodDeclaration[] = [];
204
+ this.methodInfos = new Map();
205
+ this.propInfos = new Map();
206
+ this._localTypeSymbols = new Map();
207
+ this._accessBuiltinsSymbols = new Set();
208
+ this._importedTypeSymbols = new Map();
209
+ this._stateTypeSymbols = new Map();
210
+ this._watch = false;
211
+ this._constructorParametersMap = new Map();
212
+ this._srcFile = sourceFile;
213
+ this._host = host;
214
+ this._checker = checker;
215
+ this._tsRootDir = tsRootDir;
216
+ this._scryptOutDir = scryptOutDir;
217
+ this._indexer = indexer;
218
+ this._compilerOptions = compilerOptions;
219
+ this._watch = compilerOptions.watch || false;
220
+ this.loadBuiltinIndexer();
221
+ this.searchSmartContractComponents();
222
+ this.searchTopCtcs();
223
+ }
224
+ get ctxMethods() {
225
+ return this.getCtxMethodInfos().map((info) => info.name);
226
+ }
227
+ get _scryptRelativePath() {
228
+ return this.getRelativePathFromTsRoot(this._srcFile.fileName);
229
+ }
230
+ get _scryptFullPath() {
231
+ return path.join(this._scryptOutDir, this._scryptRelativePath);
232
+ }
233
+ get currentContractName() {
234
+ return this._currentContract.name.getText();
235
+ }
236
+ get currentbaseContractName() {
237
+ return Transpiler.getBaseContractName(this._currentContract).baseContractName;
238
+ }
239
+ get currentbaseContract() {
240
+ return Transpiler.contractAst.get(this.currentbaseContractName);
241
+ }
242
+ get _transformationResultRelativePath() {
243
+ return (0, utils_1.alterFileExt)(this._scryptRelativePath, 'transformer.json', Transpiler.scryptFileExt);
244
+ }
245
+ get _transformationResultFullPath() {
246
+ return (0, utils_1.alterFileExt)(this._scryptFullPath, 'transformer.json', Transpiler.scryptFileExt);
247
+ }
248
+ get _sourceMapRelativePath() {
249
+ return (0, utils_1.alterFileExt)(this._scryptRelativePath, 'scrypt.map', Transpiler.scryptFileExt);
250
+ }
251
+ get _sourceMapFullPath() {
252
+ return (0, utils_1.alterFileExt)(this._scryptFullPath, 'scrypt.map', Transpiler.scryptFileExt);
253
+ }
254
+ setLocalSymbols(localTypeSymbols) {
255
+ this._localTypeSymbols = localTypeSymbols;
256
+ }
257
+ transform(allmissSym, relinker) {
258
+ console.log(`transform ${this._srcFile.fileName} to ${this._scryptFullPath}`);
259
+ // transform the contract, library, structs and find its imported symbols
260
+ const contractAndLibs = this.scComponents.map((cDef) => this.transformClassDeclaration(cDef));
261
+ const structs = this.transformTypeLiteralAndInterfaces();
262
+ // transform the import expressions
263
+ const imports = this.transformImports(allmissSym, relinker);
264
+ const result = EmittedSection.join(imports, structs, ...contractAndLibs);
265
+ if (this._watch) {
266
+ setTimeout(() => {
267
+ this.diagnose(result.errors);
268
+ }, 500);
269
+ }
270
+ else {
271
+ this.outputScrypt(result);
272
+ if (result.errors.length == 0) {
273
+ try {
274
+ relinker.relink(this._scryptFullPath);
275
+ }
276
+ catch (_err) {
277
+ console.log('relink failed', this._scryptFullPath);
278
+ console.log(_err);
279
+ }
280
+ }
281
+ this.updateIndex();
282
+ this.diagnose(result.errors);
283
+ this.outputSourceMapFile(result);
284
+ this.outputTransformationResult(result);
285
+ }
286
+ return result;
287
+ }
288
+ isTransformable() {
289
+ return this.scComponents.length > 0;
290
+ }
291
+ getSCComponents() {
292
+ return this.scComponents;
293
+ }
294
+ isFromThirdParty(filepath) {
295
+ return filepath.endsWith('.d.ts');
296
+ }
297
+ outputScrypt(result) {
298
+ typescript_1.default.sys.writeFile(this._scryptFullPath, result.getCode());
299
+ }
300
+ outputTransformationResult(result) {
301
+ const transResult = {
302
+ success: result.errors.length === 0,
303
+ errors: result.errors,
304
+ scryptfile: this._scryptRelativePath,
305
+ sourceMapFile: this._sourceMapRelativePath,
306
+ ctxMethods: this.ctxMethods,
307
+ };
308
+ typescript_1.default.sys.writeFile(this._transformationResultFullPath, JSON.stringify(transResult, null, 2));
309
+ }
310
+ diagnose(errors) {
311
+ errors.forEach((error) => this.outputDiagnostic(error));
312
+ }
313
+ outputDiagnostic(diag) {
314
+ console.log(`${BUILDIN_PACKAGE_NAME} ERROR - ${diag.srcRange.fileName}:${diag.srcRange.start.line}:${diag.srcRange.start.character}:${diag.srcRange.end.line}:${diag.srcRange.end.character} - ${diag.message}\n`);
315
+ }
316
+ // check transformed scrypt code to collect compile time errors
317
+ checkTransformedScrypt(result) {
318
+ if (result.errors.length === 0 && (0, fs_1.existsSync)(this._scryptFullPath)) {
319
+ try {
320
+ const tmpDir = (0, fs_1.mkdtempSync)(path.join((0, os_1.tmpdir)(), 'scrypt-ts-btc'));
321
+ const input = {
322
+ path: this._scryptFullPath,
323
+ content: result.getCode(),
324
+ };
325
+ const settings = Object.assign({}, DEFAULT_AST_COMPILE_OPTS, {
326
+ cmdPrefix: (0, findCompiler_1.findCompiler)(),
327
+ outputDir: tmpDir,
328
+ });
329
+ const astResult = (0, compilerWrapper_1.compile)(input, settings);
330
+ const errors = this.compileError2transpileError(astResult.errors, result.lines);
331
+ return errors;
332
+ }
333
+ catch (err) {
334
+ // for the case when scrypt compiler crushed.
335
+ return [
336
+ new types_1.TranspileError(`Internal compilation FATAL error raised for auto-generated file: ${this._scryptFullPath} , please report it as a bug to ${BUILDIN_PACKAGE_NAME}\n${err}`, {
337
+ fileName: this._scryptFullPath,
338
+ start: { line: -1, character: -1 },
339
+ end: { line: -1, character: -1 },
340
+ }),
341
+ ];
342
+ }
343
+ }
344
+ return [];
345
+ }
346
+ compileError2transpileError(errors, scryptLines) {
347
+ const tsSrcLines = this._srcFile.getFullText().split('\n');
348
+ return errors.map((error) => {
349
+ const scryptLine = scryptLines[(error.position[0]?.line || 0) - 1];
350
+ const sourcemap = scryptLine
351
+ ? scryptLine.findByCol(error.position[0]?.column || -1)
352
+ : undefined;
353
+ if (sourcemap) {
354
+ const tsLine = sourcemap[2];
355
+ const tsColumn = sourcemap[3];
356
+ // code from current column to end of line
357
+ const tsCode = tsSrcLines[tsLine]
358
+ .slice(tsColumn)
359
+ .replace(/\/\/.*/, '')
360
+ .trim();
361
+ return new types_1.TranspileError(`The code '${tsCode}' was successfully transformed but compiled with error: ${error.message}`, {
362
+ fileName: this._srcFile.fileName,
363
+ start: { line: tsLine, character: tsColumn },
364
+ end: { line: tsLine, character: tsColumn + tsCode.length },
365
+ });
366
+ }
367
+ else {
368
+ // when can not find sourcemap for the certain `scryptLine`, use the first sourcemap
369
+ const sourcemap = scryptLines[1]?.sourceMap[0];
370
+ return new types_1.TranspileError(`Internal compilation error raised for auto-generated file: ${this._scryptFullPath}:${error.position[0]?.line || -1}:${error.position[0]?.column || -1} , please report it as a bug to ${BUILDIN_PACKAGE_NAME}`, {
371
+ fileName: this._srcFile.fileName,
372
+ start: {
373
+ line: sourcemap ? sourcemap[2] : -1,
374
+ character: sourcemap ? sourcemap[3] : -1,
375
+ },
376
+ end: { line: -1, character: -1 },
377
+ });
378
+ }
379
+ });
380
+ }
381
+ outputSourceMapFile(result) {
382
+ typescript_1.default.sys.writeFile(this._sourceMapFullPath, JSON.stringify(result.getSourceMap()));
383
+ }
384
+ updateIndex() {
385
+ const globalSymbols = Array.from(this._localTypeSymbols.entries()).map(([symbolName, symbol]) => {
386
+ const symbolFile = this.findDeclarationFile(symbol);
387
+ let symbolDec = symbol.declarations[0];
388
+ symbolDec = symbolDec['name'] || symbolDec;
389
+ const stateTypeSymbol = this._stateTypeSymbols.get(symbolName);
390
+ return {
391
+ name: symbolName,
392
+ srcRange: {
393
+ fileName: symbolFile.fileName,
394
+ start: symbolFile.getLineAndCharacterOfPosition(symbolDec.getStart()),
395
+ end: symbolFile.getLineAndCharacterOfPosition(symbolDec.getEnd()),
396
+ },
397
+ stateType: stateTypeSymbol?.name,
398
+ };
399
+ });
400
+ this._indexer.addSymbols(globalSymbols, this._scryptRelativePath);
401
+ }
402
+ isExtendsSCComponent(node) {
403
+ let isExtends = false;
404
+ if (this.isContract(node)) {
405
+ Transpiler.contractAst.set(node.name.getText(), node);
406
+ if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.AbstractKeyword)) {
407
+ // ignore abstract class, then are base contract class
408
+ return false;
409
+ }
410
+ isExtends = true;
411
+ }
412
+ if (this.isLibrary(node)) {
413
+ isExtends = true;
414
+ }
415
+ if (!isExtends) {
416
+ return false;
417
+ }
418
+ const typeParameterNames = (node.typeParameters || []).map((v) => v.name.getText());
419
+ const { typeArgName: stateTypeName, stateTypeNode } = Transpiler.getBaseContractName(node);
420
+ if (stateTypeName) {
421
+ // exclude the case like: `class ContractA<T> extends Base<T>`
422
+ if (typeParameterNames.includes(stateTypeName)) {
423
+ return false;
424
+ }
425
+ if (typescript_1.default.isTypeReferenceNode(stateTypeNode)) {
426
+ const typeRef = this._checker.getSymbolAtLocation(stateTypeNode.typeName);
427
+ if (typeRef) {
428
+ this._stateTypeSymbols.set(node.name.getText(), typeRef.flags & typescript_1.default.SymbolFlags.Alias
429
+ ? this._checker.getAliasedSymbol(typeRef)
430
+ : typeRef);
431
+ }
432
+ }
433
+ else {
434
+ // TODO: add support for type literal like: `SmartContract<{ state: number }>`
435
+ // currently, do not support type literal state type, and throw error in `checkLiteralStateType`
436
+ }
437
+ }
438
+ return true;
439
+ }
440
+ isContract(node) {
441
+ if (Array.isArray(node.heritageClauses) && node.heritageClauses.length === 1) {
442
+ const clause = node.heritageClauses[0];
443
+ if (clause.token == typescript_1.default.SyntaxKind.ExtendsKeyword && clause.types.length === 1) {
444
+ const { baseContractName } = Transpiler.getBaseContractName(node);
445
+ return baseContractName === 'SmartContract' || Transpiler.contractAst.has(baseContractName);
446
+ }
447
+ }
448
+ return false;
449
+ }
450
+ checkLiteralStateType() {
451
+ const node = this._currentContract;
452
+ if (Array.isArray(node.heritageClauses) && node.heritageClauses.length === 1) {
453
+ const clause = node.heritageClauses[0];
454
+ if (clause.token === typescript_1.default.SyntaxKind.ExtendsKeyword && clause.types.length === 1) {
455
+ const type = clause.types[0];
456
+ if (type.kind === typescript_1.default.SyntaxKind.ExpressionWithTypeArguments) {
457
+ const stateTypeNode = type.typeArguments?.length > 0 ? type.typeArguments[0] : undefined;
458
+ if (stateTypeNode && typescript_1.default.isTypeLiteralNode(stateTypeNode)) {
459
+ throw new types_1.TranspileError(`State type of the contract \`${node.name.getText()}\` should be a TypeReference instead of a ${typescript_1.default.SyntaxKind[stateTypeNode.kind]}`, this.getRange(stateTypeNode));
460
+ }
461
+ }
462
+ }
463
+ }
464
+ }
465
+ static getBaseContractName(node) {
466
+ if (Array.isArray(node.heritageClauses) && node.heritageClauses.length === 1) {
467
+ const clause = node.heritageClauses[0];
468
+ if (clause.token == typescript_1.default.SyntaxKind.ExtendsKeyword && clause.types.length === 1) {
469
+ const type = clause.types[0];
470
+ if (type.kind === typescript_1.default.SyntaxKind.ExpressionWithTypeArguments) {
471
+ // get base contract node, and parse the chains
472
+ const baseContractName = type.expression.getText();
473
+ // class Child extends Base<State>
474
+ let typeArgName = type.typeArguments?.length > 0 ? type.typeArguments[0].getText() : undefined;
475
+ let stateTypeNode = undefined;
476
+ if (typeArgName) {
477
+ stateTypeNode = node.heritageClauses[0].types[0].typeArguments[0];
478
+ }
479
+ // class Child extends Base
480
+ // class Base extends SmartContract<State>
481
+ if (!typeArgName &&
482
+ baseContractName !== 'SmartContract' &&
483
+ baseContractName !== 'SmartContractLib') {
484
+ const baseContractAst = Transpiler.contractAst.get(baseContractName);
485
+ if (!baseContractAst) {
486
+ return { baseContractName };
487
+ }
488
+ const baseRes = this.getBaseContractName(baseContractAst);
489
+ typeArgName = baseRes.typeArgName;
490
+ stateTypeNode = baseRes.stateTypeNode;
491
+ }
492
+ return {
493
+ baseContractName,
494
+ typeArgName,
495
+ stateTypeNode,
496
+ };
497
+ }
498
+ return {
499
+ baseContractName: type.getText(),
500
+ };
501
+ }
502
+ }
503
+ throw new Error(`Can't get base contract for class ${node.name.getText()}`);
504
+ }
505
+ isInherited(node) {
506
+ if (node.heritageClauses &&
507
+ node.heritageClauses.length === 1 &&
508
+ node.heritageClauses[0].token === typescript_1.default.SyntaxKind.ExtendsKeyword) {
509
+ const { baseContractName } = Transpiler.getBaseContractName(node);
510
+ return Transpiler.contractAst.has(baseContractName);
511
+ }
512
+ return false;
513
+ }
514
+ isTranspilingConstructor(node) {
515
+ if (!node.parent) {
516
+ return false;
517
+ }
518
+ if (typescript_1.default.isConstructorDeclaration(node.parent)) {
519
+ return true;
520
+ }
521
+ return this.isTranspilingConstructor(node.parent);
522
+ }
523
+ isTranspilingBaseContract(node) {
524
+ const cls = Transpiler.getClassDeclaration(node);
525
+ if (cls && cls.name.getText() !== this.currentContractName) {
526
+ return true;
527
+ }
528
+ return false;
529
+ }
530
+ isLibrary(node) {
531
+ if (node.heritageClauses &&
532
+ node.heritageClauses.length === 1 &&
533
+ node?.heritageClauses[0].token === typescript_1.default.SyntaxKind.ExtendsKeyword) {
534
+ const parentClassName = node?.heritageClauses[0].types[0]?.getText();
535
+ return parentClassName === 'SmartContractLib' || /^StateLib<.+>$/.test(parentClassName);
536
+ }
537
+ return false;
538
+ }
539
+ /**
540
+ * get relative path starting from `tsRootDir`
541
+ * @param fullFilePath
542
+ * @param ext extension of the path
543
+ * @returns
544
+ */
545
+ getRelativePathFromTsRoot(fullFilePath, ext = Transpiler.scryptFileExt) {
546
+ // ts source file to root dir relative path which will be kept in scrypt output structure.
547
+ let root2srcRelativePath = path.relative(this._tsRootDir, fullFilePath).replaceAll('\\', '/');
548
+ if (root2srcRelativePath.startsWith('src/contracts/')) {
549
+ root2srcRelativePath = root2srcRelativePath.replace('src/contracts/', '');
550
+ }
551
+ if (root2srcRelativePath.startsWith('../')) {
552
+ root2srcRelativePath = root2srcRelativePath.replace('../', '');
553
+ }
554
+ const basename = path.basename(root2srcRelativePath, path.extname(root2srcRelativePath));
555
+ return path
556
+ .join(path.dirname(root2srcRelativePath), `${basename}.${ext}`)
557
+ .replaceAll('\\', '/');
558
+ }
559
+ getRelativePathFromArtifacts(fullFilePath, ext = Transpiler.scryptFileExt) {
560
+ // ts source file to root dir relative path which will be kept in scrypt output structure.
561
+ const relativePath = path.relative(this._scryptOutDir, fullFilePath).replaceAll('\\', '/');
562
+ const basename = path.basename(relativePath, path.extname(relativePath));
563
+ return path.join(path.dirname(relativePath), `${basename}.${ext}`).replaceAll('\\', '/');
564
+ }
565
+ searchSmartContractComponents() {
566
+ const scComponentDefs = (0, tsquery_1.tsquery)(this._srcFile, 'ClassDeclaration:has(HeritageClause)');
567
+ const scComponents = scComponentDefs.filter((cDef) => typescript_1.default.isClassDeclaration(cDef) && this.isExtendsSCComponent(cDef));
568
+ this.scComponents = scComponents;
569
+ }
570
+ searchTopCtcs() {
571
+ const declarations = (0, tsquery_1.tsquery)(this._srcFile, 'SourceFile > VariableStatement > VariableDeclarationList');
572
+ const ctcDeclarations = declarations.filter((d) => {
573
+ if (typescript_1.default.isVariableDeclarationList(d)) {
574
+ return this.isCtcDeclaration(d.declarations[0]);
575
+ }
576
+ return false;
577
+ });
578
+ ctcDeclarations.forEach((d) => {
579
+ Transpiler.topCtcs.set(`${(0, utils_1.sha1)(this._srcFile.fileName)}:${d.declarations[0].name.getText()}`, this.evalCtcExpression(d.declarations[0].initializer));
580
+ });
581
+ }
582
+ getCoordinates(pos) {
583
+ return pos ? this._srcFile.getLineAndCharacterOfPosition(pos) : undefined;
584
+ }
585
+ getRange(node) {
586
+ return {
587
+ fileName: this._srcFile.fileName,
588
+ start: this.getCoordinates(node.getStart()),
589
+ end: this.getCoordinates(node.getEnd()),
590
+ };
591
+ }
592
+ getResolvedType(node) {
593
+ const symbol = this._checker.getSymbolAtLocation(node);
594
+ return symbol
595
+ ? this._checker.getTypeOfSymbolAtLocation(symbol, symbol.declarations[0])
596
+ : undefined;
597
+ }
598
+ findDeclarationFile(symbol) {
599
+ let node = symbol.declarations[0];
600
+ while (node?.parent) {
601
+ node = node.parent;
602
+ }
603
+ return typescript_1.default.isSourceFile(node) ? node : undefined;
604
+ }
605
+ transformProps(section, node) {
606
+ const category = this.isContract(node) ? 'contract' : 'library';
607
+ node.members.forEach((m) => {
608
+ if (m.kind === typescript_1.default.SyntaxKind.PropertyDeclaration) {
609
+ section.appendWith(this, (memSec) => {
610
+ return this.transformPropertyDeclaration(m, memSec, category);
611
+ });
612
+ }
613
+ });
614
+ }
615
+ checkPropsOverride(node, baseContract) {
616
+ const baseContractProps = baseContract.members
617
+ .filter((m) => Transpiler.isProperty(m))
618
+ .map((m) => m.name.getText());
619
+ node.members.forEach((m) => {
620
+ if (Transpiler.isProperty(m)) {
621
+ if (baseContractProps.includes(m.name.getText())) {
622
+ throw new types_1.TranspileError(`Untransformable property: '${m.name.getText()}', already defined in base contract '${baseContract.name.getText()}'`, this.getRange(node));
623
+ }
624
+ }
625
+ });
626
+ }
627
+ checkMethodsOverride(node, baseContract) {
628
+ const baseContractMethods = baseContract.members
629
+ .filter((m) => Transpiler.isMethod(m))
630
+ .map((m) => m.name.getText());
631
+ node.members.forEach((m) => {
632
+ if (Transpiler.isMethod(m)) {
633
+ if (baseContractMethods.includes(m.name.getText())) {
634
+ throw new types_1.TranspileError(`Untransformable method: '${m.name.getText()}', already defined in base contract '${baseContract.name.getText()}'`, this.getRange(node));
635
+ }
636
+ }
637
+ });
638
+ }
639
+ transformMethods(section, node) {
640
+ const category = this.isContract(node) ? 'contract' : 'library';
641
+ node.members.forEach((m) => {
642
+ if (m.kind === typescript_1.default.SyntaxKind.MethodDeclaration) {
643
+ section.appendWith(this, (memSec) => {
644
+ return this.transformMethodDeclaration(m, memSec, category);
645
+ });
646
+ }
647
+ });
648
+ }
649
+ transformClassDeclaration(node) {
650
+ const className = node.name.getText();
651
+ this._currentContract = node;
652
+ // also add it to local type symbols
653
+ this._localTypeSymbols.set(className, this._checker.getSymbolAtLocation(node.name));
654
+ const stateTypeSymbol = this._stateTypeSymbols.get(className);
655
+ if (stateTypeSymbol) {
656
+ if (stateTypeSymbol.declarations[0].getSourceFile() === node.getSourceFile()) {
657
+ this._localTypeSymbols.set(stateTypeSymbol.name, stateTypeSymbol);
658
+ }
659
+ else {
660
+ this._importedTypeSymbols.set(stateTypeSymbol.name, stateTypeSymbol);
661
+ }
662
+ }
663
+ const category = this.isContract(node) ? 'contract' : 'library';
664
+ const baseContract = this.currentbaseContract;
665
+ // Preprocess all methods
666
+ const section = new EmittedSection();
667
+ try {
668
+ this.checkLiteralStateType();
669
+ this.initAllPropInfos();
670
+ this.initAllMethodInfos();
671
+ this.injectScryptStructs(section);
672
+ section
673
+ .append('\n')
674
+ .append(`\n${category} `)
675
+ .append(`${className} {`, this.getCoordinates(node.name.getStart()))
676
+ .appendWith(this, (membersSection) => {
677
+ if (baseContract) {
678
+ this.checkPropsOverride(node, baseContract);
679
+ this.transformProps(membersSection, baseContract);
680
+ }
681
+ this.transformProps(membersSection, node);
682
+ this.injectScryptProps(membersSection);
683
+ this.transformConstructor(membersSection, node, baseContract);
684
+ if (baseContract) {
685
+ this.checkMethodsOverride(node, baseContract);
686
+ this.transformMethods(membersSection, baseContract);
687
+ }
688
+ this.transformMethods(membersSection, node);
689
+ if (this.isStateful()) {
690
+ this.injectSerializeStateFunc(membersSection);
691
+ }
692
+ // if (this.accessChange()) {
693
+ // membersSection.append(`\n${BUILD_CHANGE_OUTPUT_FUNCTION}`);
694
+ // }
695
+ if (this.getPublicMethodCount() === 0 && category === 'contract') {
696
+ throw new types_1.TranspileError('A `SmartContract` should have at least one public `@method`', this.getRange(node.name));
697
+ }
698
+ return membersSection;
699
+ }, true)
700
+ .append('\n}');
701
+ const methodInfos = Array.from(this.methodInfos, ([key, methodInfo]) => ({ key, methodInfo }))
702
+ .filter((i) => i.key.startsWith(`${className}.`))
703
+ .filter((i) => i.methodInfo.codeSeparatorCount > 0);
704
+ if (methodInfos.length > 1) {
705
+ throw new types_1.TranspileError(`insertCodeSeparator() can only be called by one pulic method`, this.getRange(node));
706
+ }
707
+ }
708
+ catch (error) {
709
+ if (error instanceof types_1.TranspileError) {
710
+ section.errors.push(error);
711
+ }
712
+ else {
713
+ throw error;
714
+ }
715
+ }
716
+ return section;
717
+ }
718
+ injectScryptProps(section) {
719
+ if (this.shouldInjectSHPreimageProp()) {
720
+ section.append(`\nSHPreimage ${snippets_1.InjectedProp_SHPreimage};`);
721
+ }
722
+ if (this.shouldInjectChangeProp()) {
723
+ section.append(`\nTxOut ${snippets_1.InjectedProp_ChangeInfo};`);
724
+ }
725
+ if (this.shouldInjectPrevoutsProp()) {
726
+ section.append(`\nbytes ${snippets_1.InjectedProp_PrevoutsCtx};`);
727
+ }
728
+ if (this.shouldInjectPrevoutProp()) {
729
+ section.append(`\nOutpoint ${snippets_1.InjectedProp_Prevout};`);
730
+ }
731
+ if (this.shouldInjectSpentScriptsProp()) {
732
+ section.append(`\nbytes ${snippets_1.InjectedProp_SpentScriptHashes};`);
733
+ }
734
+ if (this.shouldInjectSpentAmountsProp()) {
735
+ section.append(`\nbytes ${snippets_1.InjectedProp_SpentAmounts};`);
736
+ }
737
+ if (this.shouldInjectSpentDataHashesProp()) {
738
+ section.append(`\nbytes ${snippets_1.InjectedProp_SpentDataHashes};`);
739
+ }
740
+ if (this.shouldInjectPrevTxHashPreimageProp()) {
741
+ section.append(`\nTxHashPreimage ${snippets_1.InjectedProp_PrevTxHashPreimage};`);
742
+ }
743
+ if (this.shouldInjectCurStateProp()) {
744
+ const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
745
+ if (!stateTypeSymbol) {
746
+ throw new Error('State type symbol is not defined');
747
+ }
748
+ section.append(`\n${stateTypeSymbol.name} ${snippets_1.InjectedProp_NextState};`);
749
+ }
750
+ }
751
+ injectScryptStructs(_section) { }
752
+ injectSerializeStateFunc(section) {
753
+ const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
754
+ if (!stateTypeSymbol) {
755
+ throw new Error('State type symbol is not defined');
756
+ }
757
+ const flattenProps = (prop, prefix) => {
758
+ const dec = prop.flags & typescript_1.default.SymbolFlags.Alias
759
+ ? this._checker.getAliasedSymbol(prop).declarations[0]
760
+ : prop.declarations[0];
761
+ let members;
762
+ // interface StateA {count: Int32}; SmartContract<StateA>
763
+ if (typescript_1.default.isInterfaceDeclaration(dec)) {
764
+ members = dec.members;
765
+ }
766
+ // SmartContract<{count: Int32}>
767
+ if (typescript_1.default.isTypeLiteralNode(dec)) {
768
+ members = dec.members;
769
+ }
770
+ // type StateA = {count: Int32}; SmartContract<StateA>
771
+ if (typescript_1.default.isTypeAliasDeclaration(dec) && typescript_1.default.isTypeLiteralNode(dec.type)) {
772
+ members = dec.type.members;
773
+ }
774
+ // state type must be a struct type
775
+ if (!members && prefix === snippets_1.InjectedParam_CurState) {
776
+ throw new Error(`State type must extends StructObject`);
777
+ }
778
+ // is a primitive type, just return
779
+ const type = this._checker.getTypeAtLocation(dec);
780
+ const typeStr = this.type2ResolvedName(type);
781
+ if (allowedTypeStr(typeStr)) {
782
+ return [
783
+ {
784
+ name: prefix,
785
+ type,
786
+ },
787
+ ];
788
+ }
789
+ const flattenFixedArray = (type, prefix) => {
790
+ const len = type.aliasTypeArguments[1];
791
+ if (len.isNumberLiteral()) {
792
+ return Array.from({ length: len.value }).flatMap((_, i) => {
793
+ const elemType = type.aliasTypeArguments[0];
794
+ const typeStr = this.type2ResolvedName(elemType);
795
+ if (elemType.aliasSymbol?.name === 'FixedArray') {
796
+ // fixed array type under fixed array type
797
+ return flattenFixedArray(elemType, `${prefix}[${i}]`);
798
+ }
799
+ else {
800
+ // non-fixed array types under fixed array type
801
+ const symbol = elemType.aliasSymbol || elemType.symbol;
802
+ if (!symbol) {
803
+ if (allowedTypeStr(typeStr)) {
804
+ // compatible with bigint, boolean, string
805
+ return [
806
+ {
807
+ name: `${prefix}[${i}]`,
808
+ type: elemType,
809
+ },
810
+ ];
811
+ }
812
+ else {
813
+ throw new Error(`Unsupported type for prop hash: ${prefix}[${i}]: ${typeStr}`);
814
+ }
815
+ }
816
+ else {
817
+ return flattenProps(symbol, `${prefix}[${i}]`);
818
+ }
819
+ }
820
+ });
821
+ }
822
+ else {
823
+ throw new Error(`Unsupported type for prop hash: ${prefix}[index]: ${typeStr}`);
824
+ }
825
+ };
826
+ if (!members) {
827
+ // not a struct type, throw error
828
+ throw new Error(`Unsupported type for prop hash: ${prop.name}: ${typeStr}`);
829
+ }
830
+ else {
831
+ // is a struct type, do recursive flatten for each member
832
+ return members.flatMap((m) => {
833
+ const k = m.name.getText();
834
+ const type = this._checker.getTypeAtLocation(m);
835
+ const typeStr = this.type2ResolvedName(type);
836
+ if (type.aliasSymbol?.name === 'FixedArray') {
837
+ // fixed array type under struct type
838
+ return flattenFixedArray(type, `${prefix}.${k}`);
839
+ }
840
+ else {
841
+ // non-fixed array types under struct type
842
+ const symbol = type.aliasSymbol || type.symbol;
843
+ if (!symbol) {
844
+ if (allowedTypeStr(typeStr)) {
845
+ // compatible with bigint, boolean, string
846
+ return [
847
+ {
848
+ name: `${prefix}.${k}`,
849
+ type,
850
+ },
851
+ ];
852
+ }
853
+ else {
854
+ throw new Error(`Unsupported type for prop hash: ${prefix}.${k}: ${typeStr}`);
855
+ }
856
+ }
857
+ return flattenProps(symbol, `${prefix}.${k}`);
858
+ }
859
+ });
860
+ }
861
+ };
862
+ function allowedTypeStr(typeStr) {
863
+ try {
864
+ propHash('dummyName', typeStr);
865
+ return true;
866
+ }
867
+ catch (_e) {
868
+ return false;
869
+ }
870
+ }
871
+ function byteStringify(name, typeStr) {
872
+ switch (typeStr) {
873
+ case 'Int32':
874
+ case 'SigHashType':
875
+ case 'bigint':
876
+ return `pack(${name})`;
877
+ case 'Bool':
878
+ case 'boolean':
879
+ return `(${name} ? b'01' : b'')`;
880
+ case 'ByteString':
881
+ case 'Sha256':
882
+ case 'Sha1':
883
+ case 'Sig':
884
+ case 'PubKey':
885
+ case 'Ripemd160':
886
+ case 'OpCodeType':
887
+ case 'string':
888
+ return `${name}`;
889
+ default: {
890
+ throw new Error(`Unsupported type for byteStringify: ${typeStr}`);
891
+ }
892
+ }
893
+ }
894
+ function propHash(name, typeStr) {
895
+ switch (typeStr) {
896
+ case 'Int32':
897
+ case 'SigHashType':
898
+ case 'bigint':
899
+ return `hash160(pack(${name}))`;
900
+ case 'Bool':
901
+ case 'boolean':
902
+ return `hash160(${name} ? b'01' : b'')`;
903
+ case 'ByteString':
904
+ case 'Sha256':
905
+ case 'Sha1':
906
+ case 'Sig':
907
+ case 'PubKey':
908
+ case 'Ripemd160':
909
+ case 'OpCodeType':
910
+ case 'string':
911
+ return `hash160(${name})`;
912
+ default: {
913
+ throw new Error(`Unsupported type for prop hash: ${typeStr}`);
914
+ }
915
+ }
916
+ }
917
+ const hashes = flattenProps(stateTypeSymbol, snippets_1.InjectedParam_CurState).map(({ name, type }) => propHash(name, this.type2ResolvedName(type)));
918
+ const dataPart = flattenProps(stateTypeSymbol, snippets_1.InjectedParam_CurState).map(({ name, type }) => {
919
+ const bytes = byteStringify(name, this.type2ResolvedName(type));
920
+ return `num2bin(len(${bytes}), 2) + ${bytes}`;
921
+ // return `StdUtils.writeVarint(${bytes})`;
922
+ });
923
+ section
924
+ .append('\n')
925
+ .append(`static function serializeState(${stateTypeSymbol.name} ${snippets_1.InjectedParam_CurState}): bytes {`)
926
+ .append('\n')
927
+ .append(` return ${dataPart.join(' + ')} + hash160(${hashes.join(' + ')});`)
928
+ .append('\n')
929
+ .append('}')
930
+ .append('\n')
931
+ .append(`static function stateHash(${stateTypeSymbol.name} ${snippets_1.InjectedParam_CurState}): bytes {`)
932
+ .append('\n')
933
+ .append(` return sha256(${dataPart.join(' + ')} + hash160(${hashes.join(' + ')}));`)
934
+ .append('\n')
935
+ .append('}');
936
+ }
937
+ transformPropertySignature(node, toSection) {
938
+ return this.transformEnclosingTypeNode(node.type || node, toSection)
939
+ .append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart()))
940
+ .append(';');
941
+ }
942
+ transformPropertyDeclaration(node, toSection, category) {
943
+ if (!Transpiler.isProperty(node))
944
+ return toSection;
945
+ toSection.append('\n');
946
+ if (Transpiler.isStateProperty(node)) {
947
+ if (category === 'library') {
948
+ const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
949
+ throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', \`@prop(true)\` is only allowed to be used in \`SmartContract\``, this.getRange(decorator.expression));
950
+ }
951
+ if (Transpiler.isStaticProperty(node)) {
952
+ throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', \`@prop(true)\` cannot be static`, this.getRange(node));
953
+ }
954
+ if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword)) {
955
+ throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', \`@prop(true)\` cannot be readonly`, this.getRange(node));
956
+ }
957
+ toSection.append('@state ');
958
+ }
959
+ else if (Transpiler.isStaticProperty(node)) {
960
+ if (!node.initializer) {
961
+ throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', static property shall be initialized when declared`, this.getRange(node));
962
+ }
963
+ }
964
+ toSection
965
+ .appendWith(this, (toSec) => {
966
+ return this.transformModifiers(node, toSec);
967
+ })
968
+ .appendWith(this, (toSec) => {
969
+ if (!node.type) {
970
+ throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', all \`prop()\` should be typed explicitly`, this.getRange(node));
971
+ }
972
+ return this.transformEnclosingTypeNode(node.type, toSec);
973
+ })
974
+ .append(` ${node.name.getText()}`, this.getCoordinates(node.name.getStart()));
975
+ if (node.initializer) {
976
+ if (Transpiler.isStaticProperty(node)) {
977
+ toSection
978
+ .append(' = ')
979
+ .appendWith(this, (toSec) => {
980
+ return this.transformExpression(node.initializer, toSec);
981
+ })
982
+ .append(';');
983
+ return toSection;
984
+ }
985
+ else {
986
+ throw new types_1.TranspileError(`Untransformable property: '${node.name.getText()}', Non-static properties shall only be initialized in the constructor`, this.getRange(node.initializer));
987
+ }
988
+ }
989
+ toSection.append(';');
990
+ return toSection;
991
+ }
992
+ buildConstructorParametersMap(node, baseContract) {
993
+ function getSuperParameters() {
994
+ const superStmt = node.body.statements[0];
995
+ const callexpr = superStmt.expression;
996
+ if (callexpr.arguments[0] &&
997
+ typescript_1.default.isSpreadElement(callexpr.arguments[0]) &&
998
+ callexpr.arguments[0].getText().endsWith('arguments')) {
999
+ return node.parameters;
1000
+ }
1001
+ else {
1002
+ return callexpr.arguments;
1003
+ }
1004
+ }
1005
+ const basector = this.getConstructor(baseContract);
1006
+ if (!basector) {
1007
+ return;
1008
+ }
1009
+ const superParameters = getSuperParameters();
1010
+ basector.parameters.forEach((parameter, index) => {
1011
+ const superParameter = superParameters[index];
1012
+ this._constructorParametersMap.set(parameter.name.getText(), superParameter);
1013
+ });
1014
+ }
1015
+ static accessSetConstructor(statements) {
1016
+ return (statements.findIndex((statement) => {
1017
+ return (typescript_1.default.isExpressionStatement(statement) &&
1018
+ statement.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
1019
+ /^this\.init\(/.test(statement.expression.getText()));
1020
+ }) > -1);
1021
+ }
1022
+ checkSuperStmt(node) {
1023
+ const superStmt = node.body.statements[0];
1024
+ const callexpr = superStmt.expression;
1025
+ if (!typescript_1.default.isCallExpression(callexpr) || callexpr.expression.getText() !== 'super') {
1026
+ throw new types_1.TranspileError(`Constructors for derived classes must contain a \`super()\` call`, this.getRange(node));
1027
+ }
1028
+ if (Transpiler.accessSetConstructor(node.body.statements)) {
1029
+ throw new types_1.TranspileError(`Direct subclasses of \`SmartContract\` do not need to call \`this.init()\`.`, this.getRange(node));
1030
+ }
1031
+ if (callexpr.arguments[0] &&
1032
+ typescript_1.default.isSpreadElement(callexpr.arguments[0]) &&
1033
+ callexpr.arguments[0].getText().endsWith('arguments')) {
1034
+ return;
1035
+ }
1036
+ if (node.parameters.length !== callexpr.arguments.length) {
1037
+ throw new types_1.TranspileError(`All parameters in the constructor must be passed to the \`super()\` call`, this.getRange(node));
1038
+ }
1039
+ node.parameters.forEach((p, pIdx) => {
1040
+ const arg = callexpr.arguments[pIdx];
1041
+ if (arg.getText() !== p.name.getText()) {
1042
+ throw new types_1.TranspileError(`All parameters in the constructor must be passed to the \`super()\` call following their declaration order`, this.getRange(node));
1043
+ }
1044
+ });
1045
+ }
1046
+ checkSetConstructorStmt(node) {
1047
+ const superStmt = node.body.statements[0];
1048
+ const callexpr = superStmt.expression;
1049
+ if (!typescript_1.default.isCallExpression(callexpr) || callexpr.expression.getText() !== 'super') {
1050
+ throw new types_1.TranspileError(`Constructors for derived classes must contain a \`super()\` call`, this.getRange(node));
1051
+ }
1052
+ if (callexpr.arguments[0] &&
1053
+ typescript_1.default.isSpreadElement(callexpr.arguments[0]) &&
1054
+ callexpr.arguments[0].getText().endsWith('arguments')) {
1055
+ return;
1056
+ }
1057
+ if (node.parameters.length === callexpr.arguments.length) {
1058
+ return;
1059
+ }
1060
+ }
1061
+ canBeImplicitConstructor(node) {
1062
+ // Test if it can be an implicit constructor.
1063
+ let res = true;
1064
+ const ctorParamNames = [];
1065
+ node.parameters.forEach((p) => {
1066
+ const pText = p.name.getFullText().trim();
1067
+ ctorParamNames.push(pText);
1068
+ });
1069
+ node.body.statements.slice(1).forEach((stmt) => {
1070
+ // For each statement in the constr check if its a property init stmt.
1071
+ // If not, the constructor cannot be implicit.
1072
+ const isPropSet = this.queryPropertyInitializedInStmt(stmt);
1073
+ if (!isPropSet) {
1074
+ res = false;
1075
+ return;
1076
+ }
1077
+ // If its a prop init stmt, then also check if the value being set is a
1078
+ // constructor parameter.
1079
+ if (!typescript_1.default.isExpressionStatement(stmt) || !typescript_1.default.isBinaryExpression(stmt.expression)) {
1080
+ res = false;
1081
+ return;
1082
+ }
1083
+ const exprName = stmt.expression.right.getFullText().trim();
1084
+ let found = false;
1085
+ for (const ctorParamName of ctorParamNames) {
1086
+ if (ctorParamName == exprName) {
1087
+ found = true;
1088
+ break;
1089
+ }
1090
+ }
1091
+ if (!found) {
1092
+ res = false;
1093
+ return;
1094
+ }
1095
+ });
1096
+ // Check the order of the ctor params compared to the order of prop declarations.
1097
+ const props = node.parent.members.filter((member) => Transpiler.isProperty(member));
1098
+ props.forEach((p) => {
1099
+ const pName = p.name.getFullText().trim();
1100
+ const ctorParamName = ctorParamNames.shift();
1101
+ if (pName != ctorParamName) {
1102
+ res = false;
1103
+ return;
1104
+ }
1105
+ });
1106
+ // Check if the contract contains any non-props. It can still contain static (readonly) non-props.
1107
+ const nonProps = node.parent.members.filter((member) => this.isNonProp(member) && !this.isStaticReadOnlyNonProp(member));
1108
+ if (nonProps.length > 0) {
1109
+ res = false;
1110
+ }
1111
+ // Also check for VarIntReader properties.
1112
+ if (this.accessSHPreimage() || this.accessChange() || this.accessPrevouts()) {
1113
+ res = false;
1114
+ }
1115
+ return res;
1116
+ }
1117
+ transformConstructorBody(section, node) {
1118
+ const allProps = this.allPropertyDeclaration(node.parent);
1119
+ const initializedProps = [];
1120
+ node.body.statements.slice(1).forEach((stmt) => {
1121
+ section.append('\n').appendWith(this, (stmtsSec) => {
1122
+ const prop = this.queryPropertyInitializedInStmt(stmt);
1123
+ if (prop) {
1124
+ initializedProps.push(prop);
1125
+ if (!Transpiler.isProperty(prop)) {
1126
+ return stmtsSec; //allow but ignore Non-Prop Property
1127
+ }
1128
+ }
1129
+ return this.transformStatement(stmt, stmtsSec);
1130
+ }, true);
1131
+ });
1132
+ allProps.forEach((prop) => {
1133
+ if (!initializedProps.map((p) => p.name.getText()).includes(prop.name.getText())) {
1134
+ // allowed static const prop initialized
1135
+ if (!Transpiler.isStaticProperty(prop)) {
1136
+ throw new types_1.TranspileError(`property \`${prop.name.getText()}\` must be initialized in the constructor`, this.getRange(prop));
1137
+ }
1138
+ }
1139
+ });
1140
+ return section;
1141
+ }
1142
+ transformConstructor(section, cls, baseContract) {
1143
+ this.checkConstructor(cls);
1144
+ const node = cls.members.find((m) => m.kind === typescript_1.default.SyntaxKind.Constructor);
1145
+ if (node) {
1146
+ if (baseContract) {
1147
+ this.buildConstructorParametersMap(node, baseContract);
1148
+ }
1149
+ section.appendWith(this, (section) => {
1150
+ if (!node.body) {
1151
+ throw new types_1.TranspileError(`Missing function body`, this.getRange(node));
1152
+ }
1153
+ const tmpSection = new EmittedSection();
1154
+ tmpSection.lines = section.lines;
1155
+ tmpSection.errors = section.errors;
1156
+ tmpSection
1157
+ .append('\n')
1158
+ .append('constructor', this.getCoordinates(node.getStart()))
1159
+ .append('(')
1160
+ .appendWith(this, (psSec) => {
1161
+ node.parameters.forEach((p, pIdx) => {
1162
+ psSec.appendWith(this, (pSec) => {
1163
+ const sec = this.transformParameter(p, pSec);
1164
+ if (pIdx !== node.parameters.length - 1) {
1165
+ sec.append(', ');
1166
+ }
1167
+ return sec;
1168
+ });
1169
+ });
1170
+ return psSec;
1171
+ })
1172
+ .append(') ');
1173
+ tmpSection
1174
+ .append('{')
1175
+ .appendWith(this, (sec) => {
1176
+ if (baseContract) {
1177
+ baseContract.members.forEach((member) => {
1178
+ if (member.kind === typescript_1.default.SyntaxKind.Constructor) {
1179
+ this.transformConstructorBody(sec, member);
1180
+ }
1181
+ });
1182
+ }
1183
+ return this.transformConstructorBody(sec, node);
1184
+ }, true)
1185
+ .append('\n}');
1186
+ // If the constructor can be implicit, we don't even have to transform it.
1187
+ if (this.canBeImplicitConstructor(node)) {
1188
+ section.errors = tmpSection.errors;
1189
+ return section;
1190
+ }
1191
+ return tmpSection;
1192
+ });
1193
+ }
1194
+ else {
1195
+ if (this.accessSHPreimage() || this.accessChange() || this.accessPrevouts()) {
1196
+ section.append(`\n${snippets_1.EMPTY_CONSTRUCTOR}`);
1197
+ return section;
1198
+ }
1199
+ }
1200
+ return section;
1201
+ }
1202
+ setMethodDecOptions(methodDec) {
1203
+ const expression = methodDec.expression;
1204
+ // set default value for options
1205
+ this._currentMethodDecOptions = {
1206
+ autoCheckInputState: true,
1207
+ };
1208
+ if (typescript_1.default.isCallExpression(expression) && expression.arguments[0]) {
1209
+ const argTxt = expression.arguments[0].getText();
1210
+ this._currentMethodDecOptions.autoCheckInputState =
1211
+ !/autoCheckInputState: false/.test(argTxt);
1212
+ }
1213
+ }
1214
+ transformMethodDeclaration(node, toSection, category) {
1215
+ const methodDec = Transpiler.findDecorator(node, DecoratorName.Method);
1216
+ if (!methodDec)
1217
+ return toSection;
1218
+ const match = /^method\((.*)?\)$/.exec(methodDec.expression.getText());
1219
+ if (!match) {
1220
+ return toSection;
1221
+ }
1222
+ this.setMethodDecOptions(methodDec);
1223
+ this._currentMethodName = node.name.getText();
1224
+ let sigHashType = '41'; //SigHash.ALL;
1225
+ switch (match[1]) {
1226
+ case 'SigHash.ANYONECANPAY_SINGLE':
1227
+ sigHashType = 'c3'; // SigHash.ANYONECANPAY_SINGLE;
1228
+ break;
1229
+ case 'SigHash.ANYONECANPAY_ALL':
1230
+ sigHashType = 'c1'; //SigHash.ANYONECANPAY_ALL;
1231
+ break;
1232
+ case 'SigHash.ANYONECANPAY_NONE':
1233
+ sigHashType = 'c2'; //SigHash.ANYONECANPAY_NONE;
1234
+ break;
1235
+ case 'SigHash.ALL':
1236
+ sigHashType = '41'; //SigHash.ALL;
1237
+ break;
1238
+ case 'SigHash.SINGLE':
1239
+ sigHashType = '43'; //SigHash.SINGLE;
1240
+ break;
1241
+ case 'SigHash.NONE':
1242
+ sigHashType = '42'; //SigHash.NONE;
1243
+ break;
1244
+ default:
1245
+ break;
1246
+ }
1247
+ const isPublicMethod = Transpiler.isPublicMethod(node);
1248
+ if (isPublicMethod) {
1249
+ if (category === 'library') {
1250
+ const publicModifier = typescript_1.default
1251
+ .getModifiers(node)
1252
+ .find((m) => m.kind === typescript_1.default.SyntaxKind.PublicKeyword);
1253
+ throw new types_1.TranspileError(`\`@method\` in \`SmartContractLib\` should not be declared as \`public\``, this.getRange(publicModifier ? publicModifier : node));
1254
+ }
1255
+ const retStmt = (0, utils_1.findReturnStatement)(node);
1256
+ if (retStmt) {
1257
+ throw new types_1.TranspileError(`public methods cannot contain an explicit return statement`, this.getRange(retStmt));
1258
+ }
1259
+ if (node.type) {
1260
+ // has return type
1261
+ throw new types_1.TranspileError(`public methods cannot have a return type`, this.getRange(node.type));
1262
+ }
1263
+ }
1264
+ else {
1265
+ if (!node.type) {
1266
+ throw new types_1.TranspileError(`non-public methods must declare the return type explicitly`, this.getRange(node));
1267
+ }
1268
+ }
1269
+ if (!node.body) {
1270
+ throw new types_1.TranspileError(`Missing function body`, this.getRange(node));
1271
+ }
1272
+ const shouldAutoAppendSighashPreimage = this.shouldAutoAppendSighashPreimage(node);
1273
+ const shouldAutoAppendChangeAmount = this.shouldAutoAppendChangeAmount(node);
1274
+ const shouldAutoAppendPrevouts = this.shouldAutoAppendPrevouts(node);
1275
+ const shouldAutoAppendPrevout = this.shouldAutoAppendPrevout(node);
1276
+ const shouldAutoAppendSpentScripts = this.shouldAutoAppendSpentScripts(node);
1277
+ const shouldAutoAppendSpentAmounts = this.shouldAutoAppendSpentAmounts(node);
1278
+ const shouldAutoAppendSpentDataHashes = this.shouldAutoAppendSpentDataHashes(node);
1279
+ const shouldAutoAppendStateArgs = this.shouldAutoAppendStateArgs(node);
1280
+ const shouldAutoAppendPrevTxHashPreimage = this.shouldAutoAppendPrevTxHashPreimageArgs(node);
1281
+ const buildChangeOutputExpression = (0, utils_1.findBuildChangeOutputExpression)(node);
1282
+ if (shouldAutoAppendSighashPreimage.shouldAppendArguments &&
1283
+ buildChangeOutputExpression !== undefined) {
1284
+ const allowedSighashType = ['c1', '41']; // Only sighash ALL allowed.
1285
+ if (!allowedSighashType.includes(sigHashType)) {
1286
+ throw new types_1.TranspileError(`Can only use sighash ALL or ANYONECANPAY_ALL if using \`this.buildChangeOutput()\``, this.getRange(node));
1287
+ }
1288
+ }
1289
+ toSection
1290
+ .append('\n')
1291
+ .appendWith(this, (toSec) => {
1292
+ return this.transformModifiers(node, toSec);
1293
+ })
1294
+ .append('function ')
1295
+ .append(node.name.getText(), this.getCoordinates(node.name.getStart()))
1296
+ .append('(')
1297
+ .appendWith(this, (psSec) => {
1298
+ // not allow SmartContract as parameter
1299
+ const inValidParams = node.parameters.find((p) => p.type?.getText() === 'SmartContract');
1300
+ if (inValidParams) {
1301
+ throw new types_1.TranspileError(`Untransformable parameter: '${node.getText()}'`, this.getRange(node));
1302
+ }
1303
+ node.parameters.forEach((p, pIdx) => {
1304
+ psSec.appendWith(this, (pSec) => {
1305
+ const sec = this.transformParameter(p, pSec);
1306
+ if (pIdx !== node.parameters.length - 1) {
1307
+ sec.append(', ');
1308
+ }
1309
+ return sec;
1310
+ });
1311
+ });
1312
+ let paramLen = node.parameters.length;
1313
+ if (shouldAutoAppendSighashPreimage.shouldAppendArguments) {
1314
+ this._accessBuiltinsSymbols.add('SHPreimage');
1315
+ if (paramLen > 0) {
1316
+ psSec.append(', ');
1317
+ }
1318
+ psSec.append(`SHPreimage ${snippets_1.InjectedParam_SHPreimage}`);
1319
+ paramLen += 2;
1320
+ }
1321
+ if (shouldAutoAppendChangeAmount.shouldAppendArguments) {
1322
+ this._accessBuiltinsSymbols.add('TxOut');
1323
+ if (paramLen > 0) {
1324
+ psSec.append(', ');
1325
+ }
1326
+ psSec.append(`TxOut ${snippets_1.InjectedParam_ChangeInfo}`);
1327
+ paramLen += 2;
1328
+ }
1329
+ if (shouldAutoAppendStateArgs.shouldAppendArguments) {
1330
+ if (paramLen > 0) {
1331
+ psSec.append(', ');
1332
+ }
1333
+ const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
1334
+ this._accessBuiltinsSymbols.add('StdUtils');
1335
+ if (!stateTypeSymbol) {
1336
+ throw new Error('State type symbol is not defined');
1337
+ }
1338
+ psSec.append(`${stateTypeSymbol.name} ${snippets_1.InjectedParam_CurState}`);
1339
+ paramLen += 1;
1340
+ }
1341
+ // note this should be the third from bottom parameter if exists
1342
+ if (shouldAutoAppendPrevouts.shouldAppendArguments) {
1343
+ this._accessBuiltinsSymbols.add('Outpoint');
1344
+ if (paramLen > 0) {
1345
+ psSec.append(', ');
1346
+ }
1347
+ psSec.append(`bytes ${snippets_1.InjectedParam_Prevouts}`);
1348
+ paramLen += 1;
1349
+ }
1350
+ // note this should be the second from bottom parameter if exists
1351
+ if (shouldAutoAppendSpentAmounts.shouldAppendArguments) {
1352
+ if (paramLen > 0) {
1353
+ psSec.append(', ');
1354
+ }
1355
+ psSec.append(`bytes ${snippets_1.InjectedParam_SpentAmounts}`);
1356
+ paramLen += 1;
1357
+ }
1358
+ if (shouldAutoAppendSpentDataHashes.shouldAppendArguments) {
1359
+ if (paramLen > 0) {
1360
+ psSec.append(', ');
1361
+ }
1362
+ psSec.append(`bytes ${snippets_1.InjectedParam_SpentDataHashes}`);
1363
+ paramLen += 1;
1364
+ }
1365
+ // note this should be the last parameter if exists
1366
+ if (shouldAutoAppendSpentScripts.shouldAppendArguments) {
1367
+ if (paramLen > 0) {
1368
+ psSec.append(', ');
1369
+ }
1370
+ psSec.append(`bytes ${snippets_1.InjectedParam_SpentScriptHashes}`);
1371
+ paramLen += 1;
1372
+ }
1373
+ if (shouldAutoAppendPrevTxHashPreimage.shouldAppendArguments) {
1374
+ if (paramLen > 0) {
1375
+ psSec.append(', ');
1376
+ }
1377
+ psSec.append(`TxHashPreimage ${snippets_1.InjectedParam_PrevTxHashPreimage}`);
1378
+ paramLen += 1;
1379
+ }
1380
+ return psSec;
1381
+ })
1382
+ .append(') ');
1383
+ // non-public method return type
1384
+ let autoReturnStatement = '';
1385
+ if (!isPublicMethod) {
1386
+ // for non-public method, `node.type` is not `undefined` definitely
1387
+ if (node.type.kind !== typescript_1.default.SyntaxKind.VoidKeyword) {
1388
+ // return not void
1389
+ toSection
1390
+ .append(': ')
1391
+ .appendWith(this, (toSec) => this.transformEnclosingTypeNode(node.type, toSec))
1392
+ .append(' ');
1393
+ }
1394
+ else {
1395
+ // return void
1396
+ toSection.append(': bool ');
1397
+ autoReturnStatement = '\n return true;';
1398
+ }
1399
+ }
1400
+ this._accessBuiltinsSymbols.add('TxUtils');
1401
+ this._accessBuiltinsSymbols.add('ContextUtils');
1402
+ return toSection
1403
+ .append('{')
1404
+ .appendWith(this, (sec) => {
1405
+ if (shouldAutoAppendSighashPreimage.shouldAppendArguments) {
1406
+ sec
1407
+ .append('\n')
1408
+ .append(`${snippets_1.CALL_CHECK_SHPREIMAGE};`)
1409
+ .append(`\n`);
1410
+ }
1411
+ if (shouldAutoAppendSighashPreimage.shouldAppendThisAssignment) {
1412
+ sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_SHPreimage)).append('\n');
1413
+ }
1414
+ if (shouldAutoAppendChangeAmount.shouldAppendThisAssignment) {
1415
+ sec.append(`\n`).append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_ChangeInfo)).append(`\n`);
1416
+ }
1417
+ if (shouldAutoAppendSpentAmounts.shouldAppendArguments) {
1418
+ sec
1419
+ .append('\n')
1420
+ .append(`int ${snippets_1.InjectedVar_InputCount} = ContextUtils.checkSpentAmounts(${snippets_1.InjectedParam_SpentAmounts}, ${snippets_1.InjectedParam_SHPreimage}.hashSpentAmounts);`)
1421
+ .append('\n');
1422
+ }
1423
+ if (shouldAutoAppendPrevouts.shouldAppendArguments) {
1424
+ sec
1425
+ .append('\n')
1426
+ .append(`Outpoint ${snippets_1.InjectedProp_Prevout} = ContextUtils.checkPrevouts(${snippets_1.InjectedParam_Prevouts}, ${snippets_1.InjectedParam_SHPreimage}.hashPrevouts, ${snippets_1.InjectedParam_SHPreimage}.inputIndex, ${snippets_1.InjectedVar_InputCount});`)
1427
+ .append('\n');
1428
+ }
1429
+ if (shouldAutoAppendPrevouts.shouldAppendThisAssignment) {
1430
+ sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_PrevoutsCtx)).append('\n');
1431
+ }
1432
+ if (shouldAutoAppendPrevout.shouldAppendThisAssignment) {
1433
+ sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_Prevout)).append('\n');
1434
+ }
1435
+ if (shouldAutoAppendSpentScripts.shouldAppendArguments) {
1436
+ sec
1437
+ .append('\n')
1438
+ .append(`ContextUtils.checkSpentScripts(${snippets_1.InjectedParam_SpentScriptHashes}, ${snippets_1.InjectedParam_SHPreimage}.hashSpentScriptHashes, ${snippets_1.InjectedVar_InputCount});`)
1439
+ .append('\n');
1440
+ }
1441
+ if (shouldAutoAppendSpentScripts.shouldAppendThisAssignment) {
1442
+ sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_SpentScriptHashes)).append('\n');
1443
+ }
1444
+ if (shouldAutoAppendSpentDataHashes.shouldAppendArguments) {
1445
+ sec
1446
+ .append('\n')
1447
+ .append(`ContextUtils.checkSpentDataHashes(${snippets_1.InjectedParam_SpentDataHashes}, ${snippets_1.InjectedParam_SHPreimage}.hashSpentDataHashes, ${snippets_1.InjectedVar_InputCount});`)
1448
+ .append('\n');
1449
+ }
1450
+ if (shouldAutoAppendStateArgs.shouldAppendArguments) {
1451
+ // append initialize next state
1452
+ const stateTypeSymbol = this._stateTypeSymbols.get(this.currentContractName);
1453
+ sec
1454
+ .append('\n')
1455
+ .append(`${stateTypeSymbol.name} ${snippets_1.InjectedVar_NextState} = ${snippets_1.InjectedParam_CurState};`)
1456
+ .append('\n');
1457
+ if (shouldAutoAppendStateArgs.shouldAppendThisAssignment) {
1458
+ sec
1459
+ .append('\n')
1460
+ .append(`${(0, snippets_1.thisAssignment)(snippets_1.InjectedVar_NextState)}`)
1461
+ .append('\n');
1462
+ }
1463
+ }
1464
+ if (shouldAutoAppendStateArgs.shouldAppendArguments &&
1465
+ this._currentMethodDecOptions.autoCheckInputState) {
1466
+ // append checkInputState for current input
1467
+ const rawState = this._stateTypeSymbols.has(this.currentContractName)
1468
+ ? `${this._currentContract.name.getText()}.serializeState(${snippets_1.InjectedParam_CurState})`
1469
+ : "b''";
1470
+ // here no need to access this, because it's in public function, the variable we access is in the arguments
1471
+ this._accessBuiltinsSymbols.add('StateUtils');
1472
+ sec
1473
+ .append('\n')
1474
+ .append(`StateUtils.checkInputState(${snippets_1.InjectedParam_SHPreimage}.inputIndex, ${rawState}, ${snippets_1.InjectedParam_SpentDataHashes});`)
1475
+ .append('\n');
1476
+ }
1477
+ if (shouldAutoAppendPrevTxHashPreimage.shouldAppendArguments) {
1478
+ sec
1479
+ .append('\n')
1480
+ .append(`Backtrace.checkPrevTxHashPreimage(${snippets_1.InjectedParam_PrevTxHashPreimage}, ${snippets_1.InjectedParam_Prevouts}, ${snippets_1.InjectedParam_SHPreimage}.inputIndex);`)
1481
+ .append('\n');
1482
+ }
1483
+ if (shouldAutoAppendPrevTxHashPreimage.shouldAppendThisAssignment) {
1484
+ sec.append((0, snippets_1.thisAssignment)(snippets_1.InjectedProp_PrevTxHashPreimage)).append('\n');
1485
+ }
1486
+ node.body.statements.forEach((stmt) => {
1487
+ sec.append('\n').appendWith(this, (stmtsSec) => {
1488
+ return this.transformStatement(stmt, stmtsSec);
1489
+ }, true);
1490
+ });
1491
+ if (isPublicMethod) {
1492
+ if (!this.verifyLastAssertStatement(node.body.statements)) {
1493
+ throw new types_1.TranspileError(`Untransformable public method: Public method \`${node.name.getText()}\` not ended with \`assert()\``, this.getRange(node));
1494
+ }
1495
+ if (this.needAppendLastStatement(node.body.statements)) {
1496
+ sec.append('\n').append('require(true);\n');
1497
+ }
1498
+ }
1499
+ return sec;
1500
+ }, true)
1501
+ .append(autoReturnStatement)
1502
+ .append('\n}');
1503
+ }
1504
+ isAssertStatement(node) {
1505
+ if (typescript_1.default.isExpressionStatement(node)) {
1506
+ const s = node;
1507
+ if (typescript_1.default.isCallExpression(s.expression)) {
1508
+ const e = s.expression;
1509
+ return e.expression.getText() === 'assert';
1510
+ }
1511
+ }
1512
+ else if (typescript_1.default.isForStatement(node)) {
1513
+ return this.isForStatementAssert(node);
1514
+ }
1515
+ else if (typescript_1.default.isIfStatement(node)) {
1516
+ return this.isIfStatementAssert(node);
1517
+ }
1518
+ else if (typescript_1.default.isBlock(node)) {
1519
+ return this.isBlockStatementAssert(node);
1520
+ }
1521
+ return false;
1522
+ }
1523
+ isBlockStatementAssert(node) {
1524
+ if (typescript_1.default.isBlock(node)) {
1525
+ const block = node;
1526
+ if (block.statements.length === 0) {
1527
+ return false;
1528
+ }
1529
+ let i = block.statements.length - 1;
1530
+ while (i >= 0 && this.isConsoleLogStatement(block.statements[i])) {
1531
+ --i;
1532
+ }
1533
+ return i >= 0 && this.isAssertStatement(block.statements[i]);
1534
+ }
1535
+ return false;
1536
+ }
1537
+ isForStatementAssert(node) {
1538
+ if (typescript_1.default.isForStatement(node)) {
1539
+ const s = node;
1540
+ return this.isAssertStatement(s.statement);
1541
+ }
1542
+ return false;
1543
+ }
1544
+ isIfStatementAssert(node) {
1545
+ if (typescript_1.default.isIfStatement(node)) {
1546
+ const s = node;
1547
+ if (s.elseStatement) {
1548
+ return this.isAssertStatement(s.thenStatement) && this.isAssertStatement(s.elseStatement);
1549
+ }
1550
+ return false;
1551
+ }
1552
+ return false;
1553
+ }
1554
+ isConsoleLogStatement(node) {
1555
+ if (typescript_1.default.isExpressionStatement(node)) {
1556
+ const s = node;
1557
+ return (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
1558
+ /^console\.log/.test(s.expression.getText()));
1559
+ }
1560
+ return false;
1561
+ }
1562
+ verifyLastAssertStatement(statements) {
1563
+ if (statements.length === 0) {
1564
+ return false;
1565
+ }
1566
+ let i = statements.length - 1;
1567
+ while (i >= 0 && this.isConsoleLogStatement(statements[i])) {
1568
+ --i;
1569
+ }
1570
+ return i >= 0 && this.isAssertStatement(statements[i]);
1571
+ }
1572
+ needAppendLastStatement(statements) {
1573
+ if (statements.length === 0) {
1574
+ return false;
1575
+ }
1576
+ let i = statements.length - 1;
1577
+ while (i >= 0 && this.isConsoleLogStatement(statements[i])) {
1578
+ --i;
1579
+ }
1580
+ return i >= 0 && !typescript_1.default.isExpressionStatement(statements[i]);
1581
+ }
1582
+ hasPropertyAccessExpression(node, propName) {
1583
+ if (typescript_1.default.isPropertyAccessExpression(node)) {
1584
+ const ae = node;
1585
+ if (ae.expression.getText() === 'this' && ae.name.getText() === propName) {
1586
+ return true;
1587
+ }
1588
+ }
1589
+ return typescript_1.default.forEachChild(node, (node) => this.hasPropertyAccessExpression(node, propName));
1590
+ }
1591
+ hasFunctionCallExpression(node, funcName) {
1592
+ if (typescript_1.default.isCallExpression(node)) {
1593
+ const callee = node.expression;
1594
+ if (typescript_1.default.isPropertyAccessExpression(callee) &&
1595
+ callee.expression.getText() === 'this' &&
1596
+ callee.name.getText() === funcName) {
1597
+ return true;
1598
+ }
1599
+ }
1600
+ return typescript_1.default.forEachChild(node, (node) => this.hasFunctionCallExpression(node, funcName));
1601
+ }
1602
+ createMethodAccessInfo(method, currentMethodName, isBase) {
1603
+ const accessInfo = {
1604
+ accessSHPreimage: false,
1605
+ accessSHPreimageInSubCall: false,
1606
+ accessChange: false,
1607
+ accessChangeInSubCall: false,
1608
+ accessState: false,
1609
+ accessStateInSubCall: false,
1610
+ accessPrevouts: false,
1611
+ accessPrevoutsInSubCall: false,
1612
+ accessPrevout: false,
1613
+ accessPrevoutInSubCall: false,
1614
+ accessSpentScripts: false,
1615
+ accessSpentScriptsInSubCall: false,
1616
+ accessSpentAmounts: false,
1617
+ accessSpentAmountsInSubCall: false,
1618
+ accessSpentDataHashes: false,
1619
+ accessSpentDataHashesInSubCall: false,
1620
+ accessBacktrace: false,
1621
+ accessBacktraceInSubCall: false,
1622
+ accessCSV: false,
1623
+ accessCLTV: false,
1624
+ };
1625
+ function vistMethodChild(self, node) {
1626
+ if (typescript_1.default.isPropertyAccessExpression(node)) {
1627
+ // access properties under `this.ctx`
1628
+ if (node.expression.getText() === 'this.ctx') {
1629
+ Object.assign(accessInfo, {
1630
+ accessSHPreimage: true,
1631
+ });
1632
+ switch (node.name.getText()) {
1633
+ case 'prevouts':
1634
+ Object.assign(accessInfo, {
1635
+ accessPrevouts: true,
1636
+ accessSpentAmounts: true,
1637
+ });
1638
+ break;
1639
+ case 'prevout':
1640
+ Object.assign(accessInfo, {
1641
+ accessPrevout: true,
1642
+ accessSpentAmounts: true,
1643
+ });
1644
+ break;
1645
+ case 'spentAmounts':
1646
+ Object.assign(accessInfo, {
1647
+ accessSpentAmounts: true,
1648
+ });
1649
+ break;
1650
+ case 'spentScriptHashes':
1651
+ Object.assign(accessInfo, {
1652
+ accessSpentAmounts: true,
1653
+ accessSpentScripts: true,
1654
+ });
1655
+ break;
1656
+ case 'spentDataHashes':
1657
+ Object.assign(accessInfo, {
1658
+ accessSpentAmounts: true,
1659
+ accessSpentDataHashes: true,
1660
+ });
1661
+ break;
1662
+ }
1663
+ }
1664
+ // access properties under `this`
1665
+ if (node.expression.getText() === 'this') {
1666
+ if (node.name.getText() === 'changeInfo') {
1667
+ Object.assign(accessInfo, {
1668
+ accessChange: true,
1669
+ });
1670
+ }
1671
+ else if (node.name.getText() === 'state') {
1672
+ Object.assign(accessInfo, {
1673
+ accessSHPreimage: true, // accessSpentDataHashes depends on shPreimage
1674
+ accessState: true,
1675
+ accessSpentAmounts: true, // spentDataHashes depends on spentAmounts
1676
+ accessSpentDataHashes: true, // state depends on spentDataHashes
1677
+ });
1678
+ }
1679
+ }
1680
+ }
1681
+ else if (typescript_1.default.isCallExpression(node)) {
1682
+ const callee = node.expression;
1683
+ if (typescript_1.default.isPropertyAccessExpression(callee) && callee.expression.getText() === 'this') {
1684
+ const methodName = callee.name.getText();
1685
+ if (methodName === currentMethodName) {
1686
+ throw new types_1.TranspileError(`Cycle detected in function call: this.${methodName}()!`, self.getRange(node));
1687
+ }
1688
+ else if (SmartContractBuiltinMethods.includes(methodName)) {
1689
+ if (methodName === 'buildChangeOutput') {
1690
+ Object.assign(accessInfo, {
1691
+ accessChange: true,
1692
+ });
1693
+ }
1694
+ else if (methodName === 'checkInputState') {
1695
+ Object.assign(accessInfo, {
1696
+ accessSHPreimage: true, // accessSpentDataHashes depends on shPreimage
1697
+ accessState: true,
1698
+ accessSpentDataHashes: true,
1699
+ });
1700
+ }
1701
+ else if (['relTimeLock'].includes(methodName)) {
1702
+ Object.assign(accessInfo, {
1703
+ accessCSV: true,
1704
+ });
1705
+ }
1706
+ else if (['absTimeLock'].includes(methodName)) {
1707
+ Object.assign(accessInfo, {
1708
+ accessCLTV: true,
1709
+ });
1710
+ }
1711
+ else if (['backtraceToOutpoint', 'backtraceToScript'].includes(methodName)) {
1712
+ Object.assign(accessInfo, {
1713
+ accessSHPreimage: true,
1714
+ accessPrevouts: true,
1715
+ accessSpentScripts: true,
1716
+ accessSpentAmounts: true,
1717
+ accessBacktrace: true,
1718
+ });
1719
+ }
1720
+ }
1721
+ else {
1722
+ // access in subCall method
1723
+ const clsName = Transpiler.getClassDeclaration(node).name.getText();
1724
+ let methodInfo = self.methodInfos.get(`${clsName}.${methodName}`);
1725
+ if (!methodInfo) {
1726
+ const m = self.findMethodDeclaration(methodName);
1727
+ if (!m) {
1728
+ const msg = `\`${methodName}\` is not \`@method\` decorated so cannot be called in \`${currentMethodName}\`.`;
1729
+ throw new types_1.TranspileError(msg, self.getRange(callee.name));
1730
+ }
1731
+ methodInfo = self.createMethodInfo(m, currentMethodName, isBase);
1732
+ if (!methodInfo) {
1733
+ throw new types_1.TranspileError('createMethodInfo failed', self.getRange(callee.name));
1734
+ }
1735
+ }
1736
+ Transpiler.checkAccessInSubCall(currentMethodName, self.getRange(callee.name), methodInfo);
1737
+ Transpiler.pickAccessInfo(accessInfo, methodInfo.accessInfo);
1738
+ }
1739
+ }
1740
+ }
1741
+ typescript_1.default.forEachChild(node, (node) => vistMethodChild(self, node));
1742
+ }
1743
+ vistMethodChild(this, method);
1744
+ return accessInfo;
1745
+ }
1746
+ static checkAccessInSubCall(calleeMethod, calleeRange, subCallMethodInfo) {
1747
+ if (subCallMethodInfo.isPublic) {
1748
+ // public function with access to injected arguments cannot be called by another function
1749
+ const accessInfo = subCallMethodInfo.accessInfo;
1750
+ const getErrorMsg = () => `\`${calleeMethod}\` cannot call public function \`${subCallMethodInfo.name}\`, because public function \`${subCallMethodInfo.name}\` has access to \`this.ctx\`, \`this.state\`, \`this.changeInfo\`, or \`backtrace\``;
1751
+ if (accessInfo.accessSHPreimage) {
1752
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1753
+ }
1754
+ if (accessInfo.accessChange) {
1755
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1756
+ }
1757
+ if (accessInfo.accessState) {
1758
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1759
+ }
1760
+ if (accessInfo.accessPrevouts) {
1761
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1762
+ }
1763
+ if (accessInfo.accessSpentAmounts) {
1764
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1765
+ }
1766
+ if (accessInfo.accessSpentScripts) {
1767
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1768
+ }
1769
+ if (accessInfo.accessSpentDataHashes) {
1770
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1771
+ }
1772
+ if (accessInfo.accessBacktrace) {
1773
+ throw new types_1.TranspileError(getErrorMsg(), calleeRange);
1774
+ }
1775
+ }
1776
+ }
1777
+ static pickAccessInfo(target, info) {
1778
+ return Object.keys(info).reduce((acc, current) => {
1779
+ if (info[current] === true) {
1780
+ Object.assign(acc, {
1781
+ [current]: info[current],
1782
+ });
1783
+ Object.assign(acc, {
1784
+ [current + 'InSubCall']: info[current],
1785
+ });
1786
+ }
1787
+ return acc;
1788
+ }, target);
1789
+ }
1790
+ initAllMethodInfos() {
1791
+ if (this.currentbaseContract) {
1792
+ this.currentbaseContract.members.forEach((m) => {
1793
+ if (Transpiler.isMethod(m)) {
1794
+ this.createMethodInfo(m, m.name.getText(), true);
1795
+ }
1796
+ });
1797
+ }
1798
+ this._currentContract.members.forEach((m) => {
1799
+ if (Transpiler.isMethod(m)) {
1800
+ this.createMethodInfo(m, m.name.getText(), false);
1801
+ }
1802
+ });
1803
+ }
1804
+ initAllPropInfos() {
1805
+ if (this.currentbaseContract) {
1806
+ this.currentbaseContract.members.forEach((m) => {
1807
+ if (Transpiler.isProperty(m)) {
1808
+ this.createPropInfo(m, true);
1809
+ }
1810
+ });
1811
+ }
1812
+ this._currentContract.members.forEach((m) => {
1813
+ if (Transpiler.isProperty(m)) {
1814
+ this.createPropInfo(m, false);
1815
+ }
1816
+ });
1817
+ }
1818
+ createMethodInfo(m, currentMethodName, isBase) {
1819
+ //save methods info
1820
+ const cls = Transpiler.getClassDeclaration(m);
1821
+ if (!cls) {
1822
+ throw new types_1.TranspileError(`getClassDeclaration undefined`, this.getRange(m.name));
1823
+ }
1824
+ const key = `${cls.name.getText()}.${m.name.getText()}`;
1825
+ if (!this.methodInfos.has(key)) {
1826
+ const methodInfo = {
1827
+ isPublic: Transpiler.isPublicMethod(m),
1828
+ isBase: isBase,
1829
+ accessInfo: this.createMethodAccessInfo(m, currentMethodName, isBase),
1830
+ name: m.name.getText(),
1831
+ codeSeparatorCount: 0,
1832
+ };
1833
+ this.methodInfos.set(key, methodInfo);
1834
+ }
1835
+ return this.methodInfos.get(key);
1836
+ }
1837
+ createPropInfo(m, isBase) {
1838
+ const cls = Transpiler.getClassDeclaration(m);
1839
+ if (!cls) {
1840
+ throw new Error('No ClassDeclaration found!');
1841
+ }
1842
+ //save prop info
1843
+ const key = `${cls.name.getText()}.${m.name.getText()}`;
1844
+ if (!this.propInfos.has(key)) {
1845
+ const propInfo = {
1846
+ name: m.name.getText(),
1847
+ isState: Transpiler.isStateProperty(m),
1848
+ isStatic: Transpiler.isStaticProperty(m),
1849
+ isReadonly: Transpiler.isReadonlyProperty(m),
1850
+ isBase: isBase,
1851
+ isCTC: this.isCtcDeclaration(m),
1852
+ };
1853
+ this.propInfos.set(key, propInfo);
1854
+ }
1855
+ return this.propInfos.get(key);
1856
+ }
1857
+ // TODO: don't support override now
1858
+ findMethodInfo(name) {
1859
+ const methodInfo = this.methodInfos.get(`${this.currentContractName}.${name}`);
1860
+ if (methodInfo) {
1861
+ return methodInfo;
1862
+ }
1863
+ if (this.currentbaseContract) {
1864
+ const methodInfo = this.methodInfos.get(`${this.currentbaseContractName}.${name}`);
1865
+ if (methodInfo) {
1866
+ return methodInfo;
1867
+ }
1868
+ }
1869
+ return undefined;
1870
+ }
1871
+ getMethodContainsTheNode(node) {
1872
+ let methodNode;
1873
+ let _node = node;
1874
+ const baseContractNode = this.currentbaseContract;
1875
+ while (_node.parent.parent) {
1876
+ // current contract
1877
+ if (this.scComponents.includes(_node.parent.parent)) {
1878
+ methodNode = _node.parent;
1879
+ break;
1880
+ }
1881
+ // base contract
1882
+ if (_node.parent.parent === baseContractNode) {
1883
+ methodNode = _node.parent;
1884
+ break;
1885
+ }
1886
+ _node = _node.parent;
1887
+ }
1888
+ if (!methodNode) {
1889
+ throw new types_1.TranspileError(`Cannot find the method that contains the \`${node.getText()}\``, this.getRange(node));
1890
+ }
1891
+ return methodNode;
1892
+ }
1893
+ getCtxMethodInfos(isPublic = true) {
1894
+ return Array.from(this.methodInfos.values()).filter((info) => info.accessInfo.accessSHPreimage && info.isPublic === isPublic);
1895
+ }
1896
+ findPropInfo(name) {
1897
+ const propInfo = this.propInfos.get(`${this.currentContractName}.${name}`);
1898
+ if (propInfo) {
1899
+ return propInfo;
1900
+ }
1901
+ if (this.currentbaseContract) {
1902
+ const propInfo = this.propInfos.get(`${this.currentbaseContractName}.${name}`);
1903
+ if (propInfo) {
1904
+ return propInfo;
1905
+ }
1906
+ }
1907
+ return undefined;
1908
+ }
1909
+ getStatePropInfos() {
1910
+ return Array.from(this.propInfos.values()).filter((info) => info.isState);
1911
+ }
1912
+ getPublicMethodCount() {
1913
+ let count = 0;
1914
+ this.methodInfos.forEach((info) => {
1915
+ if (info.isPublic) {
1916
+ count++;
1917
+ }
1918
+ });
1919
+ return count;
1920
+ }
1921
+ /**
1922
+ * does the current contract have a method to access CTX
1923
+ * @returns
1924
+ */
1925
+ accessSHPreimage() {
1926
+ let ret = false;
1927
+ this.methodInfos.forEach((info) => {
1928
+ if (info.accessInfo.accessSHPreimage) {
1929
+ ret = true;
1930
+ }
1931
+ });
1932
+ return ret;
1933
+ }
1934
+ checkShouldInject(methodCheckFn) {
1935
+ // do not inject if the current contract is a library
1936
+ if (this.isLibrary(this._currentContract))
1937
+ return false;
1938
+ let ret = false;
1939
+ this._currentContract.members.forEach((member) => {
1940
+ if (member.kind === typescript_1.default.SyntaxKind.MethodDeclaration) {
1941
+ const method = member;
1942
+ // do not inject if the method is not decorated with @method
1943
+ const methodDec = Transpiler.findDecorator(method, DecoratorName.Method);
1944
+ if (!methodDec)
1945
+ return;
1946
+ const match = /^method\((.*)?\)$/.exec(methodDec.expression.getText());
1947
+ if (!match) {
1948
+ return;
1949
+ }
1950
+ // do not inject if the method is not public
1951
+ if (!Transpiler.isPublicMethod(method))
1952
+ return;
1953
+ // do not inject if the method is static
1954
+ if (Transpiler.isStaticMethod(method))
1955
+ return;
1956
+ try {
1957
+ if (methodCheckFn(method)) {
1958
+ ret = true;
1959
+ }
1960
+ }
1961
+ catch (_e) {
1962
+ // ignore error
1963
+ }
1964
+ }
1965
+ });
1966
+ return ret;
1967
+ }
1968
+ shouldInjectSHPreimageProp() {
1969
+ return this.checkShouldInject((method) => this.shouldAutoAppendSighashPreimage(method).shouldAccessThis);
1970
+ }
1971
+ shouldInjectChangeProp() {
1972
+ return this.checkShouldInject((method) => this.shouldAutoAppendChangeAmount(method).shouldAccessThis);
1973
+ }
1974
+ shouldInjectPrevoutsProp() {
1975
+ return this.checkShouldInject((method) => this.shouldAutoAppendPrevouts(method).shouldAccessThis);
1976
+ }
1977
+ shouldInjectPrevoutProp() {
1978
+ return this.checkShouldInject((method) => this.shouldAutoAppendPrevout(method).shouldAccessThis);
1979
+ }
1980
+ shouldInjectSpentScriptsProp() {
1981
+ return this.checkShouldInject((method) => this.shouldAutoAppendSpentScripts(method).shouldAccessThis);
1982
+ }
1983
+ shouldInjectSpentAmountsProp() {
1984
+ return this.checkShouldInject((method) => this.shouldAutoAppendSpentAmounts(method).shouldAccessThis);
1985
+ }
1986
+ shouldInjectSpentDataHashesProp() {
1987
+ return this.checkShouldInject((method) => this.shouldAutoAppendSpentDataHashes(method).shouldAccessThis);
1988
+ }
1989
+ shouldInjectPrevTxHashPreimageProp() {
1990
+ return this.checkShouldInject((method) => this.shouldAutoAppendPrevTxHashPreimageArgs(method).shouldAccessThis);
1991
+ }
1992
+ shouldInjectCurStateProp() {
1993
+ return this.checkShouldInject((method) => this.shouldAutoAppendStateArgs(method).shouldAccessThis);
1994
+ }
1995
+ isStateful() {
1996
+ return this._stateTypeSymbols.has(this.currentContractName);
1997
+ }
1998
+ accessChange() {
1999
+ let ret = false;
2000
+ this.methodInfos.forEach((info) => {
2001
+ if (info.isPublic && info.accessInfo.accessChange) {
2002
+ ret = true;
2003
+ }
2004
+ });
2005
+ return ret;
2006
+ }
2007
+ accessPrevouts() {
2008
+ let ret = false;
2009
+ this.methodInfos.forEach((info) => {
2010
+ if (info.isPublic && info.accessInfo.accessPrevouts) {
2011
+ ret = true;
2012
+ }
2013
+ });
2014
+ return ret;
2015
+ }
2016
+ _shouldAutoAppend(node, shouldFn) {
2017
+ const methodInfo = this.findMethodInfo(node.name.getText());
2018
+ if (!methodInfo) {
2019
+ throw new types_1.UnknownError(`No method info found for \`${node.name.getText()}\``, this.getRange(node.name));
2020
+ }
2021
+ const [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis] = shouldFn(methodInfo);
2022
+ // addtional check: if a private method is accessing , but no public method is accessing, then it should not be injected and throw an error
2023
+ if (shouldAccessThis && !methodInfo.isPublic) {
2024
+ let addtionalCheckPassed = shouldAccessThis ? false : true;
2025
+ for (const info of this.methodInfos.values()) {
2026
+ const [shouldAppendArguments_] = shouldFn(info);
2027
+ if (info.isPublic && shouldAppendArguments_) {
2028
+ addtionalCheckPassed = true;
2029
+ break;
2030
+ }
2031
+ }
2032
+ if (!addtionalCheckPassed) {
2033
+ throw new types_1.TranspileError(`Cannot access \`this.ctx\`, \`this.state\`, \`this.changeInfo\`, or \`backtrace\` in a private method \`${node.name.getText()}\`, because the private method is not called by any public method`, this.getRange(node.name));
2034
+ }
2035
+ }
2036
+ return { shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis };
2037
+ }
2038
+ shouldAutoAppendChangeAmount(node) {
2039
+ return this._shouldAutoAppend(node, (methodInfo) => {
2040
+ const { accessChange, accessChangeInSubCall } = methodInfo.accessInfo;
2041
+ const { isPublic } = methodInfo;
2042
+ const shouldAppendArguments = accessChange && isPublic;
2043
+ const shouldAppendThisAssignment = accessChange && accessChangeInSubCall && isPublic;
2044
+ const shouldAccessThis = (!isPublic || accessChangeInSubCall) && accessChange;
2045
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2046
+ });
2047
+ }
2048
+ shouldAutoAppendPrevouts(node) {
2049
+ return this._shouldAutoAppend(node, (methodInfo) => {
2050
+ const { accessPrevouts, accessPrevoutsInSubCall } = methodInfo.accessInfo;
2051
+ const { isPublic } = methodInfo;
2052
+ const shouldAppendArguments = accessPrevouts && isPublic;
2053
+ const shouldAppendThisAssignment = accessPrevouts && accessPrevoutsInSubCall && isPublic;
2054
+ const shouldAccessThis = (!isPublic || accessPrevoutsInSubCall) && accessPrevouts;
2055
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2056
+ });
2057
+ }
2058
+ shouldAutoAppendPrevout(node) {
2059
+ return this._shouldAutoAppend(node, (methodInfo) => {
2060
+ const { accessPrevout, accessPrevoutInSubCall } = methodInfo.accessInfo;
2061
+ const { isPublic } = methodInfo;
2062
+ const shouldAppendArguments = accessPrevout && isPublic;
2063
+ const shouldAppendThisAssignment = accessPrevout && accessPrevoutInSubCall && isPublic;
2064
+ const shouldAccessThis = (!isPublic || accessPrevoutInSubCall) && accessPrevout;
2065
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2066
+ });
2067
+ }
2068
+ shouldAutoAppendSpentScripts(node) {
2069
+ return this._shouldAutoAppend(node, (methodInfo) => {
2070
+ const { accessSpentScripts, accessSpentScriptsInSubCall } = methodInfo.accessInfo;
2071
+ const { isPublic } = methodInfo;
2072
+ const shouldAppendArguments = accessSpentScripts && isPublic;
2073
+ const shouldAppendThisAssignment = accessSpentScripts && accessSpentScriptsInSubCall && isPublic;
2074
+ const shouldAccessThis = (!isPublic || accessSpentScriptsInSubCall) && accessSpentScripts;
2075
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2076
+ });
2077
+ }
2078
+ shouldAutoAppendSpentAmounts(node) {
2079
+ return this._shouldAutoAppend(node, (methodInfo) => {
2080
+ const { accessSpentAmounts, accessSpentAmountsInSubCall } = methodInfo.accessInfo;
2081
+ const { isPublic } = methodInfo;
2082
+ const shouldAppendArguments = accessSpentAmounts && isPublic;
2083
+ const shouldAppendThisAssignment = accessSpentAmounts && accessSpentAmountsInSubCall && isPublic;
2084
+ const shouldAccessThis = (!isPublic || accessSpentAmountsInSubCall) && accessSpentAmounts;
2085
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2086
+ });
2087
+ }
2088
+ shouldAutoAppendSpentDataHashes(node) {
2089
+ return this._shouldAutoAppend(node, (methodInfo) => {
2090
+ const { accessSpentDataHashes, accessSpentDataHashesInSubCall } = methodInfo.accessInfo;
2091
+ const { isPublic } = methodInfo;
2092
+ const shouldAppendArguments = accessSpentDataHashes && isPublic;
2093
+ const shouldAppendThisAssignment = accessSpentDataHashes && accessSpentDataHashesInSubCall && isPublic;
2094
+ const shouldAccessThis = (!isPublic || accessSpentDataHashesInSubCall) && accessSpentDataHashes;
2095
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2096
+ });
2097
+ }
2098
+ shouldAutoAppendStateArgs(node) {
2099
+ return this._shouldAutoAppend(node, (methodInfo) => {
2100
+ const { accessState, accessStateInSubCall } = methodInfo.accessInfo;
2101
+ const { isPublic } = methodInfo;
2102
+ const shouldAppendArguments = accessState && isPublic;
2103
+ const shouldAppendThisAssignment = accessState && accessStateInSubCall && isPublic;
2104
+ const shouldAccessThis = (!isPublic || accessStateInSubCall) && accessState;
2105
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2106
+ });
2107
+ }
2108
+ shouldAutoAppendPrevTxHashPreimageArgs(node) {
2109
+ return this._shouldAutoAppend(node, (methodInfo) => {
2110
+ const { accessBacktrace, accessBacktraceInSubCall } = methodInfo.accessInfo;
2111
+ const { isPublic } = methodInfo;
2112
+ const shouldAppendArguments = accessBacktrace && isPublic;
2113
+ const shouldAppendThisAssignment = accessBacktrace && accessBacktraceInSubCall && isPublic;
2114
+ const shouldAccessThis = (!isPublic || accessBacktraceInSubCall) && accessBacktrace;
2115
+ return [shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis];
2116
+ });
2117
+ }
2118
+ static isStaticMethod(node) {
2119
+ if (typescript_1.default.isMethodDeclaration(node) && (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword)) {
2120
+ return true;
2121
+ }
2122
+ return false;
2123
+ }
2124
+ static isPublicMethod(node) {
2125
+ if (typescript_1.default.isMethodDeclaration(node) &&
2126
+ (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.PublicKeyword) &&
2127
+ this.findDecorator(node, DecoratorName.Method)) {
2128
+ return true;
2129
+ }
2130
+ return false;
2131
+ }
2132
+ shouldAutoAppendSighashPreimage(node) {
2133
+ // const hasSigHashPreimageParameters = node.parameters.find(
2134
+ // (p) => p.type?.getText() === 'SigHashPreimage',
2135
+ // );
2136
+ // if (hasSigHashPreimageParameters) {
2137
+ // return false;
2138
+ // }
2139
+ const methodInfo = this.findMethodInfo(node.name.getText());
2140
+ if (!methodInfo) {
2141
+ throw new types_1.UnknownError(`No method info found for \`${node.name.getText()}\``, this.getRange(node.name));
2142
+ }
2143
+ const { accessSHPreimage, accessSHPreimageInSubCall } = methodInfo.accessInfo;
2144
+ const { isPublic } = methodInfo;
2145
+ const shouldAppendArguments = accessSHPreimage && isPublic;
2146
+ const shouldAppendThisAssignment = accessSHPreimage && accessSHPreimageInSubCall && isPublic;
2147
+ const shouldAccessThis = (!isPublic || accessSHPreimageInSubCall) && accessSHPreimage;
2148
+ return { shouldAppendArguments, shouldAppendThisAssignment, shouldAccessThis };
2149
+ }
2150
+ transformParameter(node, toSection) {
2151
+ return toSection
2152
+ .appendWith(this, (toSec) => {
2153
+ if (!node.type) {
2154
+ throw new types_1.TranspileError(`Untransformable parameter: '${node.getText()}', all parameters should be typed explicitly`, this.getRange(node));
2155
+ }
2156
+ return this.transformEnclosingTypeNode(node.type, toSec);
2157
+ })
2158
+ .append(' ')
2159
+ .append(node.name.getText(), this.getCoordinates(node.name.getStart()));
2160
+ }
2161
+ transformStatement(node, toSection) {
2162
+ switch (node.kind) {
2163
+ case typescript_1.default.SyntaxKind.Block: {
2164
+ return toSection
2165
+ .append('{')
2166
+ .appendWith(this, (blockSec) => {
2167
+ node.statements.forEach((stmt) => {
2168
+ blockSec.append('\n').appendWith(this, (stmtsSec) => {
2169
+ return this.transformStatement(stmt, stmtsSec);
2170
+ });
2171
+ });
2172
+ return blockSec;
2173
+ }, true)
2174
+ .append('\n}');
2175
+ }
2176
+ case typescript_1.default.SyntaxKind.ExpressionStatement: {
2177
+ const s = node;
2178
+ if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
2179
+ /^super(\s*)\(.*\)/.test(s.expression.getText())) {
2180
+ return toSection;
2181
+ }
2182
+ else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
2183
+ /^console\./.test(s.expression.getText())) {
2184
+ return toSection;
2185
+ }
2186
+ else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
2187
+ /^this\.debug\./.test(s.expression.getText())) {
2188
+ return toSection;
2189
+ }
2190
+ else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
2191
+ /^this\.init\(/.test(s.expression.getText())) {
2192
+ return toSection;
2193
+ }
2194
+ else if (s.expression.kind === typescript_1.default.SyntaxKind.CallExpression &&
2195
+ /^this\.checkInputState\(/.test(s.expression.getText())) {
2196
+ return this.transformCallCheckInputState(s.expression, toSection);
2197
+ }
2198
+ return this.transformExpression(s.expression, toSection).append(';');
2199
+ }
2200
+ case typescript_1.default.SyntaxKind.VariableStatement: {
2201
+ const stmt = node;
2202
+ if (stmt.declarationList.declarations.length > 1) {
2203
+ throw new types_1.TranspileError(`Untransformable statement: '${node.getText()}'`, this.getRange(node));
2204
+ }
2205
+ const d = stmt.declarationList.declarations[0];
2206
+ if (!d.initializer) {
2207
+ throw new types_1.TranspileError(`Untransformable statement: '${node.getText()}'`, this.getRange(node));
2208
+ }
2209
+ if (this.isCtcDeclaration(d)) {
2210
+ return toSection;
2211
+ }
2212
+ else {
2213
+ // use `d.type` as type context node if it exists,
2214
+ // otherwise use `d.initializer` to provide type context, so we can leverage the type inference.
2215
+ if (d.type) {
2216
+ return this.transformEnclosingTypeNode(d.type, toSection)
2217
+ .append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))
2218
+ .append(' = ')
2219
+ .appendWith(this, (toSec) => {
2220
+ return this.transformExpression(d.initializer, toSec);
2221
+ })
2222
+ .append(';');
2223
+ }
2224
+ const type = this._checker.getTypeAtLocation(d.initializer);
2225
+ if (type.flags === typescript_1.default.TypeFlags.String) {
2226
+ // fix https://github.com/sCrypt-Inc/scrypt-ts/issues/390
2227
+ const coordinates = this.getCoordinates(d.initializer.getStart());
2228
+ return toSection
2229
+ .append('bytes', coordinates)
2230
+ .append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))
2231
+ .append(' = ')
2232
+ .appendWith(this, (toSec) => {
2233
+ return this.transformExpression(d.initializer, toSec);
2234
+ })
2235
+ .append(';');
2236
+ }
2237
+ return this.transformEnclosingTypeNode(d.initializer, toSection)
2238
+ .append(` ${d.name.getText()}`, this.getCoordinates(d.name.getStart()))
2239
+ .append(' = ')
2240
+ .appendWith(this, (toSec) => {
2241
+ return this.transformExpression(d.initializer, toSec);
2242
+ })
2243
+ .append(';');
2244
+ }
2245
+ }
2246
+ case typescript_1.default.SyntaxKind.ReturnStatement: {
2247
+ const s = node;
2248
+ if (!s.expression) {
2249
+ // If only "return", then the method must be a void type.
2250
+ // The return stmt gets handled elsewhere.
2251
+ return toSection;
2252
+ }
2253
+ return toSection
2254
+ .append(`return `, this.getCoordinates(s.getStart()))
2255
+ .appendWith(this, (toSec) => {
2256
+ return this.transformExpression(s.expression, toSec);
2257
+ })
2258
+ .append(';');
2259
+ }
2260
+ case typescript_1.default.SyntaxKind.IfStatement: {
2261
+ const s = node;
2262
+ toSection
2263
+ .append(`if(`, this.getCoordinates(s.getStart()))
2264
+ .appendWith(this, (toSec) => {
2265
+ return this.transformExpression(s.expression, toSec);
2266
+ })
2267
+ .append(`) `)
2268
+ .appendWith(this, (toSec) => {
2269
+ return this.transformStatement(s.thenStatement, toSec);
2270
+ });
2271
+ if (s.elseStatement) {
2272
+ toSection.append(` else `).appendWith(this, (toSec) => {
2273
+ return this.transformStatement(s.elseStatement, toSec);
2274
+ });
2275
+ }
2276
+ return toSection;
2277
+ }
2278
+ case typescript_1.default.SyntaxKind.ForStatement: {
2279
+ const s = node;
2280
+ let inductionVar = undefined;
2281
+ if (s.initializer?.kind === typescript_1.default.SyntaxKind.VariableDeclarationList) {
2282
+ const ivDeclare = s.initializer.declarations[0];
2283
+ // initializer expr must match `let $i = 0;`
2284
+ if (ivDeclare.initializer.getText() === '0' ||
2285
+ ivDeclare.initializer.getText() === '0n') {
2286
+ inductionVar = ivDeclare.name;
2287
+ }
2288
+ }
2289
+ if (!inductionVar) {
2290
+ throw new types_1.TranspileError(`\`for\` statement in \`@method\` should have induction variable declaration as: 'for(let $i = 0; ...; ...)'`, this.getRange(s));
2291
+ }
2292
+ let loopCount = undefined;
2293
+ if (s.condition?.kind === typescript_1.default.SyntaxKind.BinaryExpression) {
2294
+ const cond = s.condition;
2295
+ // condition expr must match `$i < $constNum;`
2296
+ const condVarName = cond.left.getText();
2297
+ if (condVarName === inductionVar.getText()) {
2298
+ if (cond.operatorToken.kind !== typescript_1.default.SyntaxKind.LessThanToken) {
2299
+ throw new types_1.TranspileError(`\`for\` statement in \`@method\` should only have a \`<\`(lessthan) operator: 'for(...; $i < $constNum; ...)'`, this.getRange(cond.operatorToken));
2300
+ }
2301
+ if (this.isCtcExpression(cond.right)) {
2302
+ if (this.isParameterNode(cond.right)) {
2303
+ loopCount = cond.right.getText();
2304
+ }
2305
+ else {
2306
+ loopCount = this.evalCtcExpression(cond.right);
2307
+ }
2308
+ }
2309
+ }
2310
+ }
2311
+ if (loopCount === undefined) {
2312
+ throw new types_1.TranspileError(`\`for\` statement in \`@method\` should have condition expression as: 'for(...; $i < $constNum; ...)'`, this.getRange(s));
2313
+ }
2314
+ let postIncIV = false;
2315
+ // incrementor expr must match `$i++`
2316
+ if (s.incrementor?.kind === typescript_1.default.SyntaxKind.PostfixUnaryExpression) {
2317
+ const inc = s.incrementor;
2318
+ if (inc.operator === typescript_1.default.SyntaxKind.PlusPlusToken &&
2319
+ inc.operand.getText() === inductionVar.getText()) {
2320
+ postIncIV = true;
2321
+ }
2322
+ }
2323
+ if (!postIncIV) {
2324
+ throw new types_1.TranspileError(`\`for\` statement in \`@method\` should have incrementor expression as: 'for(...; ...; $i++)'`, this.getRange(s.incrementor));
2325
+ }
2326
+ return toSection
2327
+ .append('loop (')
2328
+ .append(loopCount)
2329
+ .append(') : ')
2330
+ .append(`${inductionVar.getText()} `, this.getCoordinates(inductionVar.getStart()))
2331
+ .appendWith(this, (toSec) => {
2332
+ return this.transformStatement(s.statement, toSec);
2333
+ });
2334
+ }
2335
+ default: {
2336
+ throw new types_1.TranspileError(`Untransformable statement: '${node.getText()}'`, this.getRange(node));
2337
+ }
2338
+ }
2339
+ }
2340
+ transformExpression(node, toSection) {
2341
+ const srcLoc = this.getCoordinates(node.getStart());
2342
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
2343
+ const map = new Map();
2344
+ map.set(typescript_1.default.SyntaxKind.NumericLiteral, (node, toSection) => toSection.append(`${node.getText()}`, srcLoc));
2345
+ map.set(typescript_1.default.SyntaxKind.StringLiteral, (node, toSection) => this.transformStringLiteralExpression(node, toSection));
2346
+ map.set(typescript_1.default.SyntaxKind.BigIntLiteral, (node, toSection) => this.transformBigIntLiteralExpression(node, toSection));
2347
+ map.set(typescript_1.default.SyntaxKind.ArrayLiteralExpression, (node, toSection) => this.transformArrayLiteralExpression(node, toSection));
2348
+ map.set(typescript_1.default.SyntaxKind.ObjectLiteralExpression, (node, toSection) => this.transformObjectLiteralExpression(node, toSection));
2349
+ map.set(typescript_1.default.SyntaxKind.CallExpression, (node, toSection) => this.transformCallExpression(node, toSection));
2350
+ map.set(typescript_1.default.SyntaxKind.Identifier, (node, toSection) => this.transformIdentifierExpression(node, toSection));
2351
+ map.set(typescript_1.default.SyntaxKind.BinaryExpression, (node, toSection) => this.transformBinaryExpression(node, toSection));
2352
+ map.set(typescript_1.default.SyntaxKind.PropertyAccessExpression, (node, toSection) => this.transformPropertyAccessExpression(node, toSection));
2353
+ map.set(typescript_1.default.SyntaxKind.ArrowFunction, (node, toSection) => this.transformArrowFunctionExpression(node, toSection));
2354
+ map.set(typescript_1.default.SyntaxKind.NewExpression, (node, toSection) => this.transformNewExpression(node, toSection));
2355
+ map.set(typescript_1.default.SyntaxKind.ThisKeyword, (_node, toSection) => toSection.append('this', srcLoc));
2356
+ map.set(typescript_1.default.SyntaxKind.TrueKeyword, (_node, toSection) => toSection.append('true', srcLoc));
2357
+ map.set(typescript_1.default.SyntaxKind.FalseKeyword, (_node, toSection) => toSection.append('false', srcLoc));
2358
+ map.set(typescript_1.default.SyntaxKind.SuperKeyword, (_node, toSection) => toSection);
2359
+ map.set(typescript_1.default.SyntaxKind.ElementAccessExpression, (node, toSection) => this.transformElementAccessExpression(node, toSection));
2360
+ map.set(typescript_1.default.SyntaxKind.ConditionalExpression, (node, toSection) => this.transformConditionalExpression(node, toSection));
2361
+ map.set(typescript_1.default.SyntaxKind.ParenthesizedExpression, (node, toSection) => this.transformParenthesizedExpression(node, toSection));
2362
+ map.set(typescript_1.default.SyntaxKind.PostfixUnaryExpression, (node, toSection) => this.transformPostfixUnaryExpression(node, toSection));
2363
+ map.set(typescript_1.default.SyntaxKind.PrefixUnaryExpression, (node, toSection) => this.transformPrefixUnaryExpression(node, toSection));
2364
+ map.set(typescript_1.default.SyntaxKind.AsExpression, (node, toSection) => toSection.appendWith(this, (toSec) => this.transformExpression(node.expression, toSec)));
2365
+ const func = map.get(node.kind);
2366
+ if (func === undefined) {
2367
+ throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[node.kind]}: '${node.getText()}'`, this.getRange(node));
2368
+ }
2369
+ return func(node, toSection);
2370
+ }
2371
+ // transform the type enclosed in the node.
2372
+ transformEnclosingTypeNode(node, toSection) {
2373
+ const type = this._checker.getTypeAtLocation(node);
2374
+ return this.transformType(type, node, toSection);
2375
+ }
2376
+ isByteStringNode(node) {
2377
+ const type = this._checker.getTypeAtLocation(node);
2378
+ const typeString = this.type2ResolvedName(type);
2379
+ return typeString === 'ByteString' || typeString === 'OpCodeType';
2380
+ }
2381
+ // `typeStrCtx` is the type's literal name or the text of the node which encloses the type.
2382
+ transformType(type, node, toSection) {
2383
+ const typeStrCtx = node.getText();
2384
+ const coordinates = this.getCoordinates(node.getStart());
2385
+ switch (type.flags) {
2386
+ case typescript_1.default.TypeFlags.Union + typescript_1.default.TypeFlags.Boolean: // This is the real internal type of `boolean`, it's a union of `true` and `false`
2387
+ case typescript_1.default.TypeFlags.BooleanLiteral:
2388
+ case typescript_1.default.TypeFlags.Boolean: {
2389
+ return toSection.append('bool', coordinates);
2390
+ }
2391
+ case typescript_1.default.TypeFlags.Union + typescript_1.default.TypeFlags.EnumLiteral:
2392
+ case typescript_1.default.TypeFlags.BigIntLiteral:
2393
+ case typescript_1.default.TypeFlags.BigInt: {
2394
+ return toSection.append('int', coordinates);
2395
+ }
2396
+ case typescript_1.default.TypeFlags.NumberLiteral:
2397
+ case typescript_1.default.TypeFlags.Number: {
2398
+ if (typescript_1.default.isParameter(node.parent)) {
2399
+ return toSection.append('static const int', coordinates);
2400
+ }
2401
+ throw new types_1.TranspileError(`Untransformable type \`number\` here, please use type \`bigint\` instead`, this.getRange(node));
2402
+ }
2403
+ case typescript_1.default.TypeFlags.Intersection:
2404
+ case typescript_1.default.TypeFlags.Object: {
2405
+ const typeString = this.type2ResolvedName(type);
2406
+ // for declared object literal type, like `x : {prop: number}`
2407
+ if (typeString === '__type') {
2408
+ throw new types_1.TranspileError(`Untransformable literal object type: '${typeStrCtx}'`, this.getRange(node));
2409
+ }
2410
+ // for inferred object literal type, like `x = {prop: 1}`
2411
+ if (typeString === '__object') {
2412
+ throw new types_1.TranspileError(`Untransformable literal object type: '${typeStrCtx}'`, this.getRange(node));
2413
+ }
2414
+ // for bigint & number wrapper
2415
+ if (typeString === 'BigInt') {
2416
+ return toSection.append('int', coordinates);
2417
+ }
2418
+ if (typeString === 'Number') {
2419
+ throw new types_1.TranspileError(`Untransformable type \`${typeStrCtx}\` here, please use type \`bigint\` instead`, this.getRange(node));
2420
+ }
2421
+ // for string wrapper
2422
+ if (typeString === 'String') {
2423
+ throw new types_1.TranspileError(`Untransformable type \`${typeStrCtx}\` here, please use type \`ByteString\` instead`, this.getRange(node));
2424
+ }
2425
+ const isFixedArray = (type) => {
2426
+ if (type.isUnionOrIntersection() && type.types.length === 2) {
2427
+ const t1 = this._checker.typeToString(type.types[0]);
2428
+ const t2 = this._checker.typeToString(type.types[1]);
2429
+ if (t1.endsWith('[]') && /\{\slength:\s((\d+)|(number)|(any));\s\}/.test(t2)) {
2430
+ return true;
2431
+ }
2432
+ }
2433
+ return false;
2434
+ };
2435
+ if (isFixedArray(type)) {
2436
+ const getBaseElemTypeAndLengths = (typeRef, arrLens) => {
2437
+ if (typeRef.isUnionOrIntersection() && isFixedArray(typeRef)) {
2438
+ const FixedArrayLenString = this._checker.typeToString(typeRef.types[1]);
2439
+ const m = /(\d)+/.exec(FixedArrayLenString);
2440
+ if (m) {
2441
+ const fst = this._checker.getTypeArguments(typeRef.types[0]);
2442
+ return getBaseElemTypeAndLengths(fst[0], arrLens.concat(parseInt(m[0])));
2443
+ }
2444
+ else {
2445
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2446
+ }
2447
+ }
2448
+ else {
2449
+ return { typeRef: typeRef, arrLens };
2450
+ }
2451
+ };
2452
+ const getBaseElemTypeAndLengthsIncludeCTC = (tNode, arrLens) => {
2453
+ const typeRef = this._checker.getTypeAtLocation(tNode);
2454
+ if (typeRef.isUnionOrIntersection() && isFixedArray(typeRef)) {
2455
+ const FixedArrayLenString = this._checker.typeToString(typeRef.types[1]);
2456
+ if (typescript_1.default.isTypeReferenceNode(tNode)) {
2457
+ if (FixedArrayLenString === '{ length: number; }' &&
2458
+ tNode.typeArguments &&
2459
+ tNode.typeArguments.length == 2 &&
2460
+ typescript_1.default.isTypeQueryNode(tNode.typeArguments[1])) {
2461
+ if (this.isParameterNode(tNode.typeArguments[1].exprName)) {
2462
+ return getBaseElemTypeAndLengthsIncludeCTC(tNode.typeArguments[0], arrLens.concat(tNode.typeArguments[1].exprName.getText()));
2463
+ }
2464
+ else {
2465
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2466
+ }
2467
+ }
2468
+ else if (FixedArrayLenString === '{ length: any; }' &&
2469
+ tNode.typeArguments &&
2470
+ tNode.typeArguments.length == 2 &&
2471
+ typescript_1.default.isUnionTypeNode(tNode.typeArguments[1]) &&
2472
+ typescript_1.default.isTypeQueryNode(tNode.typeArguments[1].types[0])) {
2473
+ if (this.isParameterNode(tNode.typeArguments[1].types[0].exprName)) {
2474
+ return getBaseElemTypeAndLengthsIncludeCTC(tNode.typeArguments[0], arrLens.concat(tNode.typeArguments[1].types[0].exprName.getText()));
2475
+ }
2476
+ else {
2477
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2478
+ }
2479
+ }
2480
+ else {
2481
+ const m = /(\d)+/.exec(FixedArrayLenString);
2482
+ if (m) {
2483
+ if (tNode.typeArguments && tNode.typeArguments[0]) {
2484
+ return getBaseElemTypeAndLengthsIncludeCTC(tNode.typeArguments[0], arrLens.concat(parseInt(m[0])));
2485
+ }
2486
+ else {
2487
+ const fst = this._checker.getTypeArguments(typeRef.types[0]);
2488
+ return getBaseElemTypeAndLengths(fst[0], arrLens.concat(parseInt(m[0])));
2489
+ }
2490
+ }
2491
+ else {
2492
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2493
+ }
2494
+ }
2495
+ }
2496
+ else {
2497
+ const m = /(\d)+/.exec(FixedArrayLenString);
2498
+ if (m) {
2499
+ const fst = this._checker.getTypeArguments(typeRef.types[0]);
2500
+ return getBaseElemTypeAndLengths(fst[0], arrLens.concat(parseInt(m[0])));
2501
+ }
2502
+ else {
2503
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2504
+ }
2505
+ }
2506
+ }
2507
+ else {
2508
+ return { typeRef: typeRef, arrLens };
2509
+ }
2510
+ };
2511
+ const { typeRef: baseElemType, arrLens } = getBaseElemTypeAndLengthsIncludeCTC(node, []);
2512
+ this.transformType(baseElemType, node, toSection).append(`${arrLens.map((i) => '[' + i + ']').join('')}`, coordinates);
2513
+ }
2514
+ else if (typeString === 'Array' || typeString === '[]') {
2515
+ const isOldFixedArray = (typeRef) => {
2516
+ if (typeRef.typeArguments.length > 0) {
2517
+ const typeString = this.type2ResolvedName(typeRef.typeArguments[0]);
2518
+ return typeString.includes('Flavor') && typeString.includes('FixedArray');
2519
+ }
2520
+ else {
2521
+ return false;
2522
+ }
2523
+ };
2524
+ if (isOldFixedArray(type)) {
2525
+ const getBaseElemTypeAndLengths = (typeRef, arrLens) => {
2526
+ if (typeRef.typeArguments.length > 0) {
2527
+ const innerTypeRef = typeRef.typeArguments[0];
2528
+ if (innerTypeRef.aliasTypeArguments.length > 1) {
2529
+ const FixedArrayString = this.type2ResolvedName(innerTypeRef.aliasTypeArguments[1]);
2530
+ const m = /(\d)+/.exec(FixedArrayString);
2531
+ if (m) {
2532
+ return getBaseElemTypeAndLengths(innerTypeRef.aliasTypeArguments[0], arrLens.concat(parseInt(m[0])));
2533
+ }
2534
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2535
+ }
2536
+ else {
2537
+ return getBaseElemTypeAndLengths(innerTypeRef, arrLens.concat(typeRef.typeArguments.length));
2538
+ }
2539
+ }
2540
+ else {
2541
+ return { typeRef, arrLens };
2542
+ }
2543
+ };
2544
+ const { typeRef: baseElemType, arrLens } = getBaseElemTypeAndLengths(type, []);
2545
+ this.transformType(baseElemType, node, toSection).append(`${arrLens.map((i) => '[' + i + ']').join('')}`, coordinates);
2546
+ return toSection;
2547
+ }
2548
+ else {
2549
+ throw new types_1.TranspileError(`Untransformable type \`Array\`, please use type \`FixedArray\` instead`, this.getRange(node));
2550
+ }
2551
+ }
2552
+ else if (typeString.startsWith('[') && typeString.endsWith(']')) {
2553
+ // built-in type `FixedArray` goes here.
2554
+ const getBaseElemTypeAndLengths = (typeRef, arrLens) => {
2555
+ if (typeRef.typeArguments.length > 0) {
2556
+ const innerTypeRef = typeRef.typeArguments[0];
2557
+ return getBaseElemTypeAndLengths(innerTypeRef, arrLens.concat(typeRef.typeArguments.length));
2558
+ }
2559
+ else {
2560
+ return { typeRef, arrLens };
2561
+ }
2562
+ };
2563
+ const { typeRef: baseElemType, arrLens } = getBaseElemTypeAndLengths(type, []);
2564
+ this.transformType(baseElemType, node, toSection).append(`${arrLens.map((i) => '[' + i + ']').join('')}`, coordinates);
2565
+ }
2566
+ else {
2567
+ const t = (0, utils_1.toBuiltinsTypes)(typeString);
2568
+ if (t) {
2569
+ toSection.append(t, coordinates);
2570
+ }
2571
+ else {
2572
+ // all user defined or std types go here.
2573
+ toSection.append(typeString, coordinates);
2574
+ if (type.symbol) {
2575
+ this.saveSymbol(typeString, type.symbol);
2576
+ }
2577
+ }
2578
+ }
2579
+ break;
2580
+ }
2581
+ case typescript_1.default.TypeFlags.Any: {
2582
+ toSection.append('auto', coordinates);
2583
+ break;
2584
+ }
2585
+ case typescript_1.default.TypeFlags.String: {
2586
+ throw new types_1.TranspileError(`Untransformable type \`${typeStrCtx}\` here, please use type \`ByteString\` instead`, this.getRange(node));
2587
+ }
2588
+ default: {
2589
+ if (typescript_1.default.isTypeReferenceNode(node)) {
2590
+ // for boolean wrapper
2591
+ if (this.type2ResolvedName(type) === 'Bool') {
2592
+ return toSection.append('bool', coordinates);
2593
+ }
2594
+ throw new types_1.TranspileError(`Untransformable type : '${typeStrCtx}'`, this.getRange(node));
2595
+ }
2596
+ else {
2597
+ throw new types_1.TranspileError(`Untransformable type : '${typeStrCtx}', missing explicitly declared type`, this.getRange(node));
2598
+ }
2599
+ }
2600
+ }
2601
+ return toSection;
2602
+ }
2603
+ saveSymbol(symbolName, symbol) {
2604
+ // skip importing or local declaration for these keyword symbols.
2605
+ if (['SmartContract', 'OpCode'].includes(symbolName)) {
2606
+ return;
2607
+ }
2608
+ const symbolSourceFile = this.findDeclarationFile(symbol);
2609
+ if (symbolSourceFile === this._srcFile) {
2610
+ this._localTypeSymbols.set(symbolName, symbol);
2611
+ }
2612
+ else {
2613
+ this._importedTypeSymbols.set(symbolName, symbol);
2614
+ }
2615
+ }
2616
+ transformModifiers(node, toSection) {
2617
+ const modifiers = typescript_1.default.getModifiers(node);
2618
+ if (modifiers) {
2619
+ modifiers.forEach((modifier) => {
2620
+ switch (modifier.kind) {
2621
+ case typescript_1.default.SyntaxKind.PublicKeyword: {
2622
+ toSection.append('public ', this.getCoordinates(modifier.getStart()));
2623
+ break;
2624
+ }
2625
+ case typescript_1.default.SyntaxKind.PrivateKeyword: {
2626
+ toSection.append('private ', this.getCoordinates(modifier.getStart()));
2627
+ break;
2628
+ }
2629
+ case typescript_1.default.SyntaxKind.ProtectedKeyword: {
2630
+ // transform to nothing in scrypt
2631
+ break;
2632
+ }
2633
+ case typescript_1.default.SyntaxKind.StaticKeyword: {
2634
+ toSection.append('static ', this.getCoordinates(modifier.getStart()));
2635
+ break;
2636
+ }
2637
+ case typescript_1.default.SyntaxKind.ReadonlyKeyword: {
2638
+ toSection.append('const ', this.getCoordinates(modifier.getStart()));
2639
+ break;
2640
+ }
2641
+ default: {
2642
+ throw new types_1.TranspileError(`Untransformable modifier kind ${modifier.kind}: '${modifier.getText()}'`, this.getRange(modifier));
2643
+ }
2644
+ }
2645
+ });
2646
+ }
2647
+ return toSection;
2648
+ }
2649
+ static findDecorator(node, decorator) {
2650
+ if (typescript_1.default.canHaveDecorators(node)) {
2651
+ return (typescript_1.default.getDecorators(node) || []).find((dec) => {
2652
+ return dec.expression.getText().match(new RegExp(`^${decorator}\\((.*)?\\)$`));
2653
+ });
2654
+ }
2655
+ return undefined;
2656
+ }
2657
+ static isProperty(node) {
2658
+ if (typescript_1.default.isPropertyDeclaration(node)) {
2659
+ const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
2660
+ if (decorator) {
2661
+ return true;
2662
+ }
2663
+ }
2664
+ return false;
2665
+ }
2666
+ static isMethod(node) {
2667
+ if (typescript_1.default.isMethodDeclaration(node)) {
2668
+ const decorator = Transpiler.findDecorator(node, DecoratorName.Method);
2669
+ if (decorator) {
2670
+ return true;
2671
+ }
2672
+ }
2673
+ return false;
2674
+ }
2675
+ isNonProp(node) {
2676
+ if (typescript_1.default.isPropertyDeclaration(node)) {
2677
+ const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
2678
+ if (!decorator) {
2679
+ return true;
2680
+ }
2681
+ }
2682
+ return false;
2683
+ }
2684
+ isStaticReadOnlyNonProp(node) {
2685
+ if (typescript_1.default.isPropertyDeclaration(node)) {
2686
+ const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
2687
+ return (!decorator &&
2688
+ (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword) &&
2689
+ (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword));
2690
+ }
2691
+ return false;
2692
+ }
2693
+ static isStateProperty(node) {
2694
+ if (typescript_1.default.isPropertyDeclaration(node)) {
2695
+ const decorator = Transpiler.findDecorator(node, DecoratorName.Prop);
2696
+ if (decorator) {
2697
+ return /^prop\((true)+\)$/.test(decorator.expression.getText());
2698
+ }
2699
+ }
2700
+ return false;
2701
+ }
2702
+ static getClassDeclaration(node) {
2703
+ if (!node.parent) {
2704
+ return undefined;
2705
+ }
2706
+ if (typescript_1.default.isClassDeclaration(node.parent)) {
2707
+ return node.parent;
2708
+ }
2709
+ return Transpiler.getClassDeclaration(node.parent);
2710
+ }
2711
+ static getMethodDeclaration(node) {
2712
+ if (!node.parent) {
2713
+ return undefined;
2714
+ }
2715
+ if (typescript_1.default.isMethodDeclaration(node.parent)) {
2716
+ return node.parent;
2717
+ }
2718
+ return Transpiler.getMethodDeclaration(node.parent);
2719
+ }
2720
+ static getIfStatement(node) {
2721
+ if (!node.parent) {
2722
+ return undefined;
2723
+ }
2724
+ if (typescript_1.default.isIfStatement(node.parent)) {
2725
+ return node.parent;
2726
+ }
2727
+ return Transpiler.getIfStatement(node.parent);
2728
+ }
2729
+ findMethodDeclaration(name) {
2730
+ const m = this._currentContract.members.find((m) => {
2731
+ return Transpiler.isMethod(m) && m.name.getText() === name;
2732
+ });
2733
+ if (m) {
2734
+ return m;
2735
+ }
2736
+ if (this.currentbaseContract) {
2737
+ return this.currentbaseContract.members.find((m) => {
2738
+ return Transpiler.isMethod(m) && m.name.getText() === name;
2739
+ });
2740
+ }
2741
+ return undefined;
2742
+ }
2743
+ isNonPropReferences(node, name) {
2744
+ return node.members
2745
+ .filter((member) => this.isNonProp(member))
2746
+ .map((m) => {
2747
+ const p = m;
2748
+ return p.name.getText();
2749
+ })
2750
+ .includes(name);
2751
+ }
2752
+ allPropertyDeclaration(node) {
2753
+ if (typescript_1.default.isClassDeclaration(node)) {
2754
+ return node.members.filter((member) => Transpiler.isProperty(member));
2755
+ }
2756
+ return [];
2757
+ }
2758
+ isCtcBinaryExpression(node) {
2759
+ const operators = [
2760
+ typescript_1.default.SyntaxKind.PlusToken,
2761
+ typescript_1.default.SyntaxKind.MinusToken,
2762
+ typescript_1.default.SyntaxKind.AsteriskToken,
2763
+ ];
2764
+ return (operators.indexOf(node.operatorToken.kind) !== -1 &&
2765
+ this.isCtcExpression(node.left) &&
2766
+ this.isCtcExpression(node.right));
2767
+ }
2768
+ isCtcPrefixUnaryExpression(node) {
2769
+ const operators = [typescript_1.default.SyntaxKind.PlusToken, typescript_1.default.SyntaxKind.MinusToken];
2770
+ return operators.indexOf(node.operator) !== -1 && this.isCtcExpression(node.operand);
2771
+ }
2772
+ isCtcParenthesizedExpression(node) {
2773
+ return this.isCtcExpression(node.expression);
2774
+ }
2775
+ isCtcExpression(node) {
2776
+ if (!node || !typescript_1.default.isExpression(node)) {
2777
+ return false;
2778
+ }
2779
+ if ((0, utils_1.isNumberLiteralExpr)(node)) {
2780
+ return true;
2781
+ }
2782
+ if (typescript_1.default.isIdentifier(node) || typescript_1.default.isPropertyAccessExpression(node)) {
2783
+ return this.isCtcIdentifierOrProperty(node);
2784
+ }
2785
+ if (typescript_1.default.isBinaryExpression(node)) {
2786
+ return this.isCtcBinaryExpression(node);
2787
+ }
2788
+ if (typescript_1.default.isPrefixUnaryExpression(node)) {
2789
+ return this.isCtcPrefixUnaryExpression(node);
2790
+ }
2791
+ if (typescript_1.default.isParenthesizedExpression(node)) {
2792
+ return this.isCtcParenthesizedExpression(node);
2793
+ }
2794
+ return false;
2795
+ }
2796
+ isCtcDeclaration(node) {
2797
+ if (typescript_1.default.isPropertyDeclaration(node) && node['initializer']) {
2798
+ if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword) &&
2799
+ (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword)) {
2800
+ return this.isCtcExpression(node['initializer']);
2801
+ }
2802
+ else if ((0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ConstKeyword)) {
2803
+ return this.isCtcExpression(node['initializer']);
2804
+ }
2805
+ }
2806
+ else if (typescript_1.default.isVariableDeclaration(node) &&
2807
+ node['initializer'] &&
2808
+ typescript_1.default.getCombinedNodeFlags(node) === typescript_1.default.NodeFlags.Const) {
2809
+ return this.isCtcExpression(node['initializer']);
2810
+ }
2811
+ else if (typescript_1.default.isParameter(node) && node.type) {
2812
+ return node.type?.kind === typescript_1.default.SyntaxKind.NumberKeyword;
2813
+ }
2814
+ return false;
2815
+ }
2816
+ isCtcIdentifierOrProperty(node) {
2817
+ const symbol = this._checker.getSymbolAtLocation(node);
2818
+ if (symbol) {
2819
+ if (symbol.valueDeclaration) {
2820
+ // local ctc
2821
+ return this.isCtcDeclaration(symbol.valueDeclaration);
2822
+ }
2823
+ // imported ctc
2824
+ return this.getImportedCtcValue(symbol) !== undefined;
2825
+ }
2826
+ return false;
2827
+ }
2828
+ isParameterNode(node) {
2829
+ const symbol = this._checker.getSymbolAtLocation(node);
2830
+ if (!symbol || !symbol.valueDeclaration) {
2831
+ return false;
2832
+ }
2833
+ return typescript_1.default.isParameter(symbol.valueDeclaration);
2834
+ }
2835
+ evalCtcBinaryExpression(node) {
2836
+ const left = this.evalCtcExpression(node.left);
2837
+ const right = this.evalCtcExpression(node.right);
2838
+ const operator = node.operatorToken.getText();
2839
+ return eval(`BigInt("${left}") ${operator} BigInt("${right}")`).toString().replace('n', '');
2840
+ }
2841
+ evalCtcPrefixUnaryExpression(node) {
2842
+ const operand = this.evalCtcExpression(node.operand);
2843
+ const operator = node.operator === typescript_1.default.SyntaxKind.MinusToken ? '-' : '';
2844
+ return eval(`${operator}BigInt("${operand}")`).toString().replace('n', '');
2845
+ }
2846
+ evalCtcParenthesizedExpression(node) {
2847
+ return this.evalCtcExpression(node.expression);
2848
+ }
2849
+ evalCtcExpression(node) {
2850
+ if ((0, utils_1.isNumberLiteralExpr)(node)) {
2851
+ return eval(node.getText()).toString().replace('n', '');
2852
+ }
2853
+ if (typescript_1.default.isIdentifier(node) || typescript_1.default.isPropertyAccessExpression(node)) {
2854
+ const symbol = this._checker.getSymbolAtLocation(node);
2855
+ if (symbol.valueDeclaration) {
2856
+ return this.evalCtcExpression(symbol.valueDeclaration['initializer']);
2857
+ }
2858
+ const ctcValue = this.getImportedCtcValue(symbol);
2859
+ if (ctcValue) {
2860
+ return ctcValue;
2861
+ }
2862
+ }
2863
+ if (typescript_1.default.isBinaryExpression(node)) {
2864
+ return this.evalCtcBinaryExpression(node);
2865
+ }
2866
+ if (typescript_1.default.isPrefixUnaryExpression(node)) {
2867
+ return this.evalCtcPrefixUnaryExpression(node);
2868
+ }
2869
+ if (typescript_1.default.isParenthesizedExpression(node)) {
2870
+ return this.evalCtcParenthesizedExpression(node);
2871
+ }
2872
+ throw new types_1.TranspileError(`Cannot eval a non-CTC expression: '${node.getText()}'`, this.getRange(node));
2873
+ }
2874
+ getImportedCtcValue(symbol) {
2875
+ if (symbol.declarations) {
2876
+ const symbolDecl = (symbol.declarations ?? [])[0];
2877
+ if (symbolDecl && typescript_1.default.isImportSpecifier(symbolDecl)) {
2878
+ const importDecl = symbolDecl.parent.parent.parent;
2879
+ if (typescript_1.default.isImportDeclaration(importDecl)) {
2880
+ const moduleSpecifier = symbolDecl.parent.parent.parent.moduleSpecifier
2881
+ .getText()
2882
+ .replace(/['"]/g, '');
2883
+ const resolvedMoudle = typescript_1.default.resolveModuleName(moduleSpecifier, this._srcFile.fileName, this._compilerOptions, this._host);
2884
+ const ctcExportFile = resolvedMoudle.resolvedModule?.resolvedFileName;
2885
+ if (ctcExportFile) {
2886
+ const nodeType = this._checker.getTypeAtLocation(symbolDecl);
2887
+ if (nodeType.isNumberLiteral()) {
2888
+ return nodeType.value.toString().replace('n', '');
2889
+ }
2890
+ // for bigint literal
2891
+ if (nodeType.isLiteral() && nodeType.value.base10Value) {
2892
+ const negative = nodeType.value.negative ? '-' : '';
2893
+ return (negative +
2894
+ nodeType.value.base10Value.toString().replace('n', ''));
2895
+ }
2896
+ return Transpiler.topCtcs.get(`${(0, utils_1.sha1)(ctcExportFile)}:${symbolDecl.propertyName?.getText() || symbolDecl.name.getText()}`);
2897
+ }
2898
+ }
2899
+ }
2900
+ }
2901
+ return undefined;
2902
+ }
2903
+ transformCtcExpr(expr, toSection) {
2904
+ const coordinates = this.getCoordinates(expr.getStart());
2905
+ const ctcValue = this.evalCtcExpression(expr).toString().replace('n', '');
2906
+ toSection.append(ctcValue, coordinates);
2907
+ return toSection;
2908
+ }
2909
+ transformCtcNode(node, toSection) {
2910
+ if (this.isParameterNode(node)) {
2911
+ // ctc parameter
2912
+ const coordinates = this.getCoordinates(node.getStart());
2913
+ toSection.append(node.getText(), coordinates);
2914
+ return toSection;
2915
+ }
2916
+ const symbol = this._checker.getSymbolAtLocation(node);
2917
+ if (symbol && symbol.valueDeclaration) {
2918
+ // local ctc
2919
+ return this.transformCtcExpr(symbol.valueDeclaration['initializer'], toSection);
2920
+ }
2921
+ if (symbol) {
2922
+ const ctcValue = this.getImportedCtcValue(symbol);
2923
+ if (ctcValue) {
2924
+ // imported ctc
2925
+ return toSection.append(ctcValue, this.getCoordinates(node.getStart()));
2926
+ }
2927
+ }
2928
+ throw new types_1.TranspileError(`Cannot find ctc value: '${node.getText()}'`, this.getRange(node));
2929
+ }
2930
+ queryPropertyInitializedInStmt(node) {
2931
+ if (typescript_1.default.isExpressionStatement(node) && typescript_1.default.isBinaryExpression(node.expression)) {
2932
+ if (node.expression.operatorToken.getText() === '=' &&
2933
+ typescript_1.default.isPropertyAccessExpression(node.expression.left) &&
2934
+ node.expression.left.expression.getText() === 'this') {
2935
+ const symbol = this._checker.getSymbolAtLocation(node.expression.left.name);
2936
+ if (symbol &&
2937
+ symbol.valueDeclaration &&
2938
+ typescript_1.default.isPropertyDeclaration(symbol.valueDeclaration)) {
2939
+ return symbol.valueDeclaration;
2940
+ }
2941
+ }
2942
+ }
2943
+ return undefined;
2944
+ }
2945
+ hasProperties(node) {
2946
+ return (node.members.findIndex((m) => typescript_1.default.isPropertyDeclaration(m) &&
2947
+ typeof Transpiler.findDecorator(m, DecoratorName.Prop) !== 'undefined') > -1);
2948
+ }
2949
+ static isStaticProperty(node) {
2950
+ if (Transpiler.isProperty(node)) {
2951
+ return (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.StaticKeyword);
2952
+ }
2953
+ return false;
2954
+ }
2955
+ static isReadonlyProperty(node) {
2956
+ if (Transpiler.isProperty(node)) {
2957
+ return (0, utils_1.hasModifier)(node, typescript_1.default.SyntaxKind.ReadonlyKeyword);
2958
+ }
2959
+ return false;
2960
+ }
2961
+ onlyHasStaticProperties(node) {
2962
+ return node.members
2963
+ .filter((m) => typescript_1.default.isPropertyDeclaration(m))
2964
+ .every((m) => {
2965
+ return (Transpiler.findDecorator(m, DecoratorName.Prop) === undefined ||
2966
+ Transpiler.isStaticProperty(m));
2967
+ });
2968
+ }
2969
+ hasConstructor(node) {
2970
+ return node.members.findIndex((m) => typescript_1.default.isConstructorDeclaration(m)) > -1;
2971
+ }
2972
+ getConstructor(node) {
2973
+ return node.members.find((m) => typescript_1.default.isConstructorDeclaration(m));
2974
+ }
2975
+ checkConstructor(node) {
2976
+ if (this.hasProperties(node)) {
2977
+ if (this.onlyHasStaticProperties(node)) {
2978
+ return;
2979
+ }
2980
+ if (!this.hasConstructor(node)) {
2981
+ throw new types_1.TranspileError(`Untransformable contract: a smart contract must have an explicit constructor if it has at least one @prop.`, this.getRange(node));
2982
+ }
2983
+ if (this.isInherited(node)) {
2984
+ this.checkSetConstructorStmt(this.getConstructor(node));
2985
+ }
2986
+ else {
2987
+ this.checkSuperStmt(this.getConstructor(node));
2988
+ }
2989
+ }
2990
+ }
2991
+ getImportedPath(fulSymbolPath) {
2992
+ const filePath = path.relative(path.dirname(this._scryptFullPath), fulSymbolPath);
2993
+ if (!filePath.startsWith('.')) {
2994
+ return `./${filePath}`;
2995
+ }
2996
+ return filePath;
2997
+ }
2998
+ resolvePackageDir(symbolFileFullPath) {
2999
+ let searchDir = path.dirname(symbolFileFullPath);
3000
+ do {
3001
+ const locatePath = path.join(searchDir, 'package.json');
3002
+ if ((0, fs_1.existsSync)(locatePath)) {
3003
+ return path.dirname(locatePath);
3004
+ }
3005
+ if (path.join(searchDir, '..') === searchDir) {
3006
+ return undefined;
3007
+ }
3008
+ searchDir = path.join(searchDir, '..');
3009
+ // eslint-disable-next-line no-constant-condition
3010
+ } while (true);
3011
+ }
3012
+ getThirdPartyPackageName(packageDir) {
3013
+ const packageJson = JSON.parse((0, fs_1.readFileSync)(path.join(packageDir, 'package.json'), 'utf8'));
3014
+ return packageJson.name;
3015
+ }
3016
+ saveThirdPartySymbol(symbol, symbolName, fullSymbolPath) {
3017
+ const symbolFile = this.findDeclarationFile(symbol);
3018
+ if (!symbolFile)
3019
+ return;
3020
+ const symbolFileFullPath = symbolFile.fileName;
3021
+ const symbolPath = this.getRelativePathFromArtifacts(fullSymbolPath);
3022
+ let symbolDec = symbol.declarations[0];
3023
+ symbolDec = symbolDec['name'] || symbolDec;
3024
+ this._indexer.addSymbols([
3025
+ {
3026
+ name: symbolName,
3027
+ srcRange: {
3028
+ fileName: symbolFileFullPath,
3029
+ start: symbolFile.getLineAndCharacterOfPosition(symbolDec.getStart()),
3030
+ end: symbolFile.getLineAndCharacterOfPosition(symbolDec.getEnd()),
3031
+ },
3032
+ },
3033
+ ], symbolPath);
3034
+ }
3035
+ saveLocalSymbol(symbol, symbolName, fullSymbolPath) {
3036
+ const symbolFile = this.findDeclarationFile(symbol);
3037
+ if (!symbolFile)
3038
+ return;
3039
+ const symbolFileFullPath = symbolFile.fileName;
3040
+ const symbolPath = this.getRelativePathFromTsRoot(fullSymbolPath);
3041
+ let symbolDec = symbol.declarations[0];
3042
+ symbolDec = symbolDec['name'] || symbolDec;
3043
+ this._indexer.addSymbols([
3044
+ {
3045
+ name: symbolName,
3046
+ srcRange: {
3047
+ fileName: symbolFileFullPath,
3048
+ start: symbolFile.getLineAndCharacterOfPosition(symbolDec.getStart()),
3049
+ end: symbolFile.getLineAndCharacterOfPosition(symbolDec.getEnd()),
3050
+ },
3051
+ },
3052
+ ], symbolPath);
3053
+ }
3054
+ transformImports(allmissSym, relinker) {
3055
+ const importsSection = new EmittedSection();
3056
+ const imports = new Set();
3057
+ const missSymbols = new Map();
3058
+ const fromPkgName = relinker_1.Relinker.getFilePackageInfo(this._scryptFullPath).packageName;
3059
+ Array.from(this._importedTypeSymbols.entries()).forEach((entry) => {
3060
+ const symbolName = entry[0];
3061
+ const symbol = entry[1];
3062
+ const symbolFile = this.findDeclarationFile(symbol);
3063
+ if (!symbolFile)
3064
+ return;
3065
+ const symbolFileFullPath = symbolFile.fileName;
3066
+ if (this.isFromThirdParty(symbolFileFullPath)) {
3067
+ const thirdPartyBaseDir = this.resolvePackageDir(symbolFileFullPath);
3068
+ if (!thirdPartyBaseDir) {
3069
+ importsSection.errors.push(new types_1.TranspileError(`Invalid symbol '${symbolName}', missing \`package.json\``, {
3070
+ fileName: symbolFileFullPath,
3071
+ start: { line: -1, character: -1 },
3072
+ end: { line: -1, character: -1 },
3073
+ }));
3074
+ return;
3075
+ }
3076
+ const thirdPartyIndexFile = indexer_1.Indexer.queryIndexFile(path.dirname(symbolFileFullPath), thirdPartyBaseDir);
3077
+ if (!thirdPartyIndexFile) {
3078
+ if (symbol.flags !== typescript_1.default.SymbolFlags.TypeLiteral) {
3079
+ const message = symbolName === 'IContext'
3080
+ ? '`IContext` is not allowed to be defined in the contract'
3081
+ : `Invalid symbol '${symbolName}', missing \`scrypt.index.json\` in the third party root directory ${thirdPartyBaseDir}`;
3082
+ importsSection.errors.push(new types_1.TranspileError(message, {
3083
+ fileName: symbolFileFullPath,
3084
+ start: { line: -1, character: -1 },
3085
+ end: { line: -1, character: -1 },
3086
+ }));
3087
+ return;
3088
+ }
3089
+ else {
3090
+ // case: builtin types from scrypt-ts-btc which does not have an index file
3091
+ // const symbols = missSymbols.get(symbolFile) || new Map<string, ts.Symbol>();
3092
+ // symbols.set(symbolName, symbol);
3093
+ // missSymbols.set(symbolFile, symbols);
3094
+ }
3095
+ }
3096
+ else {
3097
+ const thirdPartyIndexer = new indexer_1.Indexer(thirdPartyIndexFile);
3098
+ const thirdPartyFullPath = thirdPartyIndexer.getFullPath(symbolName);
3099
+ if (!thirdPartyFullPath) {
3100
+ importsSection.errors.push(new types_1.TranspileError(symbolName === 'IContext'
3101
+ ? '`IContext` is not allowed to be defined in the contract'
3102
+ : `Invalid symbol '${symbolName}', missing index info of symbol ${symbolName} in the third party indexer file '${thirdPartyIndexFile}'`, {
3103
+ fileName: symbolFileFullPath,
3104
+ start: { line: -1, character: -1 },
3105
+ end: { line: -1, character: -1 },
3106
+ }));
3107
+ return;
3108
+ }
3109
+ else {
3110
+ this.saveThirdPartySymbol(symbol, symbolName, thirdPartyFullPath);
3111
+ imports.add(thirdPartyIndexer.getPackageFilePath(this.getThirdPartyPackageName(thirdPartyBaseDir), symbolName));
3112
+ }
3113
+ }
3114
+ }
3115
+ else {
3116
+ // local imports
3117
+ let symbolPath = this._indexer.getFullPath(symbolName);
3118
+ if (symbolPath && (0, fs_1.existsSync)(symbolPath)) {
3119
+ imports.add(this.getImportedPath(symbolPath));
3120
+ }
3121
+ else {
3122
+ this.saveLocalSymbol(symbol, symbolName, symbolFileFullPath);
3123
+ symbolPath = this._indexer.getFullPath(symbolName);
3124
+ imports.add(this.getImportedPath(symbolPath));
3125
+ const symbols = missSymbols.get(symbolFile) || new Map();
3126
+ symbols.set(symbolName, symbol);
3127
+ missSymbols.set(symbolFile, symbols);
3128
+ }
3129
+ }
3130
+ });
3131
+ // merge all miss import local symbols
3132
+ if (missSymbols.size > 0) {
3133
+ missSymbols.forEach((value, key) => {
3134
+ if (allmissSym.has(key)) {
3135
+ const map = allmissSym.get(key);
3136
+ value.forEach((v, k) => {
3137
+ map.set(k, v);
3138
+ });
3139
+ }
3140
+ else {
3141
+ allmissSym.set(key, value);
3142
+ }
3143
+ });
3144
+ // generate scrypt file for missing Imported local symbol
3145
+ allmissSym.forEach((symbols, symbolFile) => {
3146
+ const transpiler = new Transpiler(symbolFile, this._host, this._checker, this._tsRootDir, this._scryptOutDir, this._indexer, this._compilerOptions);
3147
+ transpiler.setLocalSymbols(symbols);
3148
+ transpiler.transform(allmissSym, relinker);
3149
+ // imports.add(transpiler._scryptRelativePath);
3150
+ });
3151
+ }
3152
+ // only import builtins for non-builtin packages
3153
+ if (fromPkgName !== BUILDIN_PACKAGE_NAME) {
3154
+ this.importAllBuiltins(imports);
3155
+ }
3156
+ imports.forEach((import_) => {
3157
+ importsSection
3158
+ .append('import ')
3159
+ .append(`"${import_.replaceAll('\\', '/')}"`)
3160
+ .append(';')
3161
+ .append('\n');
3162
+ });
3163
+ return importsSection;
3164
+ }
3165
+ loadBuiltinIndexer() {
3166
+ const scryptTsBtcDir = (0, utils_1.findPackageDir)(BUILDIN_PACKAGE_NAME, process.cwd());
3167
+ const builtinIndexerFile = indexer_1.Indexer.queryIndexFile(scryptTsBtcDir, path.join(scryptTsBtcDir, '..'));
3168
+ if (!builtinIndexerFile) {
3169
+ throw new Error(`Missing \`scrypt.index.json\` for the built-in types & libraries in the directory \`${scryptTsBtcDir}\``);
3170
+ }
3171
+ this._builtinIndexer = new indexer_1.Indexer(builtinIndexerFile);
3172
+ }
3173
+ importAllBuiltins(imports) {
3174
+ if (!this._currentContract) {
3175
+ // skip import builtins for non-contract file
3176
+ return;
3177
+ }
3178
+ const scryptTsBtcDir = (0, utils_1.findPackageDir)(BUILDIN_PACKAGE_NAME, process.cwd());
3179
+ const pkgName = this.getThirdPartyPackageName(scryptTsBtcDir);
3180
+ new Set(this._builtinIndexer.symbolInfos.keys()).forEach((symbolName) => {
3181
+ if (this._accessBuiltinsSymbols.has(symbolName)) {
3182
+ const builtinFullPath = this._builtinIndexer.getPackageFilePath(pkgName, symbolName);
3183
+ imports.add(builtinFullPath);
3184
+ }
3185
+ });
3186
+ }
3187
+ transformTypeLiteralAndInterfaces() {
3188
+ const structSecs = [];
3189
+ const enumSecs = [];
3190
+ this._localTypeSymbols.forEach((symbol, symbolTypeName) => {
3191
+ const symbolDec = symbol.declarations[0];
3192
+ if ([
3193
+ typescript_1.default.SyntaxKind.TypeLiteral,
3194
+ typescript_1.default.SyntaxKind.InterfaceDeclaration,
3195
+ typescript_1.default.SyntaxKind.TypeAliasDeclaration,
3196
+ ].includes(symbolDec.kind)) {
3197
+ const stSec = new EmittedSection().appendWith(this, (stSec) => {
3198
+ return stSec
3199
+ .append('\nstruct ')
3200
+ .append(`${symbolTypeName} {`, this.getCoordinates(symbolDec.getStart()))
3201
+ .appendWith(this, (fieldsSec) => {
3202
+ let members;
3203
+ if (typescript_1.default.SyntaxKind.TypeAliasDeclaration === symbolDec.kind) {
3204
+ // TODO: resolve recursive type alias
3205
+ const symbol = this._checker.getTypeAtLocation(symbolDec.type).symbol;
3206
+ members = symbol.members;
3207
+ }
3208
+ else {
3209
+ members = symbol.members;
3210
+ }
3211
+ members.forEach((memSymbol) => {
3212
+ fieldsSec.append('\n').appendWith(this, (fieldSec) => {
3213
+ return this.transformPropertySignature(memSymbol.valueDeclaration, fieldSec);
3214
+ });
3215
+ });
3216
+ return fieldsSec;
3217
+ }, true)
3218
+ .append('\n}');
3219
+ });
3220
+ structSecs.push(stSec);
3221
+ }
3222
+ else if (typescript_1.default.isEnumDeclaration(symbolDec)) {
3223
+ const enumDeclaration = symbolDec;
3224
+ const enumSec = new EmittedSection().appendWith(this, (enumSec) => {
3225
+ return enumSec
3226
+ .append('\nlibrary ')
3227
+ .append(`${symbolTypeName} {`, this.getCoordinates(symbolDec.getStart()))
3228
+ .appendWith(this, (enumMembersSec) => {
3229
+ let prevEnumMemValue = -1;
3230
+ enumDeclaration.members.forEach((enumMember) => {
3231
+ enumMembersSec
3232
+ .append('\n')
3233
+ .append(`static const int ${enumMember.name.getText()} = `)
3234
+ .appendWith(this, (enumMemberSec) => {
3235
+ if (enumMember.initializer) {
3236
+ const type = this._checker.getTypeAtLocation(enumMember.initializer);
3237
+ if (type.flags !== typescript_1.default.TypeFlags.NumberLiteral) {
3238
+ throw new types_1.TranspileError(`Untransformable enum member: '${symbolTypeName}.${enumMember.name.getText()}', only allowed number literal in enum`, this.getRange(enumMember));
3239
+ }
3240
+ prevEnumMemValue = parseInt(enumMember.initializer.getText());
3241
+ return this.transformExpression(enumMember.initializer, enumMemberSec);
3242
+ }
3243
+ else {
3244
+ prevEnumMemValue++;
3245
+ enumMemberSec.append(`${prevEnumMemValue}`);
3246
+ }
3247
+ return enumMemberSec;
3248
+ })
3249
+ .append(';');
3250
+ });
3251
+ return enumMembersSec;
3252
+ }, true)
3253
+ .append('\n}');
3254
+ });
3255
+ enumSecs.push(enumSec);
3256
+ }
3257
+ });
3258
+ return EmittedSection.join(...structSecs.concat(enumSecs));
3259
+ }
3260
+ toScryptBinary(node, operator) {
3261
+ switch (operator) {
3262
+ case '==':
3263
+ case '!=':
3264
+ case '+':
3265
+ case '-':
3266
+ case '+=':
3267
+ case '-=':
3268
+ case '<':
3269
+ case '>':
3270
+ case '<=':
3271
+ case '>=':
3272
+ case '&&':
3273
+ case '||':
3274
+ case '*':
3275
+ case '/':
3276
+ case '%':
3277
+ case '=':
3278
+ return operator;
3279
+ case '===':
3280
+ return '==';
3281
+ case '!==':
3282
+ return '!=';
3283
+ default:
3284
+ throw new types_1.TranspileError(`Untransformable binary operator: '${operator}'`, this.getRange(node));
3285
+ }
3286
+ }
3287
+ // SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken | SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken
3288
+ toScryptUnary(node, operator) {
3289
+ switch (operator) {
3290
+ case typescript_1.default.SyntaxKind.PlusPlusToken:
3291
+ return '++';
3292
+ case typescript_1.default.SyntaxKind.MinusMinusToken:
3293
+ return '--';
3294
+ case typescript_1.default.SyntaxKind.MinusToken:
3295
+ return '-';
3296
+ case typescript_1.default.SyntaxKind.ExclamationToken:
3297
+ return '!';
3298
+ default:
3299
+ throw new types_1.TranspileError(`Untransformable prefix unary operator kind ${typescript_1.default.SyntaxKind[operator]} in: ${node.getText()}`, this.getRange(node));
3300
+ }
3301
+ }
3302
+ type2ResolvedName(type) {
3303
+ // for basic types
3304
+ if (!type.symbol) {
3305
+ const typeName = this._checker.typeToString(type, undefined, typescript_1.default.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
3306
+ return typeName;
3307
+ }
3308
+ let typeName = type.symbol.escapedName.toString();
3309
+ // get scrypt struct name which might be a type alias or interface
3310
+ if (typeName === '__type') {
3311
+ typeName = type.aliasSymbol?.escapedName.toString() || typeName;
3312
+ }
3313
+ return typeName === 'boolean' ? 'Bool' : typeName;
3314
+ }
3315
+ getResolvedTypeName(node) {
3316
+ const type = this._checker.getTypeAtLocation(node);
3317
+ return this.type2ResolvedName(type);
3318
+ }
3319
+ // check if a call expression is a method call to a certain class's instance.
3320
+ isCertainClassPropertyAccessExpr(expr, clazz) {
3321
+ const objExpr = expr.expression;
3322
+ return this.getResolvedTypeName(objExpr) === clazz;
3323
+ }
3324
+ transformStringLiteralExpression(node, toSection) {
3325
+ if (node.parent.kind === typescript_1.default.SyntaxKind.CallExpression) {
3326
+ const srcLoc = this.getCoordinates(node.getStart());
3327
+ const parent = node.parent;
3328
+ if ((0, utils_1.allowByteStringLiteral)(parent)) {
3329
+ if (parent.expression.getText() === 'toByteString') {
3330
+ let literal = parent.arguments[0].getText();
3331
+ literal = literal.substring(1, literal.length - 1);
3332
+ if (parent.arguments.length == 1) {
3333
+ // one argument --> hex literal
3334
+ if (!/^([0-9a-fA-F]{2})*$/.test(literal)) {
3335
+ throw new types_1.TranspileError(`\`${literal}\` is not a valid hex literal`, this.getRange(node));
3336
+ }
3337
+ toSection.append(`b'${literal}'`, srcLoc);
3338
+ }
3339
+ else {
3340
+ // two arguments
3341
+ const isUtf8 = parent.arguments[1].getText();
3342
+ if (isUtf8 === 'false') {
3343
+ // hex literal
3344
+ if (!/^([0-9a-fA-F]{2})*$/.test(literal)) {
3345
+ throw new types_1.TranspileError(`\`${literal}\` is not a valid hex literal`, this.getRange(node));
3346
+ }
3347
+ toSection.append(`b'${literal}'`, srcLoc);
3348
+ }
3349
+ else if (isUtf8 === 'true') {
3350
+ // utf8 literal
3351
+ // Auto escape double quotes.
3352
+ const escaped = literal.replace(/"/g, '\\"');
3353
+ toSection.append(`"${escaped}"`, srcLoc);
3354
+ }
3355
+ else {
3356
+ throw new types_1.TranspileError('Only boolean literal can be passed to the second parameter of `toByteString`', this.getRange(node));
3357
+ }
3358
+ }
3359
+ }
3360
+ else {
3361
+ try {
3362
+ (0, utils_1.checkByteStringLiteral)(node);
3363
+ }
3364
+ catch (error) {
3365
+ throw new types_1.TranspileError(error.message, this.getRange(node));
3366
+ }
3367
+ }
3368
+ }
3369
+ else {
3370
+ throw new types_1.TranspileError(`String literal ${node.getText()} is not allowed here, please use \`toByteString\` instead`, this.getRange(node));
3371
+ }
3372
+ }
3373
+ else {
3374
+ throw new types_1.TranspileError(`String literal ${node.getText()} is not allowed here, please use \`toByteString\` instead`, this.getRange(node));
3375
+ }
3376
+ return toSection;
3377
+ }
3378
+ transformBigIntLiteralExpression(node, toSection) {
3379
+ let text = node.getText();
3380
+ text = text.replaceAll(/[_n]/g, '');
3381
+ return toSection.append(`${text}`, this.getCoordinates(node.getStart()));
3382
+ }
3383
+ transformArrayLiteralExpression(node, toSection) {
3384
+ const e = node;
3385
+ return toSection
3386
+ .append('[')
3387
+ .appendWith(this, (toSec) => {
3388
+ e.elements.forEach((arg, index) => {
3389
+ toSec
3390
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
3391
+ .append(index < e.elements.length - 1 ? ', ' : '');
3392
+ });
3393
+ return toSec;
3394
+ })
3395
+ .append(']');
3396
+ }
3397
+ transformObjectLiteralExpression(node, toSection) {
3398
+ const e = node;
3399
+ toSection.append('{', this.getCoordinates(e.getStart()));
3400
+ const type = this._checker.getContextualType(e);
3401
+ if (type) {
3402
+ const members = [];
3403
+ type.getProperties().forEach((property) => members.push(property.getName()));
3404
+ members.forEach((member, index) => {
3405
+ const property = e.properties.find((property) => property.name.getText() === member);
3406
+ if (property) {
3407
+ const _property = property;
3408
+ if (_property.initializer) {
3409
+ toSection
3410
+ .appendWith(this, (toSec) => this.transformExpression(_property.initializer, toSec))
3411
+ .append(index < members.length - 1 ? ', ' : '');
3412
+ }
3413
+ else {
3414
+ toSection
3415
+ .append(_property.name.getText())
3416
+ .append(index < members.length - 1 ? ', ' : '');
3417
+ }
3418
+ }
3419
+ else {
3420
+ const structname = type.symbol === undefined
3421
+ ? this._checker.typeToString(type, undefined, typescript_1.default.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)
3422
+ : (type.aliasSymbol === undefined
3423
+ ? type.symbol.escapedName
3424
+ : type.aliasSymbol.escapedName).toString();
3425
+ throw new types_1.TranspileError(`field not found for struct '${structname}': '${property.getText()}'`, this.getRange(e));
3426
+ }
3427
+ });
3428
+ }
3429
+ else {
3430
+ throw new types_1.TranspileError(`Untransformable expression: '${e.getText()}'`, this.getRange(e));
3431
+ }
3432
+ return toSection.append('}');
3433
+ }
3434
+ transformIdentifierExpression(node, toSection) {
3435
+ const nodeType = this._checker.getTypeAtLocation(node);
3436
+ if (nodeType.symbol &&
3437
+ [typescript_1.default.SymbolFlags.Class, typescript_1.default.SymbolFlags.ConstEnum, typescript_1.default.SymbolFlags.RegularEnum].includes(nodeType.symbol.getFlags())) {
3438
+ this.saveSymbol(nodeType.symbol.getName(), nodeType.symbol);
3439
+ // if (node.getText() === 'Utils') {
3440
+ // toSection.append('__scrypt_Utils', this.getCoordinates(node.getStart()));
3441
+ // return toSection;
3442
+ // } else if (node.getText() === 'SigHashUtils') {
3443
+ // toSection.append('ContextUtils', this.getCoordinates(node.getStart()));
3444
+ // return toSection;
3445
+ // }
3446
+ }
3447
+ // if (node.getText() === 'ZEROSAT') {
3448
+ // toSection.append('__scrypt_Utils.ZEROSAT', this.getCoordinates(node.getStart()));
3449
+ // return toSection;
3450
+ // }
3451
+ if (this.isCtcIdentifierOrProperty(node)) {
3452
+ return this.transformCtcNode(node, toSection);
3453
+ }
3454
+ if (this.isTranspilingConstructor(node) && this.isTranspilingBaseContract(node)) {
3455
+ const mapedNode = this._constructorParametersMap.get(node.getText());
3456
+ if (mapedNode) {
3457
+ if (typescript_1.default.isParameter(mapedNode)) {
3458
+ toSection.append(mapedNode.name.getText(), this.getCoordinates(node.getStart()));
3459
+ return toSection;
3460
+ }
3461
+ else if (typescript_1.default.isExpression(mapedNode)) {
3462
+ toSection.appendWith(this, (toSec) => this.transformExpression(mapedNode, toSec));
3463
+ return toSection;
3464
+ }
3465
+ }
3466
+ }
3467
+ toSection.append(node.getText(), this.getCoordinates(node.getStart()));
3468
+ return toSection;
3469
+ }
3470
+ isBooleanType(node) {
3471
+ return (['true', 'false', 'never', 'boolean', 'Bool'].indexOf(this.getResolvedTypeName(node)) !== -1);
3472
+ }
3473
+ transformBinaryExpression(node, toSection) {
3474
+ const e = node;
3475
+ const operator = this.toScryptBinary(e, e.operatorToken.getText());
3476
+ if (e.operatorToken.getText() === '&&' || e.operatorToken.getText() === '||') {
3477
+ // require both operands are boolean type
3478
+ if (!this.isBooleanType(e.left) || !this.isBooleanType(e.right)) {
3479
+ throw new types_1.TranspileError(`\`${node.getText()}\` is not allowed, both operands of \`${e.operatorToken.getText()}\` must be boolean type`, this.getRange(node));
3480
+ }
3481
+ }
3482
+ const srcLoc = this.getCoordinates(e.getStart());
3483
+ return this.transformBinaryOperation(operator, e.left, e.right, srcLoc, toSection);
3484
+ }
3485
+ transformSpecialPropertyAccessExpression(node, toSection) {
3486
+ const text = node.getText().replaceAll('?', '');
3487
+ let isSpecial = true;
3488
+ let shouldAccessThis = false;
3489
+ switch (text) {
3490
+ case 'SigHash.ALL':
3491
+ toSection.append('SigHash.ALL | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
3492
+ break;
3493
+ case 'SigHash.NONE':
3494
+ toSection.append('SigHash.NONE | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
3495
+ break;
3496
+ case 'SigHash.SINGLE':
3497
+ toSection.append('SigHash.SINGLE | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
3498
+ break;
3499
+ case 'SigHash.ANYONECANPAY_ALL':
3500
+ toSection.append('SigHash.ALL | SigHash.ANYONECANPAY | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
3501
+ break;
3502
+ case 'SigHash.ANYONECANPAY_NONE':
3503
+ toSection.append('SigHash.NONE | SigHash.ANYONECANPAY | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
3504
+ break;
3505
+ case 'SigHash.ANYONECANPAY_SINGLE':
3506
+ toSection.append('SigHash.SINGLE | SigHash.ANYONECANPAY | SigHash.FORKID', this.getCoordinates(node.name.getStart()));
3507
+ break;
3508
+ case 'this.changeInfo':
3509
+ shouldAccessThis = this.shouldAutoAppendChangeAmount(this.getMethodContainsTheNode(node)).shouldAccessThis;
3510
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_ChangeInfo}`);
3511
+ break;
3512
+ // ctx: SH Preimage
3513
+ case 'this.ctx.nVersion':
3514
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3515
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.nVersion`);
3516
+ break;
3517
+ case 'this.ctx.hashPrevouts':
3518
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3519
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashPrevouts`);
3520
+ break;
3521
+ case 'this.ctx.spentScriptHash':
3522
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3523
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.spentScriptHash`);
3524
+ break;
3525
+ case 'this.ctx.spentDataHash':
3526
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3527
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.spentDataHash`);
3528
+ break;
3529
+ case 'this.ctx.value':
3530
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3531
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.value`);
3532
+ break;
3533
+ case 'this.ctx.nSequence':
3534
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3535
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.nSequence`);
3536
+ break;
3537
+ case 'this.ctx.hashSpentAmounts':
3538
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3539
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSpentAmounts`);
3540
+ break;
3541
+ case 'this.ctx.hashSpentScriptHashes':
3542
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3543
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSpentScriptHashes`);
3544
+ break;
3545
+ case 'this.ctx.hashSpentDataHashes':
3546
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3547
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSpentDataHashes`);
3548
+ break;
3549
+ case 'this.ctx.hashSequences':
3550
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3551
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashSequences`);
3552
+ break;
3553
+ case 'this.ctx.hashOutputs':
3554
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3555
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.hashOutputs`);
3556
+ break;
3557
+ case 'this.ctx.inputIndex':
3558
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3559
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.inputIndex`);
3560
+ break;
3561
+ case 'this.ctx.nLockTime':
3562
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3563
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.nLockTime`);
3564
+ break;
3565
+ case 'this.ctx.sigHashType':
3566
+ shouldAccessThis = this.shouldAutoAppendSighashPreimage(this.getMethodContainsTheNode(node)).shouldAccessThis;
3567
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.sigHashType`);
3568
+ break;
3569
+ // ParamCtx
3570
+ case 'this.ctx.prevouts':
3571
+ // toSection.append(`${InjectedParam_Prevouts}`);
3572
+ shouldAccessThis = this.shouldAutoAppendPrevouts(this.getMethodContainsTheNode(node)).shouldAccessThis;
3573
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_Prevouts}`);
3574
+ break;
3575
+ case 'this.ctx.spentScriptHashes':
3576
+ // toSection.append(`${InjectedParam_SpentScriptHashes}`);
3577
+ shouldAccessThis = this.shouldAutoAppendSpentScripts(this.getMethodContainsTheNode(node)).shouldAccessThis;
3578
+ toSection.append(shouldAccessThis
3579
+ ? `this.${snippets_1.InjectedParam_SpentScriptHashes}`
3580
+ : `${snippets_1.InjectedParam_SpentScriptHashes}`);
3581
+ break;
3582
+ case 'this.ctx.spentDataHashes':
3583
+ // toSection.append(`${InjectedParam_SpentScriptHashes}`);
3584
+ shouldAccessThis = this.shouldAutoAppendSpentDataHashes(this.getMethodContainsTheNode(node)).shouldAccessThis;
3585
+ toSection.append(shouldAccessThis
3586
+ ? `this.${snippets_1.InjectedParam_SpentDataHashes}`
3587
+ : `${snippets_1.InjectedParam_SpentDataHashes}`);
3588
+ break;
3589
+ case 'this.ctx.spentAmounts':
3590
+ // toSection.append(`${InjectedParam_SpentAmounts}`);
3591
+ shouldAccessThis = this.shouldAutoAppendSpentAmounts(this.getMethodContainsTheNode(node)).shouldAccessThis;
3592
+ toSection.append(shouldAccessThis
3593
+ ? `this.${snippets_1.InjectedParam_SpentAmounts}`
3594
+ : `${snippets_1.InjectedParam_SpentAmounts}`);
3595
+ break;
3596
+ case 'this.ctx.prevout':
3597
+ // toSection.append(`${InjectedParam_Prevout}`);
3598
+ shouldAccessThis = this.shouldAutoAppendPrevouts(this.getMethodContainsTheNode(node)).shouldAccessThis;
3599
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_Prevout}`);
3600
+ break;
3601
+ case 'this.ctx.inputCount':
3602
+ shouldAccessThis = this.shouldAutoAppendSpentAmounts(this.getMethodContainsTheNode(node)).shouldAccessThis;
3603
+ toSection.append(shouldAccessThis
3604
+ ? snippets_1.ACCESS_INPUT_COUNT.accessThis
3605
+ : snippets_1.ACCESS_INPUT_COUNT.accessArgument);
3606
+ break;
3607
+ case 'this.ctx':
3608
+ throw new types_1.TranspileError(`Direct access to \`this.ctx\` is prohibited, only access to \`this.ctx.*\` is allowed, such as: \`this.ctx.shaSpentAmounts\` or \`this.ctx.inputIndex\``, this.getRange(node));
3609
+ case 'this.state':
3610
+ shouldAccessThis = this.shouldAutoAppendStateArgs(this.getMethodContainsTheNode(node)).shouldAccessThis;
3611
+ toSection.append(`${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedProp_NextState}`);
3612
+ break;
3613
+ default: {
3614
+ isSpecial = false;
3615
+ }
3616
+ }
3617
+ return isSpecial;
3618
+ }
3619
+ transformCTCPropertyAccessExpression(node, toSection) {
3620
+ if (typescript_1.default.isIdentifier(node.name) && typescript_1.default.isIdentifier(node.expression)) {
3621
+ if (this.isCtcIdentifierOrProperty(node)) {
3622
+ this.transformCtcNode(node, toSection);
3623
+ return true;
3624
+ }
3625
+ }
3626
+ return false;
3627
+ }
3628
+ transformPropertyAccessExpression(node, toSection) {
3629
+ if (this.transformSpecialPropertyAccessExpression(node, toSection)) {
3630
+ return toSection;
3631
+ }
3632
+ if (this.transformCTCPropertyAccessExpression(node, toSection)) {
3633
+ return toSection;
3634
+ }
3635
+ const name = node.name.getText();
3636
+ const cls = Transpiler.getClassDeclaration(node);
3637
+ if (this.isNonPropReferences(cls, name)) {
3638
+ throw new types_1.TranspileError(`Cannot access Non-Prop property '${name}' in a \`@method()\` function`, this.getRange(node));
3639
+ }
3640
+ if (typescript_1.default.isIdentifier(node.expression)) {
3641
+ const leftSide = node.expression.getText();
3642
+ if (leftSide === this.currentbaseContractName) {
3643
+ toSection.append(`${this.currentContractName}.${node.name.getText()}`, this.getCoordinates(node.name.getStart()));
3644
+ return toSection;
3645
+ }
3646
+ }
3647
+ toSection
3648
+ .appendWith(this, (toSec) => this.transformExpression(node.expression, toSec))
3649
+ .append(`.${node.name.getText()}`, this.getCoordinates(node.name.getStart()));
3650
+ return toSection;
3651
+ }
3652
+ transformArrowFunctionExpression(node, toSection) {
3653
+ const e = node;
3654
+ if (typescript_1.default.isBlock(e.body)) {
3655
+ if (e.parameters.length > 1) {
3656
+ throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[e.kind]}: '${e.getText()}'`, this.getRange(e));
3657
+ }
3658
+ if (e.parameters.length === 1) {
3659
+ toSection.append(` : ${e.parameters[0].name.getText()}`);
3660
+ }
3661
+ toSection.append(' ');
3662
+ return this.transformStatement(e.body, toSection);
3663
+ }
3664
+ throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[e.kind]}: '${e.getText()}'`, this.getRange(e));
3665
+ }
3666
+ transformNewExpression(node, toSection) {
3667
+ const e = node;
3668
+ const clsText = e.expression.getText();
3669
+ const scryptType = (0, utils_1.getBuiltInType)(clsText);
3670
+ if (scryptType) {
3671
+ toSection.append(scryptType, this.getCoordinates(e.getStart())).append('(');
3672
+ if (clsText === 'SigHashType' && e.arguments[0].kind) {
3673
+ const t = parseInt(e.arguments[0].getText());
3674
+ toSection.append(`b'${(0, utils_1.number2hex)(t)}'`);
3675
+ }
3676
+ else {
3677
+ e.arguments.forEach((arg, index) => {
3678
+ toSection
3679
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
3680
+ .append(index < e.arguments.length - 1 ? ', ' : '');
3681
+ });
3682
+ }
3683
+ toSection.append(')');
3684
+ }
3685
+ else {
3686
+ const type = this.getResolvedType(e.expression);
3687
+ const classDec = type.getSymbol().declarations[0];
3688
+ // if this extends `SmartContract` or `SmartLibrary`
3689
+ if (this.isLibrary(classDec)) {
3690
+ toSection
3691
+ .append('new ')
3692
+ .append(e.expression.getText(), this.getCoordinates(e.expression.getStart()))
3693
+ .append('(')
3694
+ .appendWith(this, (toSec) => {
3695
+ e.arguments.forEach((arg, index) => {
3696
+ toSec
3697
+ .appendWith(this, (ts) => this.transformExpression(arg, ts))
3698
+ .append(index < e.arguments.length - 1 ? ', ' : '');
3699
+ });
3700
+ return toSec;
3701
+ })
3702
+ .append(')');
3703
+ }
3704
+ else if (this.isContract(classDec)) {
3705
+ throw new types_1.TranspileError(`contract \`${e.expression.getText()}\` can not be initialized inside a @method, only supports instantiation of library`, this.getRange(e));
3706
+ }
3707
+ else {
3708
+ throw new types_1.TranspileError(`Untransformable expression kind ${typescript_1.default.SyntaxKind[e.kind]}: '${e.getText()}'`, this.getRange(e));
3709
+ }
3710
+ }
3711
+ return toSection;
3712
+ }
3713
+ transformElementAccessExpression(node, toSection) {
3714
+ const e = node;
3715
+ if (this.getResolvedTypeName(e.expression) === 'ByteString') {
3716
+ // ByteString[] is not allowed
3717
+ throw new types_1.TranspileError(`\`${node.getText()}\` is not allowed here, slice ByteString is not supported`, this.getRange(e));
3718
+ }
3719
+ return toSection
3720
+ .appendWith(this, (toSec) => this.transformExpression(e.expression, toSec))
3721
+ .append('[')
3722
+ .appendWith(this, (toSec) => this.transformExpression(e.argumentExpression, toSec))
3723
+ .append(']');
3724
+ }
3725
+ transformConditionalExpression(node, toSection) {
3726
+ const e = node;
3727
+ return toSection
3728
+ .appendWith(this, (toSec) => this.transformExpression(e.condition, toSec))
3729
+ .append(' ? ')
3730
+ .appendWith(this, (toSec) => this.transformExpression(e.whenTrue, toSec))
3731
+ .append(' : ')
3732
+ .appendWith(this, (toSec) => this.transformExpression(e.whenFalse, toSec));
3733
+ }
3734
+ transformParenthesizedExpression(node, toSection) {
3735
+ const e = node;
3736
+ return toSection
3737
+ .append('(')
3738
+ .appendWith(this, (toSec) => this.transformExpression(e.expression, toSec))
3739
+ .append(')');
3740
+ }
3741
+ transformPostfixUnaryExpression(node, toSection) {
3742
+ const e = node;
3743
+ return toSection
3744
+ .appendWith(this, (toSec) => this.transformExpression(e.operand, toSec))
3745
+ .append(`${e.operator == typescript_1.default.SyntaxKind.PlusPlusToken ? '++' : '--'}`, this.getCoordinates(e.getStart()));
3746
+ }
3747
+ transformPrefixUnaryExpression(node, toSection) {
3748
+ const e = node;
3749
+ if (e.operator === typescript_1.default.SyntaxKind.ExclamationToken) {
3750
+ // require boolean
3751
+ if (!this.isBooleanType(e.operand)) {
3752
+ throw new types_1.TranspileError(`\`${node.getText()}\` is not allowed, operand of \`!\` must be boolean type`, this.getRange(node));
3753
+ }
3754
+ }
3755
+ return toSection
3756
+ .append(`${this.toScryptUnary(e, e.operator)}`, this.getCoordinates(e.getStart()))
3757
+ .appendWith(this, (toSec) => this.transformExpression(e.operand, toSec));
3758
+ }
3759
+ transformCallExpression(node, toSection) {
3760
+ const e = node;
3761
+ if (e.expression.kind === typescript_1.default.SyntaxKind.PropertyAccessExpression) {
3762
+ // E.g. "str1.slice(2, 6)"
3763
+ return this.transformPropertyAccessCallExpression(e, toSection);
3764
+ }
3765
+ if (e.expression.kind === typescript_1.default.SyntaxKind.Identifier) {
3766
+ // E.g. "assert(...)"
3767
+ return this.transformIdentifierCallExpression(e, toSection);
3768
+ }
3769
+ return this.transformDefaultCallExpression(e, toSection);
3770
+ }
3771
+ transformBinaryOperation(operator, left, right, srcLoc, toSection, parenthesized = false) {
3772
+ if (parenthesized) {
3773
+ toSection.append('(', srcLoc);
3774
+ }
3775
+ toSection
3776
+ .appendWith(this, (toSec) => this.transformExpression(left, toSec))
3777
+ .append(` ${operator} `, srcLoc)
3778
+ .appendWith(this, (toSec) => this.transformExpression(right, toSec));
3779
+ if (parenthesized) {
3780
+ toSection.append(')', srcLoc);
3781
+ }
3782
+ return toSection;
3783
+ }
3784
+ transformPropertyAccessCallExpression(node, toSection) {
3785
+ const expr = node.expression;
3786
+ const name = expr.name.getText();
3787
+ const leftSide = expr.expression.getText();
3788
+ if (leftSide === 'this') {
3789
+ if (name === 'checkSig') {
3790
+ return this.transformCallCheckSig(node, toSection);
3791
+ }
3792
+ if (name === 'checkMultiSig') {
3793
+ return this.transformCallCheckMultiSig(node, toSection);
3794
+ }
3795
+ if (name === 'checkPreimage' || name === 'checkPreimageSigHashType') {
3796
+ return this.transformCallCheckPreimage(node, toSection);
3797
+ }
3798
+ if (name === 'buildChangeOutput') {
3799
+ return this.transformCallBuildChangeOutput(node, toSection);
3800
+ }
3801
+ if (name === 'relTimeLock') {
3802
+ return this.transformCallCSV(node, toSection);
3803
+ }
3804
+ if (name === 'absTimeLock') {
3805
+ return this.transformCallCLTV(node, toSection);
3806
+ }
3807
+ if (name === 'checkInputState') {
3808
+ return this.transformCallCheckInputState(node, toSection);
3809
+ }
3810
+ if (name === 'backtraceToOutpoint') {
3811
+ return this.transformCallBacktraceToOutpoint(node, toSection);
3812
+ }
3813
+ if (name === 'backtraceToScript') {
3814
+ return this.transformCallBacktraceToScript(node, toSection);
3815
+ }
3816
+ if (name === 'checkOutputs') {
3817
+ return this.transformCallCheckOutputs(node, toSection);
3818
+ }
3819
+ return this.transformDefaultCallExpression(node, toSection);
3820
+ }
3821
+ if (name === 'slice') {
3822
+ return this.transformCallSlice(node);
3823
+ }
3824
+ return this.transformDefaultCallExpression(node, toSection);
3825
+ }
3826
+ transformIdentifierCallExpression(node, toSection) {
3827
+ const srcLoc = this.getCoordinates(node.getStart());
3828
+ const name = node.expression.getText();
3829
+ if (name === 'assert') {
3830
+ return toSection
3831
+ .append('require(', srcLoc)
3832
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
3833
+ .append(')');
3834
+ }
3835
+ if (name === 'BigInt' || name === 'Number') {
3836
+ toSection.append('(', srcLoc);
3837
+ node.arguments.forEach((arg) => {
3838
+ toSection.appendWith(this, (toSec) => this.transformExpression(arg, toSec));
3839
+ });
3840
+ return toSection.append(')');
3841
+ }
3842
+ if (name === 'and') {
3843
+ return this.transformBinaryOperation('&', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
3844
+ }
3845
+ if (name === 'or') {
3846
+ return this.transformBinaryOperation('|', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
3847
+ }
3848
+ if (name === 'xor') {
3849
+ return this.transformBinaryOperation('^', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
3850
+ }
3851
+ if (name === 'invert') {
3852
+ return toSection
3853
+ .append('~', srcLoc)
3854
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec));
3855
+ }
3856
+ if (name === 'lshift') {
3857
+ return this.transformBinaryOperation('<<', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
3858
+ }
3859
+ if (name === 'rshift') {
3860
+ return this.transformBinaryOperation('>>', node.arguments[0], node.arguments[1], srcLoc, toSection, true);
3861
+ }
3862
+ if (name === 'equals') {
3863
+ return this.transformBinaryOperation('==', node.arguments[0], node.arguments[1], srcLoc, toSection);
3864
+ }
3865
+ if (name === 'toByteString') {
3866
+ const arg0 = node.arguments[0];
3867
+ if (arg0.kind === typescript_1.default.SyntaxKind.StringLiteral || this.isByteStringNode(arg0)) {
3868
+ return toSection.appendWith(this, (toSec) => this.transformExpression(arg0, toSec));
3869
+ }
3870
+ throw new types_1.TranspileError('Only string literal can be passed to the first parameter of `toByteString`', this.getRange(node));
3871
+ }
3872
+ if (name === 'PubKeyHash' || name === 'Addr') {
3873
+ return toSection
3874
+ .append('Ripemd160', srcLoc)
3875
+ .append('(')
3876
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
3877
+ .append(')');
3878
+ }
3879
+ // if (name === 'SigHashType') {
3880
+ // return this.transformCallSigHashType(node, toSection);
3881
+ // }
3882
+ if (name === 'reverseByteString') {
3883
+ toSection.append('reverseBytes(', srcLoc);
3884
+ node.arguments.forEach((arg, index) => {
3885
+ toSection
3886
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
3887
+ .append(index < node.arguments.length - 1 ? ', ' : '');
3888
+ });
3889
+ return toSection.append(')');
3890
+ }
3891
+ if (name === 'intToByteString') {
3892
+ let func = node.arguments.length === 1 ? 'pack' : 'num2bin';
3893
+ toSection.append(`${func}(`, srcLoc);
3894
+ node.arguments.forEach((arg, index) => {
3895
+ toSection
3896
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
3897
+ .append(index < node.arguments.length - 1 ? ', ' : '');
3898
+ });
3899
+ return toSection.append(')');
3900
+ }
3901
+ if (name === 'byteStringToInt') {
3902
+ toSection.append(`unpack(`, srcLoc);
3903
+ node.arguments.forEach((arg, index) => {
3904
+ toSection
3905
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
3906
+ .append(index < node.arguments.length - 1 ? ', ' : '');
3907
+ });
3908
+ return toSection.append(')');
3909
+ }
3910
+ if (name === 'fill') {
3911
+ return this.transformCallFill(node, toSection);
3912
+ }
3913
+ if (name === 'slice') {
3914
+ return this.transformIdentifierCallSlice(node, toSection);
3915
+ }
3916
+ if (name === 'pubKey2Addr') {
3917
+ return toSection
3918
+ .append('hash160', srcLoc)
3919
+ .append('(')
3920
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
3921
+ .append(')');
3922
+ }
3923
+ return this.transformDefaultCallExpression(node, toSection);
3924
+ }
3925
+ transformDefaultCallExpression(node, toSection) {
3926
+ const funcType = this._checker.getTypeAtLocation(node.expression);
3927
+ if (funcType.flags === typescript_1.default.TypeFlags.Any) {
3928
+ throw new types_1.TranspileError(`Calling function which returns \`any\` is not allowed`, this.getRange(node.expression));
3929
+ }
3930
+ const text = node.getText().replaceAll('?', '');
3931
+ switch (text) {
3932
+ case 'this.ctx.serialize()':
3933
+ toSection.append(`this.${snippets_1.InjectedParam_SHPreimage}`, this.getCoordinates(node.getStart()));
3934
+ break;
3935
+ case 'this.insertCodeSeparator()':
3936
+ {
3937
+ const ifStmt = Transpiler.getIfStatement(node);
3938
+ if (ifStmt) {
3939
+ throw new types_1.TranspileError(`insertCodeSeparator() cannot be called in a if statement`, this.getRange(node));
3940
+ }
3941
+ const md = Transpiler.getMethodDeclaration(node);
3942
+ if (md) {
3943
+ if (!Transpiler.isPublicMethod(md)) {
3944
+ throw new types_1.TranspileError(`non-public methods cannot call insertCodeSeparator()`, this.getRange(node));
3945
+ }
3946
+ const methodInfo = this.findMethodInfo(md.name.getText());
3947
+ if (methodInfo) {
3948
+ methodInfo.codeSeparatorCount++;
3949
+ }
3950
+ }
3951
+ toSection.append(`***`, this.getCoordinates(node.getStart()));
3952
+ toSection.skipNextAppend = true; // "***" is not closed using semicolon
3953
+ }
3954
+ break;
3955
+ default:
3956
+ toSection
3957
+ .appendWith(this, (toSec) => this.transformExpression(node.expression, toSec))
3958
+ .append('(')
3959
+ .appendWith(this, (toSec) => {
3960
+ const methodDec = funcType.symbol.declarations[0];
3961
+ const inValidParams = methodDec.parameters.find((p) => p.type?.getText() === 'SmartContract');
3962
+ if (inValidParams) {
3963
+ throw new types_1.TranspileError(`Untransformable parameter: '${node.getText()}'`, this.getRange(node));
3964
+ }
3965
+ node.arguments.forEach((arg, index) => {
3966
+ toSec
3967
+ .appendWith(this, (ts) => this.transformExpression(arg, ts))
3968
+ .append(index < node.arguments.length - 1 ? ', ' : '');
3969
+ });
3970
+ return toSec;
3971
+ })
3972
+ .append(')');
3973
+ break;
3974
+ }
3975
+ return toSection;
3976
+ }
3977
+ transformCallSlice(node) {
3978
+ // disable ByteString.slice()
3979
+ const msg = `\`${node.getText()}\` is not allowed here, slice ByteString is not supported`;
3980
+ throw new types_1.TranspileError(msg, this.getRange(node));
3981
+ }
3982
+ transformIdentifierCallSlice(node, toSection) {
3983
+ toSection
3984
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
3985
+ .append('[')
3986
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
3987
+ .append(' : ');
3988
+ if (node.arguments.length === 3) {
3989
+ toSection.appendWith(this, (toSec) => this.transformExpression(node.arguments[2], toSec));
3990
+ }
3991
+ else {
3992
+ toSection
3993
+ .append('len(')
3994
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
3995
+ .append(')');
3996
+ }
3997
+ return toSection.append(']');
3998
+ }
3999
+ transformCallCSV(node, toSection) {
4000
+ const arg = node.arguments[0];
4001
+ this._accessBuiltinsSymbols.add('__scrypt_ASM');
4002
+ return toSection
4003
+ .append(`__scrypt_ASM.csv(`, this.getCoordinates(node.getStart()))
4004
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
4005
+ .append(`)`);
4006
+ }
4007
+ transformCallCLTV(node, toSection) {
4008
+ const arg = node.arguments[0];
4009
+ this._accessBuiltinsSymbols.add('__scrypt_ASM');
4010
+ return toSection
4011
+ .append(`__scrypt_ASM.cltv(`, this.getCoordinates(node.getStart()))
4012
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
4013
+ .append(`)`);
4014
+ }
4015
+ transformCallCheckSig(node, toSection) {
4016
+ const srcLoc = this.getCoordinates(node.getStart());
4017
+ toSection.appendWith(this, (toSec) => {
4018
+ const _e = node.expression;
4019
+ return this.transformExpression(_e.name, toSec);
4020
+ });
4021
+ toSection.append('(', srcLoc);
4022
+ const args = node.arguments.slice(0, 2);
4023
+ args.forEach((arg, index) => {
4024
+ toSection
4025
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
4026
+ .append(index < args.length - 1 ? ', ' : '');
4027
+ });
4028
+ return toSection.append(')', srcLoc);
4029
+ }
4030
+ transformCallCheckMultiSig(node, toSection) {
4031
+ const srcLoc = this.getCoordinates(node.getStart());
4032
+ toSection.appendWith(this, (toSec) => {
4033
+ const _e = node.expression;
4034
+ return this.transformExpression(_e.name, toSec);
4035
+ });
4036
+ toSection.append('(', srcLoc);
4037
+ const args = node.arguments.slice(0, 2);
4038
+ args.forEach((arg, index) => {
4039
+ toSection
4040
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
4041
+ .append(index < args.length - 1 ? ', ' : '');
4042
+ });
4043
+ return toSection.append(')', srcLoc);
4044
+ }
4045
+ transformCallCheckPreimage(node, toSection) {
4046
+ const srcLoc = this.getCoordinates(node.getStart());
4047
+ toSection.appendWith(this, (toSec) => {
4048
+ const _e = node.expression;
4049
+ toSec.append('Tx.');
4050
+ return this.transformExpression(_e.name, toSec);
4051
+ });
4052
+ toSection.append('(', srcLoc);
4053
+ node.arguments.forEach((arg, index) => {
4054
+ toSection
4055
+ .appendWith(this, (toSec) => this.transformExpression(arg, toSec))
4056
+ .append(index < node.arguments.length - 1 ? ', ' : '');
4057
+ });
4058
+ return toSection.append(')', srcLoc);
4059
+ }
4060
+ transformCallBuildPublicKeyHashOutput(node, toSection, name) {
4061
+ if (node.arguments.length !== 2) {
4062
+ throw new types_1.TranspileError(`Invalid arguments length for ${name}`, this.getRange(node));
4063
+ }
4064
+ return toSection
4065
+ .append(`Utils.buildOutput(Utils.buildPublicKeyHashScript(`, this.getCoordinates(node.getStart()))
4066
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4067
+ .append('), ')
4068
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
4069
+ .append(')');
4070
+ }
4071
+ transformCallBuildAddressScript(node, toSection) {
4072
+ if (node.arguments.length !== 1) {
4073
+ throw new types_1.TranspileError(`Invalid arguments length for buildAddressScript`, this.getRange(node));
4074
+ }
4075
+ return toSection
4076
+ .append(`Utils.buildPublicKeyHashScript(`, this.getCoordinates(node.getStart()))
4077
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4078
+ .append(')');
4079
+ }
4080
+ transformCallFill(node, toSection) {
4081
+ const _length = node.arguments[1];
4082
+ const isCtc = this.isCtcExpression(_length);
4083
+ if (_length.kind !== typescript_1.default.SyntaxKind.NumericLiteral && !isCtc) {
4084
+ throw new types_1.TranspileError('Only compiled-time constant can be passed to the second parameter of `fill`', this.getRange(node));
4085
+ }
4086
+ return toSection
4087
+ .append('repeat(', this.getCoordinates(node.getStart()))
4088
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4089
+ .append(', ')
4090
+ .appendWith(this, (toSec) => isCtc && !this.isParameterNode(node.arguments[1])
4091
+ ? this.transformCtcExpr(_length, toSec)
4092
+ : this.transformExpression(_length, toSec))
4093
+ .append(')');
4094
+ }
4095
+ transformCallSigHashType(node, toSection) {
4096
+ return toSection
4097
+ .append('SigHashType', this.getCoordinates(node.getStart()))
4098
+ .append('(')
4099
+ .appendWith(this, (toSec) => {
4100
+ if (node.arguments[0].kind !== typescript_1.default.SyntaxKind.NumericLiteral) {
4101
+ throw new types_1.TranspileError(`Only support numeric literal: [65,66,67,193,194,195]`, this.getRange(node));
4102
+ }
4103
+ const n = node.arguments[0];
4104
+ const val = parseInt(n.getText());
4105
+ if ([65, 66, 67, 193, 194, 195].includes(val)) {
4106
+ return toSec.append(`b'${val.toString(16)}'`);
4107
+ }
4108
+ throw new types_1.TranspileError(`Only support numeric literal: [65,66,67,193,194,195]`, this.getRange(node));
4109
+ })
4110
+ .append(')');
4111
+ }
4112
+ transformCallBuildChangeOutput(_node, toSection) {
4113
+ const methodNode = this.getMethodContainsTheNode(_node);
4114
+ const { shouldAccessThis } = this.shouldAutoAppendChangeAmount(methodNode);
4115
+ this._accessBuiltinsSymbols.add('TxUtils');
4116
+ return toSection
4117
+ .append('\n')
4118
+ .append(shouldAccessThis
4119
+ ? snippets_1.CALL_BUILD_CHANGE_OUTPUT.accessThis
4120
+ : snippets_1.CALL_BUILD_CHANGE_OUTPUT.accessArgument);
4121
+ }
4122
+ transformCallCheckInputState(node, toSection) {
4123
+ const dynamicInputIndex = this.isDynamicIndex(node.arguments[0]);
4124
+ const randomId = Math.random().toString(36).substring(2, 15);
4125
+ const inputIndexVar = `__scrypt_ts_input_index_${randomId}`;
4126
+ if (dynamicInputIndex) {
4127
+ // declare a new input index variable
4128
+ toSection
4129
+ .append(`int ${inputIndexVar} = `, this.getCoordinates(node.arguments[0].getStart()))
4130
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4131
+ .append(';\n');
4132
+ }
4133
+ // call `StateUtils.checkInputState`
4134
+ this._accessBuiltinsSymbols.add('StateUtils');
4135
+ return toSection
4136
+ .append('\n')
4137
+ .append(`StateUtils.checkInputState(`)
4138
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4139
+ .append(', ')
4140
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
4141
+ .append(', ')
4142
+ .appendWith(this, (toSec) => {
4143
+ const shouldAccessThis = this.shouldAutoAppendStateArgs(this.getMethodContainsTheNode(node)).shouldAccessThis;
4144
+ toSec.append(shouldAccessThis
4145
+ ? `this.${snippets_1.InjectedParam_SpentDataHashes}`
4146
+ : `${snippets_1.InjectedParam_SpentDataHashes}`);
4147
+ return toSec;
4148
+ })
4149
+ .append(');')
4150
+ .append('\n');
4151
+ }
4152
+ transformCallBacktraceToOutpoint(node, toSection) {
4153
+ const methodNode = this.getMethodContainsTheNode(node);
4154
+ const { shouldAccessThis } = this.shouldAutoAppendSighashPreimage(methodNode);
4155
+ this._accessBuiltinsSymbols.add('Backtrace');
4156
+ const spentScriptHashes = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SpentScriptHashes}`;
4157
+ const inputIndex = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.inputIndex`;
4158
+ return toSection
4159
+ .append('Backtrace.verifyFromOutpoint(')
4160
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4161
+ .append(', ')
4162
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
4163
+ .append(', ')
4164
+ .append(`${spentScriptHashes}[${inputIndex} * 32 : (${inputIndex} + 1) * 32]`)
4165
+ .append(', ')
4166
+ .appendWith(this, (toSec) => {
4167
+ return this.transformAccessPrevTxHashPreimage(node, toSec).append(`.inputList`);
4168
+ })
4169
+ .append(')');
4170
+ }
4171
+ transformCallBacktraceToScript(node, toSection) {
4172
+ const methodNode = this.getMethodContainsTheNode(node);
4173
+ const { shouldAccessThis } = this.shouldAutoAppendSighashPreimage(methodNode);
4174
+ this._accessBuiltinsSymbols.add('Backtrace');
4175
+ const spentScriptHashes = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SpentScriptHashes}`;
4176
+ const inputIndex = `${shouldAccessThis ? 'this.' : ''}${snippets_1.InjectedParam_SHPreimage}.inputIndex`;
4177
+ return toSection
4178
+ .append('Backtrace.verifyFromScript(')
4179
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4180
+ .append(', ')
4181
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[1], toSec))
4182
+ .append(', ')
4183
+ .append(`${spentScriptHashes}[${inputIndex} * 32 : (${inputIndex} + 1) * 32]`)
4184
+ .append(', ')
4185
+ .appendWith(this, (toSec) => {
4186
+ return this.transformAccessPrevTxHashPreimage(node, toSec).append(`.inputList`);
4187
+ })
4188
+ .append(')');
4189
+ }
4190
+ // private transformAccessCurInputStateProof(
4191
+ // node: ts.Node,
4192
+ // toSection: EmittedSection,
4193
+ // ): EmittedSection {
4194
+ // const methodName = `${this._currentContract.name.escapedText.toString()}.${this._currentMethodName}`;
4195
+ // const methodInfo = this.methodInfos.get(methodName);
4196
+ // if (!methodInfo) {
4197
+ // throw new Error(`Method info not found for ${methodName}`);
4198
+ // }
4199
+ // const methodNode = this.getMethodContainsTheNode(node);
4200
+ // const { shouldAccessThis } = this.shouldAutoAppendStateArgs(methodNode);
4201
+ // const expr = methodInfo.accessInfo.accessInputStateProofs
4202
+ // ? `${shouldAccessThis ? INPUT_STATE_PROOF_EXPR.accessThis(InjectedParam_InputIndexVal) : INPUT_STATE_PROOF_EXPR.accessArgument(InjectedParam_InputIndexVal)}`
4203
+ // : `${shouldAccessThis ? 'this.' : ''}${InjectedParam_InputStateProof}`;
4204
+ // return toSection.append(expr);
4205
+ // }
4206
+ transformAccessPrevTxHashPreimage(node, toSection) {
4207
+ const methodNode = this.getMethodContainsTheNode(node);
4208
+ const methodInfo = this.findMethodInfo(methodNode.name.getText());
4209
+ if (!methodInfo) {
4210
+ throw new Error(`Method info not found for ${methodNode.name.getText()}`);
4211
+ }
4212
+ const { shouldAccessThis } = this.shouldAutoAppendPrevTxHashPreimageArgs(methodNode);
4213
+ const expr = shouldAccessThis ? `this.${snippets_1.InjectedProp_PrevTxHashPreimage}` : snippets_1.InjectedProp_PrevTxHashPreimage;
4214
+ return toSection.append(expr);
4215
+ }
4216
+ isDynamicIndex(node) {
4217
+ const indexExpr = this.unwrapNumberConversion(node);
4218
+ return (!this.isCtcExpression(indexExpr) &&
4219
+ !typescript_1.default.isForStatement(this._checker.getSymbolAtLocation(indexExpr)?.declarations[0]?.parent?.parent));
4220
+ }
4221
+ unwrapNumberConversion(node) {
4222
+ // BigInt(x) => x, Number(x) => x
4223
+ if (typescript_1.default.isCallExpression(node) && ['BigInt', 'Number'].includes(node.expression.getText())) {
4224
+ return node.arguments[0];
4225
+ }
4226
+ return node;
4227
+ }
4228
+ transformCallCheckOutputs(node, toSection) {
4229
+ return toSection
4230
+ .append(`hash256(`)
4231
+ .appendWith(this, (toSec) => this.transformExpression(node.arguments[0], toSec))
4232
+ .append(`) == ${snippets_1.InjectedParam_SHPreimage}.hashOutputs`);
4233
+ }
4234
+ }
4235
+ exports.Transpiler = Transpiler;
4236
+ Transpiler.topCtcs = new Map();
4237
+ Transpiler.contractAst = new Map();
4238
+ Transpiler.scryptFileExt = 'scrypt.tpl';
4239
+ //# sourceMappingURL=transpiler.js.map