-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
executable file
·164 lines (125 loc) · 4.63 KB
/
index.js
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env node
const fs = require('fs')
const util = require('util')
const lstat = util.promisify(fs.lstat)
const readDir = util.promisify(fs.readdir)
const Table = require('cli-table')
const yargs = require('yargs/yargs')
// 1 KiB = 1.024 bytes
const DEFAULT_MAX_CONTRACT_SIZE_IN_KIB = 24
/**
* Outputs the size of a given smart contract
* @param config - A truffle-config object.
* Has attributes like `truffle_directory`, `working_directory`, etc.
* @param done - A done callback, or a normal callback.
*/
module.exports = async (config, done) => {
configureArgumentParsing()
yargs.parse(process.argv.slice(3))
const contractNames = yargs.argv.contracts
const { checkMaxSize, ignoreMocks, sizeInBytes } = yargs.argv
if (!isValidCheckMaxSize(checkMaxSize)) {
done(`--checkMaxSize: invalid value ${checkMaxSize}`)
}
const table = new Table({
head: ['Contract'.white.bold, 'Size'.white.bold],
colWidths: [70, sizeInBytes ? 13 : 10]
})
// array of objects of {file: path to file, name: name of the contract}
const contracts = await getContracts(config, contractNames, ignoreMocks, done)
const contractPromises = contracts.map(async (contract) => {
await checkFile(contract.file, done)
const contractFile = require(contract.file)
if (!('deployedBytecode' in contractFile)) {
done(`Error: deployedBytecode not found in ${contract.file} (it is not a contract json file)`)
}
const byteCodeSize = sizeInBytes
? computeByteCodeSizeInBytes(contractFile.deployedBytecode)
: computeByteCodeSizeInKiB(contractFile.deployedBytecode)
table.push([
contract.name,
formatByteCodeSize(byteCodeSize, sizeInBytes)
])
})
await Promise.all(contractPromises)
console.log(table.toString())
if (checkMaxSize) {
const maxSize = checkMaxSize === true ? DEFAULT_MAX_CONTRACT_SIZE_IN_KIB : checkMaxSize
table.forEach(row => {
if (Number.parseFloat(row[1]) > maxSize) {
done(`Contract ${row[0]} is bigger than ${maxSize} KiB`)
}
})
}
done()
}
function configureArgumentParsing () {
yargs.option('contracts', { describe: 'Only display certain contracts', type: 'array' })
yargs.option('checkMaxSize', { describe: 'Returns an error exit code if a contract is bigger than the optional size in KiB (default: 24). Must be an integer value' })
yargs.option('ignoreMocks', { describe: 'Ignores all contracts which names end with "Mock"', type: 'boolean' })
yargs.option('sizeInBytes', { describe: 'Show contract size in Bytes', type: 'boolean' })
// disable version parameter
yargs.version(false)
// puts help parameter at the end of the parameter list
yargs.help()
}
function isValidCheckMaxSize (checkMaxSize) {
if (checkMaxSize === undefined) {
return true
}
return checkMaxSize === true || !Number.isNaN(checkMaxSize)
}
function computeByteCodeSizeInBytes (byteCode) {
// -2 to remove 0x from the beginning of the string
// /2 because one byte consists of two hexadecimal values
return (byteCode.length - 2) / 2
}
function computeByteCodeSizeInKiB (byteCode) {
// /1024 to convert to size from byte to kibibytes
return computeByteCodeSizeInBytes(byteCode) / 1024
}
function formatByteCodeSize (byteCodeSize, sizeInBytes) {
return `${sizeInBytes ? byteCodeSize : byteCodeSize.toFixed(2)} ${sizeInBytes ? "bytes" : "KiB"}`
}
async function checkFile (filePath, done) {
let stat
try {
stat = await lstat(filePath)
} catch (error) {
done(`Error while checking file ${filePath}: ${error.message}`)
}
if (!stat.isFile()) {
done(`Error: ${filePath} is not a valid file`)
}
}
async function getContracts (config, contractNames, ignoreMocks, done) {
const contractsBuildDirectory = config.contracts_build_directory
if (contractNames === undefined || contractNames.length === 0) {
contractNames = await getAllContractNames(contractsBuildDirectory, ignoreMocks, done)
}
return contractNames.map(contractName => {
return {
file: `${contractsBuildDirectory}/${contractName}.json`,
name: contractName
}
})
}
async function getAllContractNames (contractsBuildDirectory, ignoreMocks, done) {
let files
try {
files = await readDir(contractsBuildDirectory)
} catch (error) {
done(`Error while getting contracts from build directory: ${error.message}`)
}
const contractsFiles = files.filter(file => {
if (!file.endsWith('.json')) {
return false
}
if (ignoreMocks && file.endsWith('Mock.json')) {
return false
}
return true
})
// -5 because that's the length of the string '.json'
return contractsFiles.map(contractFile => contractFile.slice(0, -5))
}