Repository navigation
Expand file tree
/
Copy pathcashc-cli.ts
More file actions
120 lines (103 loc) · 4.08 KB
/
Copy pathcashc-cli.ts
File metadata and controls
120 lines (103 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#! /usr/bin/env node
import { binToHex } from '@bitauth/libauth';
import {
asmToScript,
calculateBytesize,
CompilerOptions,
countOpcodes,
formatArtifact,
scriptToAsm,
scriptToBytecode,
} from '@cashscript/utils';
import { program, Option } from 'commander';
import fs from 'fs';
import path from 'path';
import { compileFile, version } from './index.js';
import { MAX_INPUT_BYTESIZE } from './constants.js';
program
.storeOptionsAsProperties(false)
.name('cashc')
.version(version, '-V, --version', 'Output the version number.')
.argument('<source_file>', 'The source file to compile.')
.option('-o, --output <path>', 'Specify a file to output the generated artifact.')
.option('-h, --hex', 'Compile the contract to hex format rather than a full artifact.')
.option('-A, --asm', 'Compile the contract to ASM format rather than a full artifact.')
.option('-c, --opcount', 'Display the number of opcodes in the compiled bytecode.')
.option('-s, --size', 'Display the size in bytes of the compiled bytecode.')
.option('-S, --skip-enforce-function-parameter-types', 'Do not enforce function parameter types.')
.option('-L, --skip-enforce-locktime-guard', 'Do not inject a tx.time guard when tx.locktime is used.')
.addOption(
new Option('-O, --optimize-for <target>', 'Optimisation objective: minimise bytecode size or executed op-cost (default).')
.choices(['size', 'opcost']),
)
.addOption(
new Option('-f, --format <format>', 'Specify the format of the output.')
.choices(['json', 'ts'])
.default('json'),
)
.helpOption('-?, --help', 'Display help')
.parse();
const opts = program.opts();
run();
function run(): void {
ensure(program.args.length === 1, 'Please provide exactly one source file');
ensure(!(opts.asm && opts.hex), 'Flags --asm and --hex cannot be used together');
ensure(!(opts.asm || opts.hex) || !opts.output, 'Flags --asm or --hex cannot be used with --output');
ensure(!opts.args || opts.asm || opts.hex, '--args can only be used with --asm or --hex');
const sourceFile = path.resolve(program.args[0]);
ensure(fs.existsSync(sourceFile) && fs.statSync(sourceFile).isFile(), 'Please provide a valid source file');
const outputFile = opts.output && opts.output !== '-' && path.resolve(opts.output);
const compilerOptions: CompilerOptions = {
enforceFunctionParameterTypes: !opts.skipEnforceFunctionParameterTypes,
enforceLocktimeGuard: !opts.skipEnforceLocktimeGuard,
...(opts.optimizeFor ? { optimizeFor: opts.optimizeFor } : {}),
};
try {
const artifact = compileFile(sourceFile, compilerOptions);
const script = asmToScript(artifact.bytecode);
const opcount = countOpcodes(script);
const bytesize = calculateBytesize(script);
if (bytesize > MAX_INPUT_BYTESIZE) {
console.warn(`Warning: Your contract is ${bytesize} bytes, which is over the limit of ${MAX_INPUT_BYTESIZE} bytes and will not be accepted by the BCH network`);
}
if (opts.asm) {
console.log(scriptToAsm(script));
return;
}
if (opts.hex) {
console.log(binToHex(scriptToBytecode(script)));
return;
}
// Opcount and size checks can happen together, but do not output compilation result
if (opts.opcount || opts.size) {
if (opts.opcount) {
console.log('Opcode count:', opcount);
}
if (opts.size) {
console.log('Bytesize:', bytesize);
}
return;
}
const formattedArtifact = formatArtifact(artifact, opts.format);
if (outputFile) {
// Create output file and write the artifact to it
const outputDir = path.dirname(outputFile);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputFile, formattedArtifact);
} else {
// Output artifact to STDOUT
console.log(formattedArtifact);
}
} catch (e: any) {
abort(e.message);
}
}
function ensure(condition: boolean, msg: string, code?: number): void {
condition || abort(msg, code);
}
function abort(msg: string, code: number = 1): void {
console.error(msg);
process.exit(code);
}