@1inch/solidity-utils 2.0.13 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -0
- package/contracts/libraries/UniERC20.sol +6 -4
- package/package.json +5 -2
- package/utils/acquit-markdown.js +53 -0
- package/utils/test-docgen.js +117 -0
package/README.md
CHANGED
|
@@ -64,3 +64,96 @@ Add to `package.json` file solidity compiler version and shortcut to run command
|
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
...
|
|
67
|
+
|
|
68
|
+
#### Test documentation generator (test-docgen)
|
|
69
|
+
Script generates documentation for tests in markdown format.
|
|
70
|
+
Give descriptions for `describe` and `it` sections and build documentation using these descriptions.
|
|
71
|
+
|
|
72
|
+
##### Example
|
|
73
|
+
Test described as shown below
|
|
74
|
+
|
|
75
|
+
```JavaScript
|
|
76
|
+
// Test suite
|
|
77
|
+
describe('My feature', function() {
|
|
78
|
+
// Nested test suite
|
|
79
|
+
describe("My subfeature", function() {
|
|
80
|
+
/*
|
|
81
|
+
**Test case 1**
|
|
82
|
+
Test case should work
|
|
83
|
+
*/
|
|
84
|
+
it("My case", function() {
|
|
85
|
+
// code here
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
will generated the following output
|
|
91
|
+
```Markdown
|
|
92
|
+
|
|
93
|
+
# My feature
|
|
94
|
+
|
|
95
|
+
Test suite
|
|
96
|
+
|
|
97
|
+
## My subfeature
|
|
98
|
+
|
|
99
|
+
Nested test suite
|
|
100
|
+
|
|
101
|
+
### My case
|
|
102
|
+
|
|
103
|
+
**Test case 1**
|
|
104
|
+
Test case should work
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
##### Installation
|
|
108
|
+
- Before use install documentation parser
|
|
109
|
+
```
|
|
110
|
+
yarn add acquit --dev
|
|
111
|
+
```
|
|
112
|
+
- Optionally configure script for default usage. Add to `script` section in `package.json`
|
|
113
|
+
```
|
|
114
|
+
"test:docs": "npx test-docgen"
|
|
115
|
+
```
|
|
116
|
+
- Optionally configure script for generating test list only. Add to `script` section in `package.json`
|
|
117
|
+
```
|
|
118
|
+
"test:docs": "npx test-docgen -l"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
##### Usage
|
|
122
|
+
If script configured
|
|
123
|
+
```
|
|
124
|
+
yarn test:docs
|
|
125
|
+
```
|
|
126
|
+
or
|
|
127
|
+
```
|
|
128
|
+
npx test-docgen
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Available parameters
|
|
132
|
+
```
|
|
133
|
+
Options:
|
|
134
|
+
-i, --input <input> tests directory (default: "test")
|
|
135
|
+
-x, --exclude [exclude] exclude directories and files. omit argument to exclude all subdirectories (default: false)
|
|
136
|
+
-o, --output <output> file to write output (default: "TESTS.md")
|
|
137
|
+
-c, --code include code (default: false)
|
|
138
|
+
-l, --list list tests only, do not include description (default: false)
|
|
139
|
+
-d, --debug debug mode (default: false)
|
|
140
|
+
-h, --help display help for command
|
|
141
|
+
```
|
|
142
|
+
##### Examples
|
|
143
|
+
Generate docs with default input and output
|
|
144
|
+
```
|
|
145
|
+
npx test-docgen
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Generate docs for files in folders `tests/mocks` and `tests/utils`
|
|
149
|
+
```
|
|
150
|
+
npx test-docgen -i "tests/mocks;tests/utils"
|
|
151
|
+
```
|
|
152
|
+
Exclude from docs file `test/mock-exclude.js` and `test/utils folder`
|
|
153
|
+
```
|
|
154
|
+
npx test-docgen -x "tests/mock-exclude.js;tests/utils"
|
|
155
|
+
```
|
|
156
|
+
Generate list of tests only
|
|
157
|
+
```
|
|
158
|
+
npx test-docgen -l
|
|
159
|
+
```
|
|
@@ -6,16 +6,18 @@ pragma abicoder v1;
|
|
|
6
6
|
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
|
7
7
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
8
8
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
9
|
-
import "./
|
|
9
|
+
import "./RevertReasonForwarder.sol";
|
|
10
10
|
import "./StringUtil.sol";
|
|
11
11
|
|
|
12
12
|
library UniERC20 {
|
|
13
13
|
using SafeMath for uint256;
|
|
14
14
|
using SafeERC20 for IERC20;
|
|
15
15
|
|
|
16
|
+
error ApproveCalledOnETH();
|
|
16
17
|
error NotEnoughValue();
|
|
17
18
|
error FromIsNotSender();
|
|
18
19
|
error ToIsNotThis();
|
|
20
|
+
error ERC20OperationFailed();
|
|
19
21
|
|
|
20
22
|
IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
|
21
23
|
IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));
|
|
@@ -67,7 +69,7 @@ library UniERC20 {
|
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
function uniApprove(IERC20 token, address to, uint256 amount) internal {
|
|
70
|
-
|
|
72
|
+
if (isETH(token)) revert ApproveCalledOnETH();
|
|
71
73
|
|
|
72
74
|
// solhint-disable-next-line avoid-low-level-calls
|
|
73
75
|
(bool success, bytes memory returndata) = address(token).call(abi.encodeWithSelector(token.approve.selector, to, amount));
|
|
@@ -123,11 +125,11 @@ library UniERC20 {
|
|
|
123
125
|
// solhint-disable-next-line avoid-low-level-calls
|
|
124
126
|
(bool success, bytes memory result) = address(token).call(data);
|
|
125
127
|
if (!success) {
|
|
126
|
-
|
|
128
|
+
RevertReasonForwarder.reRevert();
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
if (result.length > 0) { // Return data is optional
|
|
130
|
-
|
|
132
|
+
if (!abi.decode(result, (bool))) revert ERC20OperationFailed();
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1inch/solidity-utils",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.14",
|
|
4
4
|
"main": "dist/src/index.js",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"repository": {
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
"@types/node": "17.0.23",
|
|
48
48
|
"@typescript-eslint/eslint-plugin": "5.18.0",
|
|
49
49
|
"@typescript-eslint/parser": "5.18.0",
|
|
50
|
+
"acquit": "1.2.1",
|
|
51
|
+
"commander": "^9.1.0",
|
|
50
52
|
"create-ts-index": "^1.14.0",
|
|
51
53
|
"cross-spawn": "7.0.3",
|
|
52
54
|
"dotenv": "16.0.0",
|
|
@@ -68,7 +70,8 @@
|
|
|
68
70
|
"typescript": "4.6.3"
|
|
69
71
|
},
|
|
70
72
|
"bin": {
|
|
71
|
-
"solidity-utils-docify": "utils/docify.utils.js"
|
|
73
|
+
"solidity-utils-docify": "utils/docify.utils.js",
|
|
74
|
+
"test-docgen": "utils/test-docgen.js"
|
|
72
75
|
},
|
|
73
76
|
"bugs": {
|
|
74
77
|
"url": "https://github.com/1inch/solidity-utils/issues"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = plugin;
|
|
4
|
+
|
|
5
|
+
function plugin(instance, options) {
|
|
6
|
+
if (instance) {
|
|
7
|
+
instance.output(markdown(options, instance));
|
|
8
|
+
} else {
|
|
9
|
+
const acquit = require('acquit');
|
|
10
|
+
acquit.output(markdown(options, acquit));
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
plugin.markdown = markdown;
|
|
15
|
+
|
|
16
|
+
function markdown(options, acquit) {
|
|
17
|
+
return function(res) {
|
|
18
|
+
return recurse(res, 0, options, acquit);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function recurse(blocks, level, options, acquit) {
|
|
23
|
+
var str = '';
|
|
24
|
+
var hashes = getHashes(level + 1);
|
|
25
|
+
for (var i = 0; i < blocks.length; ++i) {
|
|
26
|
+
if (blocks[i].contents) {
|
|
27
|
+
str += hashes + ' ' + (blocks[i].type === 'it' ? (!options || !options.it ? 'It ': '') : '') +
|
|
28
|
+
blocks[i].contents;
|
|
29
|
+
}
|
|
30
|
+
str += '\n\n';
|
|
31
|
+
for (var j = 0; j < blocks[i].comments.length; ++j) {
|
|
32
|
+
str += acquit.trimEachLine(blocks[i].comments[j]);
|
|
33
|
+
str += '\n\n';
|
|
34
|
+
}
|
|
35
|
+
if (blocks[i].type === 'describe') {
|
|
36
|
+
str += recurse(blocks[i].blocks, level + 1, options, acquit);
|
|
37
|
+
} else if (blocks[i].code.trim() && options.code) {
|
|
38
|
+
str += ['```javascript', blocks[i].code, '```'].join('\n');
|
|
39
|
+
}
|
|
40
|
+
if (i + 1 < blocks.length) {
|
|
41
|
+
str += '\n\n';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return str;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getHashes(level) {
|
|
48
|
+
var str = '';
|
|
49
|
+
for (var i = 0; i < level; ++i) {
|
|
50
|
+
str += '#';
|
|
51
|
+
}
|
|
52
|
+
return str;
|
|
53
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const commander = require('commander');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const program = new commander.Command();
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
program
|
|
10
|
+
.option('-i, --input <input>', 'tests directory', 'test')
|
|
11
|
+
.option('-x, --exclude [exclude]', 'exclude directories and files. omit argument to exclude all subdirectories', false)
|
|
12
|
+
.option('-o, --output <output>', 'file to write output', 'TESTS.md')
|
|
13
|
+
.option('-c, --code', 'include code', false)
|
|
14
|
+
.option('-l, --list', 'list tests only, do not include description', false)
|
|
15
|
+
.option('-d, --debug', 'debug mode', false);
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
program.parse(process.argv);
|
|
19
|
+
|
|
20
|
+
const options = program.opts();
|
|
21
|
+
const debugMode = options.debug;
|
|
22
|
+
const includeCode = options.code ? true : false;
|
|
23
|
+
const listOnly = options.list ? true : false;
|
|
24
|
+
const includeSubs = !(options.exclude === true);
|
|
25
|
+
const inputDir = options.input.split(';');
|
|
26
|
+
const outputFile = options.output;
|
|
27
|
+
const excludeDirs = (typeof options.exclude == 'boolean') ? [] : options.exclude.split(';');
|
|
28
|
+
|
|
29
|
+
if (debugMode){
|
|
30
|
+
console.log('----- DEBUG MODE -----');
|
|
31
|
+
console.log('options:', options);
|
|
32
|
+
console.log();
|
|
33
|
+
console.log('parsed options:');
|
|
34
|
+
console.log(
|
|
35
|
+
' includeCode:', includeCode,
|
|
36
|
+
'\n listOnly:', listOnly,
|
|
37
|
+
'\n inputDir:', inputDir,
|
|
38
|
+
'\n outputFile:', outputFile,
|
|
39
|
+
'\n includeSubs:', includeSubs,
|
|
40
|
+
'\n excludeDirs:', excludeDirs,
|
|
41
|
+
'\n debugMode:', debugMode
|
|
42
|
+
);
|
|
43
|
+
console.log('\nRemaining arguments: ', program.args);
|
|
44
|
+
console.log('\nFiles and directories found:');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let files = [];
|
|
48
|
+
function throughDirectory (directory, includeSubs, excludeDirs) {
|
|
49
|
+
if (!fs.existsSync(directory)) {
|
|
50
|
+
console.log('WARNING! Directory does not exist:', directory, '=> skipped');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fs.readdirSync(directory).forEach(file => {
|
|
55
|
+
const absolute = path.join(directory, file);
|
|
56
|
+
if (debugMode) console.log(absolute);
|
|
57
|
+
if (!excludeDirs.includes(absolute)) {
|
|
58
|
+
if (fs.statSync(absolute).isDirectory()){
|
|
59
|
+
if (includeSubs) throughDirectory(absolute, includeSubs, excludeDirs);
|
|
60
|
+
}
|
|
61
|
+
else files.push(absolute);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
inputDir.forEach(dir => {
|
|
67
|
+
throughDirectory(dir, includeSubs, excludeDirs);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (debugMode) console.log('\nfiles:', files);
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
//Script
|
|
75
|
+
const acquitMd = require('acquit')();
|
|
76
|
+
const acquitJson = require('acquit')();
|
|
77
|
+
require('./acquit-markdown.js')(acquitMd, { code: includeCode, it: true });
|
|
78
|
+
|
|
79
|
+
const legend = {};
|
|
80
|
+
let content;
|
|
81
|
+
let markdown = '';
|
|
82
|
+
let legendMd = '';
|
|
83
|
+
|
|
84
|
+
if (debugMode) console.log('\nFiles processed:');
|
|
85
|
+
files.forEach((file) => {
|
|
86
|
+
content = fs.readFileSync(file).toString();
|
|
87
|
+
legend.blocks = acquitJson.parse(content);
|
|
88
|
+
legend.contents = file;
|
|
89
|
+
legendMd += buildLegend(legend, 1, listOnly);
|
|
90
|
+
markdown += acquitMd.parse(content).toString();
|
|
91
|
+
markdown += '\n';
|
|
92
|
+
if (debugMode) console.log(' ', file, '=> done');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
content = listOnly ? legendMd : legendMd + markdown;
|
|
96
|
+
|
|
97
|
+
fs.writeFileSync(outputFile, content);
|
|
98
|
+
console.log('done');
|
|
99
|
+
|
|
100
|
+
function buildLegend (block, depth, listOnly) {
|
|
101
|
+
// console.log(depth, block.contents);
|
|
102
|
+
const url = (block.contents == null)
|
|
103
|
+
? ''
|
|
104
|
+
: block.contents.toLowerCase().trim()
|
|
105
|
+
.split(' ').join('-')
|
|
106
|
+
.split(/,|\+|\/|:|\(|\)/).join('')
|
|
107
|
+
.replace('--', '-');
|
|
108
|
+
let legend = listOnly
|
|
109
|
+
? Array(depth).join(' ') + '* ' + block.contents + '\n'
|
|
110
|
+
: Array(depth).join(' ') + '* [' + block.contents + '](#' + url + ')\n';
|
|
111
|
+
if (block.blocks) {
|
|
112
|
+
legend += block.blocks.map(function (child) {
|
|
113
|
+
return buildLegend(child, depth + 1, listOnly);
|
|
114
|
+
}).join('');
|
|
115
|
+
}
|
|
116
|
+
return legend;
|
|
117
|
+
}
|