@1inch/solidity-utils 1.2.2 → 1.2.7

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 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
 
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
 
3
- pragma solidity 0.8.9;
3
+ pragma solidity ^0.8.0;
4
4
 
5
5
 
6
6
  library AddressArray {
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
 
3
- pragma solidity 0.8.9;
3
+ pragma solidity ^0.8.0;
4
4
 
5
5
  import "./AddressArray.sol";
6
6
 
package/js/permit.js ADDED
@@ -0,0 +1,110 @@
1
+ const { constants } = require('@openzeppelin/test-helpers');
2
+ const ethSigUtil = require('eth-sig-util');
3
+ const { fromRpcSig } = require('ethereumjs-util');
4
+ const { artifacts } = require('hardhat');
5
+ const ERC20Permit = artifacts.require('@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol:ERC20Permit');
6
+ const ERC20PermitLikeDai = artifacts.require('DaiLikePermitMock');
7
+
8
+ const defaultDeadline = constants.MAX_UINT256;
9
+
10
+ const EIP712Domain = [
11
+ { name: 'name', type: 'string' },
12
+ { name: 'version', type: 'string' },
13
+ { name: 'chainId', type: 'uint256' },
14
+ { name: 'verifyingContract', type: 'address' },
15
+ ];
16
+
17
+ const Permit = [
18
+ { name: 'owner', type: 'address' },
19
+ { name: 'spender', type: 'address' },
20
+ { name: 'value', type: 'uint256' },
21
+ { name: 'nonce', type: 'uint256' },
22
+ { name: 'deadline', type: 'uint256' },
23
+ ];
24
+
25
+ const DaiLikePermit = [
26
+ { name: 'holder', type: 'address' },
27
+ { name: 'spender', type: 'address' },
28
+ { name: 'nonce', type: 'uint256' },
29
+ { name: 'expiry', type: 'uint256' },
30
+ { name: 'allowed', type: 'bool' },
31
+ ];
32
+
33
+ function trim0x (bigNumber) {
34
+ const s = bigNumber.toString();
35
+ if (s.startsWith('0x')) {
36
+ return s.substring(2);
37
+ }
38
+ return s;
39
+ }
40
+
41
+ function cutSelector (data) {
42
+ const hexPrefix = '0x';
43
+ return hexPrefix + data.substr(hexPrefix.length + 8);
44
+ }
45
+
46
+ function domainSeparator (name, version, chainId, verifyingContract) {
47
+ return '0x' + ethSigUtil.TypedDataUtils.hashStruct(
48
+ 'EIP712Domain',
49
+ { name, version, chainId, verifyingContract },
50
+ { EIP712Domain },
51
+ ).toString('hex');
52
+ }
53
+
54
+ function buildData (name, version, chainId, verifyingContract, owner, spender, value, nonce, deadline = defaultDeadline) {
55
+ return {
56
+ primaryType: 'Permit',
57
+ types: { EIP712Domain, Permit },
58
+ domain: { name, version, chainId, verifyingContract },
59
+ message: { owner, spender, value, nonce, deadline },
60
+ };
61
+ }
62
+
63
+ function buildDataLikeDai (name, version, chainId, verifyingContract, holder, spender, nonce, allowed, expiry = defaultDeadline) {
64
+ return {
65
+ primaryType: 'Permit',
66
+ types: { EIP712Domain, Permit: DaiLikePermit },
67
+ domain: { name, version, chainId, verifyingContract },
68
+ message: { holder, spender, nonce, expiry, allowed },
69
+ };
70
+ }
71
+
72
+ async function getPermit (owner, ownerPrivateKey, token, tokenVersion, chainId, spender, value, deadline = defaultDeadline) {
73
+ const permitContract = await ERC20Permit.at(token.address);
74
+ const nonce = await permitContract.nonces(owner);
75
+ const name = await permitContract.name();
76
+ const data = buildData(name, tokenVersion, chainId, token.address, owner, spender, value, nonce, deadline);
77
+ const signature = ethSigUtil.signTypedMessage(Buffer.from(trim0x(ownerPrivateKey), 'hex'), { data });
78
+ const { v, r, s } = fromRpcSig(signature);
79
+ const permitCall = permitContract.contract.methods.permit(owner, spender, value, deadline, v, r, s).encodeABI();
80
+ return cutSelector(permitCall);
81
+ }
82
+
83
+ async function getPermitLikeDai (holder, holderPrivateKey, token, tokenVersion, chainId, spender, allowed, expiry = defaultDeadline) {
84
+ const permitContract = await ERC20PermitLikeDai.at(token.address);
85
+ const nonce = await permitContract.nonces(holder);
86
+ const name = await permitContract.name();
87
+ const data = buildDataLikeDai(name, tokenVersion, chainId, token.address, holder, spender, nonce, allowed, expiry);
88
+ const signature = ethSigUtil.signTypedMessage(Buffer.from(trim0x(holderPrivateKey), 'hex'), { data });
89
+ const { v, r, s } = fromRpcSig(signature);
90
+ const permitCall = permitContract.contract.methods.permit(holder, spender, nonce, expiry, allowed, v, r, s).encodeABI();
91
+ return cutSelector(permitCall);
92
+ }
93
+
94
+ function withTarget (target, data) {
95
+ return target.toString() + trim0x(data);
96
+ }
97
+
98
+ module.exports = {
99
+ defaultDeadline,
100
+ EIP712Domain,
101
+ Permit,
102
+ DaiLikePermit,
103
+ trim0x,
104
+ domainSeparator,
105
+ buildData,
106
+ buildDataLikeDai,
107
+ getPermit,
108
+ getPermitLikeDai,
109
+ withTarget,
110
+ };
package/js/profileEVM.js CHANGED
@@ -1,23 +1,74 @@
1
1
  const { promisify } = require('util');
2
2
  const fs = require('fs').promises;
3
3
 
4
- function _normalizeOp (op) {
5
- if (op.op === 'STATICCALL') {
6
- if (op.stack.length > 8 && op.stack[op.stack.length - 8] === '0000000000000000000000000000000000000000000000000000000000000001') {
7
- op.gasCost = 700 + 3000;
8
- op.op = 'STATICCALL-ECRECOVER';
9
- } else if (op.stack.length > 8 && op.stack[op.stack.length - 8] <= '00000000000000000000000000000000000000000000000000000000000000FF') {
10
- op.gasCost = 700;
11
- op.op = 'STATICCALL-' + op.stack[op.stack.length - 8].substr(62, 2);
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
+
10
+ function _normalizeOp (ops, i) {
11
+ if (ops[i].op === 'STATICCALL') {
12
+ ops[i].gasCost = ops[i].gasCost - ops[i + 1].gas;
13
+
14
+ if (ops[i].stack.length > 8 && ops[i].stack[ops[i].stack.length - 8] === '0000000000000000000000000000000000000000000000000000000000000001') {
15
+ ops[i].op = 'STATICCALL-ECRECOVER';
16
+ } else if (ops[i].stack.length > 8 && ops[i].stack[ops[i].stack.length - 8] <= '00000000000000000000000000000000000000000000000000000000000000FF') {
17
+ ops[i].op = 'STATICCALL-' + ops[i].stack[ops[i].stack.length - 8].substr(62, 2);
12
18
  } else {
13
- op.gasCost = 700;
19
+ ops[i].args = [
20
+ '0x' + ops[i].stack[ops[i].stack.length - 2].substr(24),
21
+ '0x' + (ops[i].memory || []).join('').substr(
22
+ 2 * web3.utils.toBN(ops[i].stack[ops[i].stack.length - 3]).toNumber(),
23
+ 2 * web3.utils.toBN(ops[i].stack[ops[i].stack.length - 4]).toNumber(),
24
+ ),
25
+ ];
26
+ if (ops[i].gasCost === 100) {
27
+ ops[i].op += '_R';
28
+ }
14
29
  }
15
30
  }
16
- if (['CALL', 'DELEGATECALL', 'CALLCODE'].indexOf(op.op) !== -1) {
17
- op.gasCost = 700;
31
+ if (['CALL', 'DELEGATECALL', 'CALLCODE'].indexOf(ops[i].op) !== -1) {
32
+ ops[i].args = [
33
+ '0x' + ops[i].stack[ops[i].stack.length - 2].substr(24),
34
+ '0x' + (ops[i].memory || []).join('').substr(
35
+ 2 * web3.utils.toBN(ops[i].stack[ops[i].stack.length - 4]).toNumber(),
36
+ 2 * web3.utils.toBN(ops[i].stack[ops[i].stack.length - 5]).toNumber(),
37
+ ),
38
+ ];
39
+ ops[i].gasCost = ops[i].gasCost - ops[i + 1].gas;
40
+ ops[i].res = ops[i + 1].stack[ops[i + 1].stack.length - 1];
41
+
42
+ if (ops[i].gasCost === 100) {
43
+ ops[i].op += '_R';
44
+ }
18
45
  }
19
- if (['RETURN', 'REVERT', 'INVALID'].indexOf(op.op) !== -1) {
20
- op.gasCost = 3;
46
+ if (['RETURN', 'REVERT', 'INVALID'].indexOf(ops[i].op) !== -1) {
47
+ ops[i].gasCost = 3;
48
+ }
49
+ if (['SSTORE', 'SLOAD'].indexOf(ops[i].op) !== -1) {
50
+ ops[i].args = [
51
+ '0x' + ops[i].stack[ops[i].stack.length - 1],
52
+ ];
53
+ if (ops[i].op === 'SSTORE') {
54
+ ops[i].args.push('0x' + ops[i].stack[ops[i].stack.length - 2]);
55
+ }
56
+ if (ops[i].gasCost === 100) {
57
+ ops[i].op += '_R';
58
+ }
59
+ if (ops[i].gasCost === 20000) {
60
+ ops[i].op += '_I';
61
+ }
62
+
63
+ if (ops[i].op.startsWith('SLOAD')) {
64
+ ops[i].res = ops[i + 1].stack[ops[i + 1].stack.length - 1];
65
+ }
66
+ }
67
+ if (ops[i].op === 'EXTCODESIZE') {
68
+ ops[i].args = [
69
+ '0x' + ops[i].stack[ops[i].stack.length - 1].substr(24),
70
+ ];
71
+ ops[i].res = ops[i + 1].stack[ops[i + 1].stack.length - 1];
21
72
  }
22
73
  }
23
74
 
@@ -44,7 +95,9 @@ async function profileEVM (txHash, instruction, optionalTraceFile) {
44
95
  return str.split('"' + instruction.toUpperCase() + '"').length - 1;
45
96
  }
46
97
 
47
- async function gasspectEVM (txHash, optionalTraceFile) {
98
+ async function gasspectEVM (txHash, options = gasspectOptionsDefault, optionalTraceFile) {
99
+ options = { ...gasspectOptionsDefault, ...options };
100
+
48
101
  const trace = await promisify(web3.currentProvider.send.bind(web3.currentProvider))({
49
102
  jsonrpc: '2.0',
50
103
  method: 'debug_traceTransaction',
@@ -55,9 +108,9 @@ async function gasspectEVM (txHash, optionalTraceFile) {
55
108
  const ops = trace.result.structLogs;
56
109
 
57
110
  const traceAddress = [0, -1];
58
- for (const op of ops) {
111
+ for (const [i, op] of ops.entries()) {
59
112
  op.traceAddress = traceAddress.slice(0, traceAddress.length - 1);
60
- _normalizeOp(op);
113
+ _normalizeOp(ops, i);
61
114
 
62
115
  if (op.depth + 2 > traceAddress.length) {
63
116
  traceAddress[traceAddress.length - 1] += 1;
@@ -69,7 +122,10 @@ async function gasspectEVM (txHash, optionalTraceFile) {
69
122
  }
70
123
  }
71
124
 
72
- const result = ops.filter(op => op.gasCost > 300).map(op => op.traceAddress.join('-') + '-' + op.op + ' = ' + op.gasCost);
125
+ const result = ops.filter(op => op.gasCost > options.minOpGasCost).map(op => op.traceAddress.join('-') + '-' + op.op +
126
+ (options.args ? '(' + (op.args || []).join(',') + ')' : '') +
127
+ (options.res ? (op.res ? ':0x' + op.res : '') : '') +
128
+ ' = ' + op.gasCost);
73
129
 
74
130
  if (optionalTraceFile) {
75
131
  await fs.writeFile(optionalTraceFile, JSON.stringify(result));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1inch/solidity-utils",
3
- "version": "1.2.2",
3
+ "version": "1.2.7",
4
4
  "main": "index.js",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,28 +8,29 @@
8
8
  },
9
9
  "license": "MIT",
10
10
  "dependencies": {
11
- "@openzeppelin/contracts": "^4.3.2"
11
+ "@openzeppelin/contracts": "^4.4.0",
12
+ "ethereumjs-wallet": "^1.0.2"
12
13
  },
13
14
  "devDependencies": {
14
- "@nomiclabs/hardhat-etherscan": "^2.1.6",
15
+ "@nomiclabs/hardhat-etherscan": "^2.1.8",
15
16
  "@nomiclabs/hardhat-truffle5": "^2.0.0",
16
17
  "@nomiclabs/hardhat-web3": "^2.0.0",
17
- "@openzeppelin/test-helpers": "^0.5.13",
18
+ "@openzeppelin/test-helpers": "^0.5.15",
18
19
  "chai": "^4.3.4",
19
20
  "cross-spawn": "^7.0.3",
20
21
  "dotenv": "^10.0.0",
21
- "eslint": "^7.32.0",
22
+ "eslint": "^8.3.0",
22
23
  "eslint-config-standard": "^16.0.3",
23
- "eslint-plugin-import": "^2.24.2",
24
+ "eslint-plugin-import": "^2.25.3",
24
25
  "eslint-plugin-node": "^11.1.0",
25
- "eslint-plugin-promise": "^5.1.0",
26
+ "eslint-plugin-promise": "^5.1.1",
26
27
  "eslint-plugin-standard": "^5.0.0",
27
- "hardhat": "^2.6.4",
28
- "hardhat-deploy": "^0.9.2",
29
- "hardhat-gas-reporter": "^1.0.4",
28
+ "hardhat": "^2.7.0",
29
+ "hardhat-deploy": "^0.9.14",
30
+ "hardhat-gas-reporter": "^1.0.6",
30
31
  "rimraf": "^3.0.2",
31
32
  "shx": "^0.3.3",
32
- "solc": "^0.8.9",
33
+ "solc": "^0.8.10",
33
34
  "solhint": "^3.3.6",
34
35
  "solidity-coverage": "^0.7.17"
35
36
  },
@@ -37,7 +38,7 @@
37
38
  "solidity-utils-docify": "utils/docify.utils.js"
38
39
  },
39
40
  "scripts": {
40
- "build": "rimraf ./dist && mkdir dist && shx cp -R js ./dist/js && shx cp -R utils ./dist/utils && shx cp package.json ./dist && cp -R ./contracts ./dist/contracts && rm -rf dist/contracts/mocks dist/contracts/tests",
41
+ "build": "rimraf ./dist && mkdir dist && shx cp -R js ./dist/js && shx cp -R utils ./dist/utils && shx cp package.json ./dist && shx cp README.md ./dist && shx cp -R ./contracts ./dist/contracts && shx rm -rf dist/contracts/mocks dist/contracts/tests",
41
42
  "coverage": "hardhat coverage",
42
43
  "lint": "yarn run lint:js && yarn run lint:sol",
43
44
  "lint:fix": "yarn run lint:js:fix && yarn run lint:sol:fix",
@@ -55,5 +56,5 @@
55
56
  "test": "test"
56
57
  },
57
58
  "author": "1inch",
58
- "description": ""
59
+ "description": "Solidity and JS utils"
59
60
  }
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules/
2
- coverage/
package/.eslintrc DELETED
@@ -1,52 +0,0 @@
1
- {
2
- "extends" : [
3
- "standard",
4
- "plugin:promise/recommended"
5
- ],
6
- "plugins": [
7
- "promise"
8
- ],
9
- "env": {
10
- "browser" : true,
11
- "node" : true,
12
- "mocha" : true,
13
- "jest" : true
14
- },
15
- "globals" : {
16
- "artifacts": false,
17
- "contract": false,
18
- "assert": false,
19
- "web3": false
20
- },
21
- "rules": {
22
-
23
- // Strict mode
24
- "strict": [2, "global"],
25
-
26
- // Code style
27
- "indent": [2, 4],
28
- "quotes": [2, "single"],
29
- "semi": ["error", "always"],
30
- "space-before-function-paren": ["error", "always"],
31
- "no-use-before-define": 0,
32
- "no-unused-expressions": "off",
33
- "eqeqeq": [2, "smart"],
34
- "dot-notation": [2, {"allowKeywords": true, "allowPattern": ""}],
35
- "no-redeclare": [2, {"builtinGlobals": true}],
36
- "no-trailing-spaces": [2, { "skipBlankLines": true }],
37
- "eol-last": 1,
38
- "comma-spacing": [2, {"before": false, "after": true}],
39
- "camelcase": [2, {"properties": "always"}],
40
- "no-mixed-spaces-and-tabs": [2, "smart-tabs"],
41
- "comma-dangle": [1, "always-multiline"],
42
- "no-dupe-args": 2,
43
- "no-dupe-keys": 2,
44
- "no-debugger": 0,
45
- "no-undef": 2,
46
- "object-curly-spacing": [2, "always"],
47
- "max-len": [2, 200, 2],
48
- "generator-star-spacing": ["error", "before"],
49
- "promise/avoid-new": 0,
50
- "promise/always-return": 0
51
- }
52
- }
@@ -1,86 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [ master ]
6
- pull_request:
7
- branches: [ master ]
8
- jobs:
9
- lint:
10
- runs-on: ubuntu-latest
11
-
12
- steps:
13
- - uses: actions/checkout@v2
14
-
15
- - uses: actions/setup-node@v2
16
- with:
17
- node-version: '16.x'
18
-
19
- - run: npm install -g yarn
20
-
21
- - id: yarn-cache
22
- run: echo "::set-output name=dir::$(yarn cache dir)"
23
-
24
- - uses: actions/cache@v2
25
- with:
26
- path: ${{ steps.yarn-cache.outputs.dir }}
27
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
28
- restore-keys: |
29
- ${{ runner.os }}-yarn-
30
-
31
- - run: yarn
32
- - run: yarn lint
33
-
34
- test:
35
- runs-on: ubuntu-latest
36
-
37
- steps:
38
- - uses: actions/checkout@v2
39
-
40
- - uses: actions/setup-node@v2
41
- with:
42
- node-version: '16.x'
43
-
44
- - run: npm install -g yarn
45
-
46
- - id: yarn-cache
47
- run: echo "::set-output name=dir::$(yarn cache dir)"
48
-
49
- - uses: actions/cache@v2
50
- with:
51
- path: ${{ steps.yarn-cache.outputs.dir }}
52
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
53
- restore-keys: |
54
- ${{ runner.os }}-yarn-
55
-
56
- - run: yarn
57
- - run: yarn test
58
-
59
- coverage:
60
- runs-on: ubuntu-latest
61
- steps:
62
- - uses: actions/checkout@v2
63
-
64
- - uses: actions/setup-node@v2
65
- with:
66
- node-version: 16.x
67
-
68
- - run: npm install -g yarn
69
-
70
- - id: yarn-cache
71
- run: echo "::set-output name=dir::$(yarn cache dir)"
72
-
73
- - uses: actions/cache@v2
74
- with:
75
- path: ${{ steps.yarn-cache.outputs.dir }}
76
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
77
- restore-keys: |
78
- ${{ runner.os }}-yarn-
79
-
80
- - run: yarn
81
- - run: yarn coverage
82
-
83
- - name: Coveralls
84
- uses: coverallsapp/github-action@v1.1.2
85
- with:
86
- github-token: ${{ secrets.GITHUB_TOKEN }}
package/.solcover.js DELETED
@@ -1,9 +0,0 @@
1
- module.exports = {
2
- skipFiles: [
3
- 'mocks', 'tests'
4
- ],
5
- mocha: {
6
- grep: "@skip-on-coverage",
7
- invert: true
8
- },
9
- }
package/.solhint.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "solhint:recommended",
3
- "rules": {
4
- "compiler-version": [
5
- "error",
6
- "^0.8.0"
7
- ],
8
- "private-vars-leading-underscore": "error",
9
- "func-visibility": ["error", { "ignoreConstructors": true }]
10
- }
11
- }
@@ -1,36 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
-
3
- pragma solidity 0.8.9;
4
-
5
- import "../libraries/AddressArray.sol";
6
-
7
-
8
- contract AddressArrayMock {
9
- using AddressArray for AddressArray.Data;
10
-
11
- AddressArray.Data private _self;
12
-
13
- function length() external view returns(uint256) {
14
- return AddressArray.length(_self);
15
- }
16
-
17
- function at(uint i) external view returns(address) {
18
- return AddressArray.at(_self, i);
19
- }
20
-
21
- function get() external view returns(address[] memory arr) {
22
- return AddressArray.get(_self);
23
- }
24
-
25
- function push(address account) external returns(uint256) {
26
- return AddressArray.push(_self, account);
27
- }
28
-
29
- function pop() external {
30
- AddressArray.pop(_self);
31
- }
32
-
33
- function set(uint256 index, address account) external {
34
- AddressArray.set(_self, index, account);
35
- }
36
- }
@@ -1,32 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
-
3
- pragma solidity 0.8.9;
4
-
5
- import "../libraries/AddressSet.sol";
6
-
7
-
8
- contract AddressSetMock {
9
- using AddressSet for AddressSet.Data;
10
-
11
- AddressSet.Data private _self;
12
-
13
- function length() external view returns(uint) {
14
- return AddressSet.length(_self);
15
- }
16
-
17
- function at(uint index) external view returns(address) {
18
- return AddressSet.at(_self, index);
19
- }
20
-
21
- function contains(address item) external view returns(bool) {
22
- return AddressSet.contains(_self, item);
23
- }
24
-
25
- function add(address item) external returns(bool) {
26
- return AddressSet.add(_self, item);
27
- }
28
-
29
- function remove(address item) external returns(bool) {
30
- return AddressSet.remove(_self, item);
31
- }
32
- }
@@ -1,21 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
-
3
- pragma solidity ^0.8.0;
4
- pragma abicoder v1;
5
-
6
- import "@openzeppelin/contracts/access/Ownable.sol";
7
- import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
8
-
9
-
10
- contract TokenMock is ERC20, Ownable {
11
- // solhint-disable-next-line no-empty-blocks
12
- constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
13
-
14
- function mint(address account, uint256 amount) external onlyOwner {
15
- _mint(account, amount);
16
- }
17
-
18
- function burn(address account, uint256 amount) external onlyOwner {
19
- _burn(account, amount);
20
- }
21
- }
@@ -1,25 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
-
3
- pragma solidity ^0.8.0;
4
- pragma abicoder v1;
5
-
6
- library StringUtilNaive {
7
- bytes16 private constant _ALPHABET = 0x30313233343536373839616263646566;
8
-
9
- function toHex(uint256 value) internal pure returns(string memory) {
10
- return toHex(abi.encodePacked(value));
11
- }
12
-
13
- function toHex(bytes memory data) internal pure returns(string memory) {
14
- unchecked {
15
- bytes memory str = new bytes(2 + data.length * 2);
16
- str[0] = "0";
17
- str[1] = "x";
18
- for (uint256 i = 0; i < data.length; i++) {
19
- str[2 * i + 2] = _ALPHABET[uint8(data[i] >> 4)];
20
- str[2 * i + 3] = _ALPHABET[uint8(data[i] & 0x0f)];
21
- }
22
- return string(str);
23
- }
24
- }
25
- }