@1inch/solidity-utils 1.2.5 → 1.3.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.
- package/README.md +1 -1
- package/contracts/libraries/AddressArray.sol +13 -3
- package/js/permit.js +111 -0
- package/js/profileEVM.js +14 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ This repository contains frequently used smart contracts, libraries and interfac
|
|
|
40
40
|
|utils|`signMessage(signer, messageHex) `|signs `messageHex` with `signer` and patchs ganache's signature to geth's version|
|
|
41
41
|
|utils|`countInstructions(txHash, instruction)`|counts amount of `instruction` in transaction with `txHash` hash|
|
|
42
42
|
|profileEVM|`profileEVM(txHash, instruction, optionalTraceFile)`|the same as the `countInstructions()` with option of writing all trace to `optionalTraceFile`|
|
|
43
|
-
|profileEVM|`gasspectEVM(txHash, optionalTraceFile)`|returns all used operations in `txHash` transaction and their costs with option of writing all trace to `optionalTraceFile`|
|
|
43
|
+
|profileEVM|`gasspectEVM(txHash, options, optionalTraceFile)`|returns all used operations in `txHash` transaction with `options` and their costs with option of writing all trace to `optionalTraceFile`|
|
|
44
44
|
|
|
45
45
|
### UTILS
|
|
46
46
|
|
|
@@ -18,14 +18,24 @@ library AddressArray {
|
|
|
18
18
|
|
|
19
19
|
function get(Data storage self) internal view returns(address[] memory arr) {
|
|
20
20
|
uint256 lengthAndFirst = self._raw[0];
|
|
21
|
+
arr = new address[](lengthAndFirst >> 160);
|
|
22
|
+
_get(self, arr, lengthAndFirst);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function get(Data storage self, address[] memory output) internal view returns(address[] memory) {
|
|
26
|
+
return _get(self, output, self._raw[0]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _get(Data storage self, address[] memory output, uint256 lengthAndFirst) private view returns(address[] memory) {
|
|
21
30
|
uint256 len = lengthAndFirst >> 160;
|
|
22
|
-
|
|
31
|
+
require(len <= output.length, "AddressArray: too small output");
|
|
23
32
|
if (len > 0) {
|
|
24
|
-
|
|
33
|
+
output[0] = address(uint160(lengthAndFirst));
|
|
25
34
|
for (uint i = 1; i < len; i++) {
|
|
26
|
-
|
|
35
|
+
output[i] = address(uint160(self._raw[i]));
|
|
27
36
|
}
|
|
28
37
|
}
|
|
38
|
+
return output;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
function push(Data storage self, address account) internal returns(uint256) {
|
package/js/permit.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const { constants } = require('@openzeppelin/test-helpers');
|
|
2
|
+
const ethSigUtil = require('eth-sig-util');
|
|
3
|
+
const { fromRpcSig } = require('ethereumjs-util');
|
|
4
|
+
|
|
5
|
+
const defaultDeadline = constants.MAX_UINT256;
|
|
6
|
+
|
|
7
|
+
const EIP712Domain = [
|
|
8
|
+
{ name: 'name', type: 'string' },
|
|
9
|
+
{ name: 'version', type: 'string' },
|
|
10
|
+
{ name: 'chainId', type: 'uint256' },
|
|
11
|
+
{ name: 'verifyingContract', type: 'address' },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const Permit = [
|
|
15
|
+
{ name: 'owner', type: 'address' },
|
|
16
|
+
{ name: 'spender', type: 'address' },
|
|
17
|
+
{ name: 'value', type: 'uint256' },
|
|
18
|
+
{ name: 'nonce', type: 'uint256' },
|
|
19
|
+
{ name: 'deadline', type: 'uint256' },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const DaiLikePermit = [
|
|
23
|
+
{ name: 'holder', type: 'address' },
|
|
24
|
+
{ name: 'spender', type: 'address' },
|
|
25
|
+
{ name: 'nonce', type: 'uint256' },
|
|
26
|
+
{ name: 'expiry', type: 'uint256' },
|
|
27
|
+
{ name: 'allowed', type: 'bool' },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
function trim0x (bigNumber) {
|
|
31
|
+
const s = bigNumber.toString();
|
|
32
|
+
if (s.startsWith('0x')) {
|
|
33
|
+
return s.substring(2);
|
|
34
|
+
}
|
|
35
|
+
return s;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function cutSelector (data) {
|
|
39
|
+
const hexPrefix = '0x';
|
|
40
|
+
return hexPrefix + data.substr(hexPrefix.length + 8);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function domainSeparator (name, version, chainId, verifyingContract) {
|
|
44
|
+
return '0x' + ethSigUtil.TypedDataUtils.hashStruct(
|
|
45
|
+
'EIP712Domain',
|
|
46
|
+
{ name, version, chainId, verifyingContract },
|
|
47
|
+
{ EIP712Domain },
|
|
48
|
+
).toString('hex');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildData (name, version, chainId, verifyingContract, owner, spender, value, nonce, deadline = defaultDeadline) {
|
|
52
|
+
return {
|
|
53
|
+
primaryType: 'Permit',
|
|
54
|
+
types: { EIP712Domain, Permit },
|
|
55
|
+
domain: { name, version, chainId, verifyingContract },
|
|
56
|
+
message: { owner, spender, value, nonce, deadline },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildDataLikeDai (name, version, chainId, verifyingContract, holder, spender, nonce, allowed, expiry = defaultDeadline) {
|
|
61
|
+
return {
|
|
62
|
+
primaryType: 'Permit',
|
|
63
|
+
types: { EIP712Domain, Permit: DaiLikePermit },
|
|
64
|
+
domain: { name, version, chainId, verifyingContract },
|
|
65
|
+
message: { holder, spender, nonce, expiry, allowed },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* @param permitContract The contract object with ERC20Permit type and token address for which the permit creating.
|
|
71
|
+
*/
|
|
72
|
+
async function getPermit (owner, ownerPrivateKey, permitContract, tokenVersion, chainId, spender, value, deadline = defaultDeadline) {
|
|
73
|
+
const nonce = await permitContract.nonces(owner);
|
|
74
|
+
const name = await permitContract.name();
|
|
75
|
+
const data = buildData(name, tokenVersion, chainId, permitContract.address, owner, spender, value, nonce, deadline);
|
|
76
|
+
const signature = ethSigUtil.signTypedMessage(Buffer.from(trim0x(ownerPrivateKey), 'hex'), { data });
|
|
77
|
+
const { v, r, s } = fromRpcSig(signature);
|
|
78
|
+
const permitCall = permitContract.contract.methods.permit(owner, spender, value, deadline, v, r, s).encodeABI();
|
|
79
|
+
return cutSelector(permitCall);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* @param permitContract The contract object with ERC20PermitLikeDai type and token address for which the permit creating.
|
|
84
|
+
*/
|
|
85
|
+
async function getPermitLikeDai (holder, holderPrivateKey, permitContract, tokenVersion, chainId, spender, allowed, expiry = defaultDeadline) {
|
|
86
|
+
const nonce = await permitContract.nonces(holder);
|
|
87
|
+
const name = await permitContract.name();
|
|
88
|
+
const data = buildDataLikeDai(name, tokenVersion, chainId, permitContract.address, holder, spender, nonce, allowed, expiry);
|
|
89
|
+
const signature = ethSigUtil.signTypedMessage(Buffer.from(trim0x(holderPrivateKey), 'hex'), { data });
|
|
90
|
+
const { v, r, s } = fromRpcSig(signature);
|
|
91
|
+
const permitCall = permitContract.contract.methods.permit(holder, spender, nonce, expiry, allowed, v, r, s).encodeABI();
|
|
92
|
+
return cutSelector(permitCall);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function withTarget (target, data) {
|
|
96
|
+
return target.toString() + trim0x(data);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
defaultDeadline,
|
|
101
|
+
EIP712Domain,
|
|
102
|
+
Permit,
|
|
103
|
+
DaiLikePermit,
|
|
104
|
+
trim0x,
|
|
105
|
+
domainSeparator,
|
|
106
|
+
buildData,
|
|
107
|
+
buildDataLikeDai,
|
|
108
|
+
getPermit,
|
|
109
|
+
getPermitLikeDai,
|
|
110
|
+
withTarget,
|
|
111
|
+
};
|
package/js/profileEVM.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const { promisify } = require('util');
|
|
2
2
|
const fs = require('fs').promises;
|
|
3
3
|
|
|
4
|
+
const gasspectOptionsDefault = {
|
|
5
|
+
minOpGasCost: 300, // minimal gas cost of returned operations
|
|
6
|
+
args: false, // return operations arguments
|
|
7
|
+
res: false, // return operations results
|
|
8
|
+
};
|
|
9
|
+
|
|
4
10
|
function _normalizeOp (ops, i) {
|
|
5
11
|
if (ops[i].op === 'STATICCALL') {
|
|
6
12
|
ops[i].gasCost = ops[i].gasCost - ops[i + 1].gas;
|
|
@@ -53,7 +59,10 @@ function _normalizeOp (ops, i) {
|
|
|
53
59
|
if (ops[i].gasCost === 20000) {
|
|
54
60
|
ops[i].op += '_I';
|
|
55
61
|
}
|
|
56
|
-
|
|
62
|
+
|
|
63
|
+
if (ops[i].op.startsWith('SLOAD')) {
|
|
64
|
+
ops[i].res = ops[i + 1].stack[ops[i + 1].stack.length - 1];
|
|
65
|
+
}
|
|
57
66
|
}
|
|
58
67
|
if (ops[i].op === 'EXTCODESIZE') {
|
|
59
68
|
ops[i].args = [
|
|
@@ -86,7 +95,9 @@ async function profileEVM (txHash, instruction, optionalTraceFile) {
|
|
|
86
95
|
return str.split('"' + instruction.toUpperCase() + '"').length - 1;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
|
-
async function gasspectEVM (txHash, options =
|
|
98
|
+
async function gasspectEVM (txHash, options = gasspectOptionsDefault, optionalTraceFile) {
|
|
99
|
+
options = { ...gasspectOptionsDefault, ...options };
|
|
100
|
+
|
|
90
101
|
const trace = await promisify(web3.currentProvider.send.bind(web3.currentProvider))({
|
|
91
102
|
jsonrpc: '2.0',
|
|
92
103
|
method: 'debug_traceTransaction',
|
|
@@ -111,8 +122,7 @@ async function gasspectEVM (txHash, options = {}, optionalTraceFile) {
|
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
const
|
|
115
|
-
const result = ops.filter(op => op.gasCost > minOpGasCost).map(op => op.traceAddress.join('-') + '-' + op.op +
|
|
125
|
+
const result = ops.filter(op => op.gasCost > options.minOpGasCost).map(op => op.traceAddress.join('-') + '-' + op.op +
|
|
116
126
|
(options.args ? '(' + (op.args || []).join(',') + ')' : '') +
|
|
117
127
|
(options.res ? (op.res ? ':0x' + op.res : '') : '') +
|
|
118
128
|
' = ' + op.gasCost);
|