├── .github
└── workflows
│ └── check-dist.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── action.yml
├── dist
├── index.js
├── index.js.map
├── licenses.txt
└── sourcemap-register.js
├── funding.json
├── package.json
├── src
├── format.ts
├── index.ts
├── report.ts
└── types.ts
├── tests
├── mocks
│ ├── 1-1.md
│ ├── 1-2.md
│ ├── gas_report.1.ansi
│ └── gas_report.2.ansi
└── report.test.ts
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
/.github/workflows/check-dist.yml:
--------------------------------------------------------------------------------
1 | name: Check dist/
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - "**.md"
9 | pull_request:
10 | paths-ignore:
11 | - "**.md"
12 | workflow_dispatch:
13 |
14 | jobs:
15 | check-dist:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v4
20 |
21 | - uses: actions/setup-node@v4
22 | with:
23 | node-version: 22
24 | cache: yarn
25 |
26 | - name: Install dependencies
27 | run: yarn install --frozen-lockfile
28 |
29 | - name: Rebuild the dist/ directory
30 | run: yarn release
31 |
32 | - name: Compare the expected and actual dist/ directories
33 | run: |
34 | if [ "$(git diff --ignore-space-at-eol --text dist/ | wc -l)" -gt "0" ]; then
35 | echo "Detected uncommitted changes after build. See status below:"
36 | git diff
37 | exit 1
38 | fi
39 | id: diff
40 |
41 | # If index.js was different than expected, upload the expected version as an artifact
42 | - uses: actions/upload-artifact@v4
43 | if: ${{ failure() && steps.diff.conclusion == 'failure' }}
44 | with:
45 | name: dist
46 | path: dist/
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directory
2 | node_modules
3 |
4 | # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # Diagnostic reports (https://nodejs.org/api/report.html)
14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 | *.lcov
28 |
29 | # nyc test coverage
30 | .nyc_output
31 |
32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33 | .grunt
34 |
35 | # Bower dependency directory (https://bower.io/)
36 | bower_components
37 |
38 | # node-waf configuration
39 | .lock-wscript
40 |
41 | # Compiled binary addons (https://nodejs.org/api/addons.html)
42 | build/Release
43 |
44 | # Dependency directories
45 | jspm_packages/
46 |
47 | # TypeScript v1 declaration files
48 | typings/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Optional REPL history
60 | .node_repl_history
61 |
62 | # Output of 'npm pack'
63 | *.tgz
64 |
65 | # Yarn Integrity file
66 | .yarn-integrity
67 |
68 | # dotenv environment variables file
69 | .env
70 | .env.test
71 |
72 | # parcel-bundler cache (https://parceljs.org/)
73 | .cache
74 |
75 | # next.js build output
76 | .next
77 |
78 | # nuxt.js build output
79 | .nuxt
80 |
81 | # vuepress build output
82 | .vuepress/dist
83 |
84 | # Serverless directories
85 | .serverless/
86 |
87 | # FuseBox cache
88 | .fusebox/
89 |
90 | # DynamoDB Local files
91 | .dynamodb/
92 |
93 | # OS metadata
94 | .DS_Store
95 | Thumbs.db
96 |
97 | # Ignore built ts files
98 | tests/runner
99 | lib
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "printWidth": 100,
4 | "importOrder": ["^(?![@\\.]).*", "^@", "^\\.\\.", "^\\."],
5 | "importOrderSeparation": true
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2024 Romain Milon
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # 🔥🛠️ Foundry Gas Diff Reporter
6 |
7 | - Easily compare gas reports generated by Foundry automatically on each of your Pull Requests!
8 | - Check out the [Live example](https://github.com/morpho-dao/morpho-tokenized-vaults/pull/228#issuecomment-1352919862) to see how it looks!
9 |
10 | ## Getting started
11 |
12 | ### Automatically generate a gas report diff on every PR
13 |
14 | Add a workflow (`.github/workflows/foundry-gas-diff.yml`):
15 |
16 | ```yaml
17 | name: Report gas diff
18 |
19 | on:
20 | push:
21 | branches:
22 | - main
23 | pull_request:
24 | # Optionally configure to run only for changes in specific files. For example:
25 | # paths:
26 | # - src/**
27 | # - test/**
28 | # - foundry.toml
29 | # - remappings.txt
30 | # - .github/workflows/foundry-gas-diff.yml
31 |
32 | jobs:
33 | compare_gas_reports:
34 | runs-on: ubuntu-latest
35 | steps:
36 | - uses: actions/checkout@v4
37 | with:
38 | submodules: recursive
39 |
40 | - name: Install Foundry
41 | uses: onbjerg/foundry-toolchain@v1
42 | with:
43 | version: nightly
44 |
45 | # Add any step generating a gas report to a temporary file named gasreport.ansi. For example:
46 | - name: Run tests
47 | run: forge test --gas-report > gasreport.ansi # <- this file name should be unique in your repository!
48 | env:
49 | # make fuzzing semi-deterministic to avoid noisy gas cost estimation
50 | # due to non-deterministic fuzzing (but still use pseudo-random fuzzing seeds)
51 | FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }}
52 |
53 | - name: Compare gas reports
54 | uses: Rubilmax/foundry-gas-diff@v3
55 | with:
56 | summaryQuantile: 0.9 # only display the 10% most significant gas diffs in the summary (defaults to 20%)
57 | sortCriteria: avg,max # sort diff rows by criteria
58 | sortOrders: desc,asc # and directions
59 | ignore: test-foundry/**/* # filter out gas reports from specific paths (test/ is included by default)
60 | id: gas_diff
61 |
62 | - name: Add gas diff to sticky comment
63 | if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
64 | uses: marocchino/sticky-pull-request-comment@v2
65 | with:
66 | # delete the comment in case changes no longer impact gas costs
67 | delete: ${{ !steps.gas_diff.outputs.markdown }}
68 | message: ${{ steps.gas_diff.outputs.markdown }}
69 | ```
70 |
71 | > :information_source: **An error will appear at first run!**
72 | > 🔴 **Error:** No workflow run found with an artifact named "main.gasreport.ansi"
73 | > As the action is expecting a comparative file stored on the base branch and cannot find it (because the action never ran on the target branch and thus has never uploaded any gas report)
74 |
75 | ---
76 |
77 | ## How it works
78 |
79 | Everytime somebody opens a Pull Request, the action expects [Foundry](https://github.com/foundry-rs/foundry) `forge` to run a test suite, generating a gas report to a temporary file (named `gasreport.ansi` by default).
80 |
81 | Once generated, the action will fetch the comparative gas report stored as an artifact from previous runs; parse & compare them, storing the results in the action's outputs as shell and as markdown.
82 |
83 | You can then do whatever you want with the results!
84 |
85 | > **Our recommandation:** Automatically submit a sticky comment displaying the gas diff!
86 |
87 | ---
88 |
89 | ## Options
90 |
91 | ### `report` _{string}_
92 |
93 | This should correspond to the path of a file where the output of forge's gas report has been logged.
94 | Only necessary when generating multiple gas reports on the same repository.
95 |
96 | ⚠️ Make sure this file uniquely identifies a gas report, to avoid messing up with a gas report of another workflow on the same repository!
97 |
98 | _Defaults to: `gasreport.ansi`_
99 |
100 | ### `base` _{string}_
101 |
102 | The gas diff reference branch name, used to fetch the previous gas report to compare the freshly generated gas report to.
103 |
104 | _Defaults to: `${{ github.base_ref || github.ref_name }}`_
105 |
106 | ### `head` _{string}_
107 |
108 | The gas diff target branch name, used to upload the freshly generated gas report.
109 |
110 | _Defaults to: `${{ github.head_ref || github.ref_name }}`_
111 |
112 | ### `token` _{string}_
113 |
114 | The github token allowing the action to upload and download gas reports generated by foundry. You should not need to customize this, as the action already has access to the default Github Action token.
115 |
116 | _Defaults to: `${{ github.token }}`_
117 |
118 | ### `header` _{string}_
119 |
120 | The top section displayed in the markdown output. Can be used to identify multiple gas diffs in the same PR or add metadata/information to the markdown output.
121 |
122 | _Defaults to:_
123 |
124 | ```markdown
125 | # Changes to gas cost
126 | ```
127 |
128 | ### `summaryQuantile` _{number}_
129 |
130 | The quantile threshold to filter avg gas cost diffs to display in the summary top section.
131 |
132 | _Defaults to: `0.8`_
133 |
134 | ### `sortCriteria` _{string[]}_
135 |
136 | A list of criteria to sort diff rows by in the report table (can be `name | min | avg | median | max | calls`), separated by a comma.
137 | Must have the same length as sortOrders.
138 |
139 | _Defaults to: name_
140 |
141 | ### `sortOrders` _{string[]}_
142 |
143 | A list of directions to sort diff rows by in the report table (can be `asc | desc`), for each sort criterion, separated by a comma.
144 | Must have the same length as sortCriteria.
145 |
146 | _Defaults to: asc_
147 |
148 | ### `ignore` _{string[]}_
149 |
150 | The list of contract paths from which to ignore gas reports, separated by a comma.
151 | This allows to clean out gas diffs from dependency contracts impacted by a change (e.g. Proxies, ERC20, ...).
152 |
153 | _No default assigned: optional opt-in (Please note that node dependencies are always discarded from gas reports)_
154 |
155 | ### `match` _{string[]}_
156 |
157 | The list of contract paths of which only to keep gas reports, separated by a comma.
158 | This allows to only display gas diff of specific contracts.
159 |
160 | _No default assigned: optional opt-in_
161 |
162 | ## ⚠️ Known limitations
163 |
164 | > **Library gas reports**
165 | > Forge does not generate library gas reports. You need to wrap their usage in a contract calling the library to be able to compare gas costs of calling the library.
166 |
167 | > **Average gas cost estimation**
168 | > Average & median gas costs for each function is estimated based on the test suite, which means they are easily impacted by small changes in the tests. We recommend using a separate, specific test suite, rarily updated, designed to perform accurate gas estimations.
169 |
170 | > **Fuzzing impacts gas costs**
171 | > Fuzzing can lead differences in gas costs estimated each time a test suite is ran. We thus recommend setting a deterministic fuzzing seed via the `--fuzz-seed` argument.
172 |
173 | This repository is maintained independently from [Foundry](https://github.com/foundry-rs/foundry) and may not work as expected with all versions of `forge`.
174 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: Generate gas diff
2 | author: Rubilmax
3 | description: Easily compare gas reports generated by foundry!
4 | branding:
5 | icon: info
6 | color: purple
7 |
8 | inputs:
9 | token:
10 | description: The repository's github token.
11 | default: ${{ github.token }}
12 | required: false
13 | base:
14 | description: The gas diff reference branch name.
15 | default: ${{ github.base_ref || github.ref_name }}
16 | required: false
17 | head:
18 | description: The gas diff target branch name.
19 | default: ${{ github.head_ref || github.ref_name }}
20 | required: false
21 | report:
22 | description: Report freshly generated to compare to reference.
23 | default: gasreport.ansi
24 | required: false
25 | header:
26 | description: The top section displayed in the markdown output.
27 | default: |
28 | # Changes to gas cost
29 | required: false
30 | summaryQuantile:
31 | description: The quantile threshold to filter avg gas cost diffs to display in the summary top section.
32 | default: 0.8
33 | required: false
34 | sortCriteria:
35 | description: The list of criteria to order diff rows by in the report (name | min | avg | median | max | calls), separated by a comma. Must have the same length as sortOrders.
36 | required: false
37 | default: name
38 | sortOrders:
39 | description: The list of directions to order diff rows in the report, according to order criteria (asc | desc), separated by a comma. Must have the same length as sortCriteria.
40 | required: false
41 | default: asc
42 | ignore:
43 | description: The list of contract paths from which to ignore gas reports, separated by a comma.
44 | required: false
45 | match:
46 | description: The list of contract paths of which only to keep gas reports, separated by a comma.
47 | required: false
48 |
49 | outputs:
50 | shell:
51 | description: The gas diff between the base gas report and the freshly generated gas report, specifically formatted for shell display
52 | markdown:
53 | description: The gas diff between the base gas report and the freshly generated gas report, specifically formatted for markdown display
54 |
55 | runs:
56 | using: node20
57 | main: dist/index.js
58 |
--------------------------------------------------------------------------------
/dist/sourcemap-register.js:
--------------------------------------------------------------------------------
1 | (()=>{var e={650:e=>{var r=Object.prototype.toString;var n=typeof Buffer.alloc==="function"&&typeof Buffer.allocUnsafe==="function"&&typeof Buffer.from==="function";function isArrayBuffer(e){return r.call(e).slice(8,-1)==="ArrayBuffer"}function fromArrayBuffer(e,r,t){r>>>=0;var o=e.byteLength-r;if(o<0){throw new RangeError("'offset' is out of bounds")}if(t===undefined){t=o}else{t>>>=0;if(t>o){throw new RangeError("'length' is out of bounds")}}return n?Buffer.from(e.slice(r,r+t)):new Buffer(new Uint8Array(e.slice(r,r+t)))}function fromString(e,r){if(typeof r!=="string"||r===""){r="utf8"}if(!Buffer.isEncoding(r)){throw new TypeError('"encoding" must be a valid string encoding')}return n?Buffer.from(e,r):new Buffer(e,r)}function bufferFrom(e,r,t){if(typeof e==="number"){throw new TypeError('"value" argument must not be a number')}if(isArrayBuffer(e)){return fromArrayBuffer(e,r,t)}if(typeof e==="string"){return fromString(e,r)}return n?Buffer.from(e):new Buffer(e)}e.exports=bufferFrom},274:(e,r,n)=>{var t=n(339);var o=Object.prototype.hasOwnProperty;var i=typeof Map!=="undefined";function ArraySet(){this._array=[];this._set=i?new Map:Object.create(null)}ArraySet.fromArray=function ArraySet_fromArray(e,r){var n=new ArraySet;for(var t=0,o=e.length;t=0){return r}}else{var n=t.toSetString(e);if(o.call(this._set,n)){return this._set[n]}}throw new Error('"'+e+'" is not in the set.')};ArraySet.prototype.at=function ArraySet_at(e){if(e>=0&&e{var t=n(190);var o=5;var i=1<>1;return r?-n:n}r.encode=function base64VLQ_encode(e){var r="";var n;var i=toVLQSigned(e);do{n=i&a;i>>>=o;if(i>0){n|=u}r+=t.encode(n)}while(i>0);return r};r.decode=function base64VLQ_decode(e,r,n){var i=e.length;var s=0;var l=0;var c,p;do{if(r>=i){throw new Error("Expected more digits in base 64 VLQ value.")}p=t.decode(e.charCodeAt(r++));if(p===-1){throw new Error("Invalid base64 digit: "+e.charAt(r-1))}c=!!(p&u);p&=a;s=s+(p<{var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");r.encode=function(e){if(0<=e&&e{r.GREATEST_LOWER_BOUND=1;r.LEAST_UPPER_BOUND=2;function recursiveSearch(e,n,t,o,i,a){var u=Math.floor((n-e)/2)+e;var s=i(t,o[u],true);if(s===0){return u}else if(s>0){if(n-u>1){return recursiveSearch(u,n,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return n1){return recursiveSearch(e,u,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return u}else{return e<0?-1:e}}}r.search=function search(e,n,t,o){if(n.length===0){return-1}var i=recursiveSearch(-1,n.length,e,n,t,o||r.GREATEST_LOWER_BOUND);if(i<0){return-1}while(i-1>=0){if(t(n[i],n[i-1],true)!==0){break}--i}return i}},680:(e,r,n)=>{var t=n(339);function generatedPositionAfter(e,r){var n=e.generatedLine;var o=r.generatedLine;var i=e.generatedColumn;var a=r.generatedColumn;return o>n||o==n&&a>=i||t.compareByGeneratedPositionsInflated(e,r)<=0}function MappingList(){this._array=[];this._sorted=true;this._last={generatedLine:-1,generatedColumn:0}}MappingList.prototype.unsortedForEach=function MappingList_forEach(e,r){this._array.forEach(e,r)};MappingList.prototype.add=function MappingList_add(e){if(generatedPositionAfter(this._last,e)){this._last=e;this._array.push(e)}else{this._sorted=false;this._array.push(e)}};MappingList.prototype.toArray=function MappingList_toArray(){if(!this._sorted){this._array.sort(t.compareByGeneratedPositionsInflated);this._sorted=true}return this._array};r.H=MappingList},758:(e,r)=>{function swap(e,r,n){var t=e[r];e[r]=e[n];e[n]=t}function randomIntInRange(e,r){return Math.round(e+Math.random()*(r-e))}function doQuickSort(e,r,n,t){if(n{var t;var o=n(339);var i=n(345);var a=n(274).I;var u=n(449);var s=n(758).U;function SourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}return n.sections!=null?new IndexedSourceMapConsumer(n,r):new BasicSourceMapConsumer(n,r)}SourceMapConsumer.fromSourceMap=function(e,r){return BasicSourceMapConsumer.fromSourceMap(e,r)};SourceMapConsumer.prototype._version=3;SourceMapConsumer.prototype.__generatedMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_generatedMappings",{configurable:true,enumerable:true,get:function(){if(!this.__generatedMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__generatedMappings}});SourceMapConsumer.prototype.__originalMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_originalMappings",{configurable:true,enumerable:true,get:function(){if(!this.__originalMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__originalMappings}});SourceMapConsumer.prototype._charIsMappingSeparator=function SourceMapConsumer_charIsMappingSeparator(e,r){var n=e.charAt(r);return n===";"||n===","};SourceMapConsumer.prototype._parseMappings=function SourceMapConsumer_parseMappings(e,r){throw new Error("Subclasses must implement _parseMappings")};SourceMapConsumer.GENERATED_ORDER=1;SourceMapConsumer.ORIGINAL_ORDER=2;SourceMapConsumer.GREATEST_LOWER_BOUND=1;SourceMapConsumer.LEAST_UPPER_BOUND=2;SourceMapConsumer.prototype.eachMapping=function SourceMapConsumer_eachMapping(e,r,n){var t=r||null;var i=n||SourceMapConsumer.GENERATED_ORDER;var a;switch(i){case SourceMapConsumer.GENERATED_ORDER:a=this._generatedMappings;break;case SourceMapConsumer.ORIGINAL_ORDER:a=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;a.map((function(e){var r=e.source===null?null:this._sources.at(e.source);r=o.computeSourceURL(u,r,this._sourceMapURL);return{source:r,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name===null?null:this._names.at(e.name)}}),this).forEach(e,t)};SourceMapConsumer.prototype.allGeneratedPositionsFor=function SourceMapConsumer_allGeneratedPositionsFor(e){var r=o.getArg(e,"line");var n={source:o.getArg(e,"source"),originalLine:r,originalColumn:o.getArg(e,"column",0)};n.source=this._findSourceIndex(n.source);if(n.source<0){return[]}var t=[];var a=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,i.LEAST_UPPER_BOUND);if(a>=0){var u=this._originalMappings[a];if(e.column===undefined){var s=u.originalLine;while(u&&u.originalLine===s){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}else{var l=u.originalColumn;while(u&&u.originalLine===r&&u.originalColumn==l){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}}return t};r.SourceMapConsumer=SourceMapConsumer;function BasicSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sources");var u=o.getArg(n,"names",[]);var s=o.getArg(n,"sourceRoot",null);var l=o.getArg(n,"sourcesContent",null);var c=o.getArg(n,"mappings");var p=o.getArg(n,"file",null);if(t!=this._version){throw new Error("Unsupported version: "+t)}if(s){s=o.normalize(s)}i=i.map(String).map(o.normalize).map((function(e){return s&&o.isAbsolute(s)&&o.isAbsolute(e)?o.relative(s,e):e}));this._names=a.fromArray(u.map(String),true);this._sources=a.fromArray(i,true);this._absoluteSources=this._sources.toArray().map((function(e){return o.computeSourceURL(s,e,r)}));this.sourceRoot=s;this.sourcesContent=l;this._mappings=c;this._sourceMapURL=r;this.file=p}BasicSourceMapConsumer.prototype=Object.create(SourceMapConsumer.prototype);BasicSourceMapConsumer.prototype.consumer=SourceMapConsumer;BasicSourceMapConsumer.prototype._findSourceIndex=function(e){var r=e;if(this.sourceRoot!=null){r=o.relative(this.sourceRoot,r)}if(this._sources.has(r)){return this._sources.indexOf(r)}var n;for(n=0;n1){v.source=l+_[1];l+=_[1];v.originalLine=i+_[2];i=v.originalLine;v.originalLine+=1;v.originalColumn=a+_[3];a=v.originalColumn;if(_.length>4){v.name=c+_[4];c+=_[4]}}m.push(v);if(typeof v.originalLine==="number"){d.push(v)}}}s(m,o.compareByGeneratedPositionsDeflated);this.__generatedMappings=m;s(d,o.compareByOriginalPositions);this.__originalMappings=d};BasicSourceMapConsumer.prototype._findMapping=function SourceMapConsumer_findMapping(e,r,n,t,o,a){if(e[n]<=0){throw new TypeError("Line must be greater than or equal to 1, got "+e[n])}if(e[t]<0){throw new TypeError("Column must be greater than or equal to 0, got "+e[t])}return i.search(e,r,o,a)};BasicSourceMapConsumer.prototype.computeColumnSpans=function SourceMapConsumer_computeColumnSpans(){for(var e=0;e=0){var t=this._generatedMappings[n];if(t.generatedLine===r.generatedLine){var i=o.getArg(t,"source",null);if(i!==null){i=this._sources.at(i);i=o.computeSourceURL(this.sourceRoot,i,this._sourceMapURL)}var a=o.getArg(t,"name",null);if(a!==null){a=this._names.at(a)}return{source:i,line:o.getArg(t,"originalLine",null),column:o.getArg(t,"originalColumn",null),name:a}}}return{source:null,line:null,column:null,name:null}};BasicSourceMapConsumer.prototype.hasContentsOfAllSources=function BasicSourceMapConsumer_hasContentsOfAllSources(){if(!this.sourcesContent){return false}return this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some((function(e){return e==null}))};BasicSourceMapConsumer.prototype.sourceContentFor=function SourceMapConsumer_sourceContentFor(e,r){if(!this.sourcesContent){return null}var n=this._findSourceIndex(e);if(n>=0){return this.sourcesContent[n]}var t=e;if(this.sourceRoot!=null){t=o.relative(this.sourceRoot,t)}var i;if(this.sourceRoot!=null&&(i=o.urlParse(this.sourceRoot))){var a=t.replace(/^file:\/\//,"");if(i.scheme=="file"&&this._sources.has(a)){return this.sourcesContent[this._sources.indexOf(a)]}if((!i.path||i.path=="/")&&this._sources.has("/"+t)){return this.sourcesContent[this._sources.indexOf("/"+t)]}}if(r){return null}else{throw new Error('"'+t+'" is not in the SourceMap.')}};BasicSourceMapConsumer.prototype.generatedPositionFor=function SourceMapConsumer_generatedPositionFor(e){var r=o.getArg(e,"source");r=this._findSourceIndex(r);if(r<0){return{line:null,column:null,lastColumn:null}}var n={source:r,originalLine:o.getArg(e,"line"),originalColumn:o.getArg(e,"column")};var t=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,o.getArg(e,"bias",SourceMapConsumer.GREATEST_LOWER_BOUND));if(t>=0){var i=this._originalMappings[t];if(i.source===n.source){return{line:o.getArg(i,"generatedLine",null),column:o.getArg(i,"generatedColumn",null),lastColumn:o.getArg(i,"lastGeneratedColumn",null)}}}return{line:null,column:null,lastColumn:null}};t=BasicSourceMapConsumer;function IndexedSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sections");if(t!=this._version){throw new Error("Unsupported version: "+t)}this._sources=new a;this._names=new a;var u={line:-1,column:0};this._sections=i.map((function(e){if(e.url){throw new Error("Support for url field in sections not implemented.")}var n=o.getArg(e,"offset");var t=o.getArg(n,"line");var i=o.getArg(n,"column");if(t{var t=n(449);var o=n(339);var i=n(274).I;var a=n(680).H;function SourceMapGenerator(e){if(!e){e={}}this._file=o.getArg(e,"file",null);this._sourceRoot=o.getArg(e,"sourceRoot",null);this._skipValidation=o.getArg(e,"skipValidation",false);this._sources=new i;this._names=new i;this._mappings=new a;this._sourcesContents=null}SourceMapGenerator.prototype._version=3;SourceMapGenerator.fromSourceMap=function SourceMapGenerator_fromSourceMap(e){var r=e.sourceRoot;var n=new SourceMapGenerator({file:e.file,sourceRoot:r});e.eachMapping((function(e){var t={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){t.source=e.source;if(r!=null){t.source=o.relative(r,t.source)}t.original={line:e.originalLine,column:e.originalColumn};if(e.name!=null){t.name=e.name}}n.addMapping(t)}));e.sources.forEach((function(t){var i=t;if(r!==null){i=o.relative(r,t)}if(!n._sources.has(i)){n._sources.add(i)}var a=e.sourceContentFor(t);if(a!=null){n.setSourceContent(t,a)}}));return n};SourceMapGenerator.prototype.addMapping=function SourceMapGenerator_addMapping(e){var r=o.getArg(e,"generated");var n=o.getArg(e,"original",null);var t=o.getArg(e,"source",null);var i=o.getArg(e,"name",null);if(!this._skipValidation){this._validateMapping(r,n,t,i)}if(t!=null){t=String(t);if(!this._sources.has(t)){this._sources.add(t)}}if(i!=null){i=String(i);if(!this._names.has(i)){this._names.add(i)}}this._mappings.add({generatedLine:r.line,generatedColumn:r.column,originalLine:n!=null&&n.line,originalColumn:n!=null&&n.column,source:t,name:i})};SourceMapGenerator.prototype.setSourceContent=function SourceMapGenerator_setSourceContent(e,r){var n=e;if(this._sourceRoot!=null){n=o.relative(this._sourceRoot,n)}if(r!=null){if(!this._sourcesContents){this._sourcesContents=Object.create(null)}this._sourcesContents[o.toSetString(n)]=r}else if(this._sourcesContents){delete this._sourcesContents[o.toSetString(n)];if(Object.keys(this._sourcesContents).length===0){this._sourcesContents=null}}};SourceMapGenerator.prototype.applySourceMap=function SourceMapGenerator_applySourceMap(e,r,n){var t=r;if(r==null){if(e.file==null){throw new Error("SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, "+'or the source map\'s "file" property. Both were omitted.')}t=e.file}var a=this._sourceRoot;if(a!=null){t=o.relative(a,t)}var u=new i;var s=new i;this._mappings.unsortedForEach((function(r){if(r.source===t&&r.originalLine!=null){var i=e.originalPositionFor({line:r.originalLine,column:r.originalColumn});if(i.source!=null){r.source=i.source;if(n!=null){r.source=o.join(n,r.source)}if(a!=null){r.source=o.relative(a,r.source)}r.originalLine=i.line;r.originalColumn=i.column;if(i.name!=null){r.name=i.name}}}var l=r.source;if(l!=null&&!u.has(l)){u.add(l)}var c=r.name;if(c!=null&&!s.has(c)){s.add(c)}}),this);this._sources=u;this._names=s;e.sources.forEach((function(r){var t=e.sourceContentFor(r);if(t!=null){if(n!=null){r=o.join(n,r)}if(a!=null){r=o.relative(a,r)}this.setSourceContent(r,t)}}),this)};SourceMapGenerator.prototype._validateMapping=function SourceMapGenerator_validateMapping(e,r,n,t){if(r&&typeof r.line!=="number"&&typeof r.column!=="number"){throw new Error("original.line and original.column are not numbers -- you probably meant to omit "+"the original mapping entirely and only map the generated position. If so, pass "+"null for the original mapping instead of an object with empty or null values.")}if(e&&"line"in e&&"column"in e&&e.line>0&&e.column>=0&&!r&&!n&&!t){return}else if(e&&"line"in e&&"column"in e&&r&&"line"in r&&"column"in r&&e.line>0&&e.column>=0&&r.line>0&&r.column>=0&&n){return}else{throw new Error("Invalid mapping: "+JSON.stringify({generated:e,source:n,original:r,name:t}))}};SourceMapGenerator.prototype._serializeMappings=function SourceMapGenerator_serializeMappings(){var e=0;var r=1;var n=0;var i=0;var a=0;var u=0;var s="";var l;var c;var p;var f;var g=this._mappings.toArray();for(var h=0,d=g.length;h0){if(!o.compareByGeneratedPositionsInflated(c,g[h-1])){continue}l+=","}}l+=t.encode(c.generatedColumn-e);e=c.generatedColumn;if(c.source!=null){f=this._sources.indexOf(c.source);l+=t.encode(f-u);u=f;l+=t.encode(c.originalLine-1-i);i=c.originalLine-1;l+=t.encode(c.originalColumn-n);n=c.originalColumn;if(c.name!=null){p=this._names.indexOf(c.name);l+=t.encode(p-a);a=p}}s+=l}return s};SourceMapGenerator.prototype._generateSourcesContent=function SourceMapGenerator_generateSourcesContent(e,r){return e.map((function(e){if(!this._sourcesContents){return null}if(r!=null){e=o.relative(r,e)}var n=o.toSetString(e);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null}),this)};SourceMapGenerator.prototype.toJSON=function SourceMapGenerator_toJSON(){var e={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null){e.file=this._file}if(this._sourceRoot!=null){e.sourceRoot=this._sourceRoot}if(this._sourcesContents){e.sourcesContent=this._generateSourcesContent(e.sources,e.sourceRoot)}return e};SourceMapGenerator.prototype.toString=function SourceMapGenerator_toString(){return JSON.stringify(this.toJSON())};r.h=SourceMapGenerator},351:(e,r,n)=>{var t;var o=n(591).h;var i=n(339);var a=/(\r?\n)/;var u=10;var s="$$$isSourceNode$$$";function SourceNode(e,r,n,t,o){this.children=[];this.sourceContents={};this.line=e==null?null:e;this.column=r==null?null:r;this.source=n==null?null:n;this.name=o==null?null:o;this[s]=true;if(t!=null)this.add(t)}SourceNode.fromStringWithSourceMap=function SourceNode_fromStringWithSourceMap(e,r,n){var t=new SourceNode;var o=e.split(a);var u=0;var shiftNextLine=function(){var e=getNextLine();var r=getNextLine()||"";return e+r;function getNextLine(){return u=0;r--){this.prepend(e[r])}}else if(e[s]||typeof e==="string"){this.children.unshift(e)}else{throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e)}return this};SourceNode.prototype.walk=function SourceNode_walk(e){var r;for(var n=0,t=this.children.length;n0){r=[];for(n=0;n{function getArg(e,r,n){if(r in e){return e[r]}else if(arguments.length===3){return n}else{throw new Error('"'+r+'" is a required argument.')}}r.getArg=getArg;var n=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;var t=/^data:.+\,.+$/;function urlParse(e){var r=e.match(n);if(!r){return null}return{scheme:r[1],auth:r[2],host:r[3],port:r[4],path:r[5]}}r.urlParse=urlParse;function urlGenerate(e){var r="";if(e.scheme){r+=e.scheme+":"}r+="//";if(e.auth){r+=e.auth+"@"}if(e.host){r+=e.host}if(e.port){r+=":"+e.port}if(e.path){r+=e.path}return r}r.urlGenerate=urlGenerate;function normalize(e){var n=e;var t=urlParse(e);if(t){if(!t.path){return e}n=t.path}var o=r.isAbsolute(n);var i=n.split(/\/+/);for(var a,u=0,s=i.length-1;s>=0;s--){a=i[s];if(a==="."){i.splice(s,1)}else if(a===".."){u++}else if(u>0){if(a===""){i.splice(s+1,u);u=0}else{i.splice(s,2);u--}}}n=i.join("/");if(n===""){n=o?"/":"."}if(t){t.path=n;return urlGenerate(t)}return n}r.normalize=normalize;function join(e,r){if(e===""){e="."}if(r===""){r="."}var n=urlParse(r);var o=urlParse(e);if(o){e=o.path||"/"}if(n&&!n.scheme){if(o){n.scheme=o.scheme}return urlGenerate(n)}if(n||r.match(t)){return r}if(o&&!o.host&&!o.path){o.host=r;return urlGenerate(o)}var i=r.charAt(0)==="/"?r:normalize(e.replace(/\/+$/,"")+"/"+r);if(o){o.path=i;return urlGenerate(o)}return i}r.join=join;r.isAbsolute=function(e){return e.charAt(0)==="/"||n.test(e)};function relative(e,r){if(e===""){e="."}e=e.replace(/\/$/,"");var n=0;while(r.indexOf(e+"/")!==0){var t=e.lastIndexOf("/");if(t<0){return r}e=e.slice(0,t);if(e.match(/^([^\/]+:\/)?\/*$/)){return r}++n}return Array(n+1).join("../")+r.substr(e.length+1)}r.relative=relative;var o=function(){var e=Object.create(null);return!("__proto__"in e)}();function identity(e){return e}function toSetString(e){if(isProtoString(e)){return"$"+e}return e}r.toSetString=o?identity:toSetString;function fromSetString(e){if(isProtoString(e)){return e.slice(1)}return e}r.fromSetString=o?identity:fromSetString;function isProtoString(e){if(!e){return false}var r=e.length;if(r<9){return false}if(e.charCodeAt(r-1)!==95||e.charCodeAt(r-2)!==95||e.charCodeAt(r-3)!==111||e.charCodeAt(r-4)!==116||e.charCodeAt(r-5)!==111||e.charCodeAt(r-6)!==114||e.charCodeAt(r-7)!==112||e.charCodeAt(r-8)!==95||e.charCodeAt(r-9)!==95){return false}for(var n=r-10;n>=0;n--){if(e.charCodeAt(n)!==36){return false}}return true}function compareByOriginalPositions(e,r,n){var t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0||n){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0){return t}t=e.generatedLine-r.generatedLine;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByOriginalPositions=compareByOriginalPositions;function compareByGeneratedPositionsDeflated(e,r,n){var t=e.generatedLine-r.generatedLine;if(t!==0){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0||n){return t}t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsDeflated=compareByGeneratedPositionsDeflated;function strcmp(e,r){if(e===r){return 0}if(e===null){return 1}if(r===null){return-1}if(e>r){return 1}return-1}function compareByGeneratedPositionsInflated(e,r){var n=e.generatedLine-r.generatedLine;if(n!==0){return n}n=e.generatedColumn-r.generatedColumn;if(n!==0){return n}n=strcmp(e.source,r.source);if(n!==0){return n}n=e.originalLine-r.originalLine;if(n!==0){return n}n=e.originalColumn-r.originalColumn;if(n!==0){return n}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsInflated=compareByGeneratedPositionsInflated;function parseSourceMapInput(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}r.parseSourceMapInput=parseSourceMapInput;function computeSourceURL(e,r,n){r=r||"";if(e){if(e[e.length-1]!=="/"&&r[0]!=="/"){e+="/"}r=e+r}if(n){var t=urlParse(n);if(!t){throw new Error("sourceMapURL could not be parsed")}if(t.path){var o=t.path.lastIndexOf("/");if(o>=0){t.path=t.path.substring(0,o+1)}}r=join(urlGenerate(t),r)}return normalize(r)}r.computeSourceURL=computeSourceURL},997:(e,r,n)=>{n(591).h;r.SourceMapConsumer=n(952).SourceMapConsumer;n(351)},284:(e,r,n)=>{e=n.nmd(e);var t=n(997).SourceMapConsumer;var o=n(17);var i;try{i=n(147);if(!i.existsSync||!i.readFileSync){i=null}}catch(e){}var a=n(650);function dynamicRequire(e,r){return e.require(r)}var u=false;var s=false;var l=false;var c="auto";var p={};var f={};var g=/^data:application\/json[^,]+base64,/;var h=[];var d=[];function isInBrowser(){if(c==="browser")return true;if(c==="node")return false;return typeof window!=="undefined"&&typeof XMLHttpRequest==="function"&&!(window.require&&window.module&&window.process&&window.process.type==="renderer")}function hasGlobalProcessEventEmitter(){return typeof process==="object"&&process!==null&&typeof process.on==="function"}function globalProcessVersion(){if(typeof process==="object"&&process!==null){return process.version}else{return""}}function globalProcessStderr(){if(typeof process==="object"&&process!==null){return process.stderr}}function globalProcessExit(e){if(typeof process==="object"&&process!==null&&typeof process.exit==="function"){return process.exit(e)}}function handlerExec(e){return function(r){for(var n=0;n"}var n=this.getLineNumber();if(n!=null){r+=":"+n;var t=this.getColumnNumber();if(t){r+=":"+t}}}var o="";var i=this.getFunctionName();var a=true;var u=this.isConstructor();var s=!(this.isToplevel()||u);if(s){var l=this.getTypeName();if(l==="[object Object]"){l="null"}var c=this.getMethodName();if(i){if(l&&i.indexOf(l)!=0){o+=l+"."}o+=i;if(c&&i.indexOf("."+c)!=i.length-c.length-1){o+=" [as "+c+"]"}}else{o+=l+"."+(c||"")}}else if(u){o+="new "+(i||"")}else if(i){o+=i}else{o+=r;a=false}if(a){o+=" ("+r+")"}return o}function cloneCallSite(e){var r={};Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach((function(n){r[n]=/^(?:is|get)/.test(n)?function(){return e[n].call(e)}:e[n]}));r.toString=CallSiteToString;return r}function wrapCallSite(e,r){if(r===undefined){r={nextPosition:null,curPosition:null}}if(e.isNative()){r.curPosition=null;return e}var n=e.getFileName()||e.getScriptNameOrSourceURL();if(n){var t=e.getLineNumber();var o=e.getColumnNumber()-1;var i=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;var a=i.test(globalProcessVersion())?0:62;if(t===1&&o>a&&!isInBrowser()&&!e.isEval()){o-=a}var u=mapSourcePosition({source:n,line:t,column:o});r.curPosition=u;e=cloneCallSite(e);var s=e.getFunctionName;e.getFunctionName=function(){if(r.nextPosition==null){return s()}return r.nextPosition.name||s()};e.getFileName=function(){return u.source};e.getLineNumber=function(){return u.line};e.getColumnNumber=function(){return u.column+1};e.getScriptNameOrSourceURL=function(){return u.source};return e}var l=e.isEval()&&e.getEvalOrigin();if(l){l=mapEvalOrigin(l);e=cloneCallSite(e);e.getEvalOrigin=function(){return l};return e}return e}function prepareStackTrace(e,r){if(l){p={};f={}}var n=e.name||"Error";var t=e.message||"";var o=n+": "+t;var i={nextPosition:null,curPosition:null};var a=[];for(var u=r.length-1;u>=0;u--){a.push("\n at "+wrapCallSite(r[u],i));i.nextPosition=i.curPosition}i.curPosition=i.nextPosition=null;return o+a.reverse().join("")}function getErrorSource(e){var r=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(e.stack);if(r){var n=r[1];var t=+r[2];var o=+r[3];var a=p[n];if(!a&&i&&i.existsSync(n)){try{a=i.readFileSync(n,"utf8")}catch(e){a=""}}if(a){var u=a.split(/(?:\r\n|\r|\n)/)[t-1];if(u){return n+":"+t+"\n"+u+"\n"+new Array(o).join(" ")+"^"}}}return null}function printErrorAndExit(e){var r=getErrorSource(e);var n=globalProcessStderr();if(n&&n._handle&&n._handle.setBlocking){n._handle.setBlocking(true)}if(r){console.error();console.error(r)}console.error(e.stack);globalProcessExit(1)}function shimEmitUncaughtException(){var e=process.emit;process.emit=function(r){if(r==="uncaughtException"){var n=arguments[1]&&arguments[1].stack;var t=this.listeners(r).length>0;if(n&&!t){return printErrorAndExit(arguments[1])}}return e.apply(this,arguments)}}var S=h.slice(0);var _=d.slice(0);r.wrapCallSite=wrapCallSite;r.getErrorSource=getErrorSource;r.mapSourcePosition=mapSourcePosition;r.retrieveSourceMap=v;r.install=function(r){r=r||{};if(r.environment){c=r.environment;if(["node","browser","auto"].indexOf(c)===-1){throw new Error("environment "+c+" was unknown. Available options are {auto, browser, node}")}}if(r.retrieveFile){if(r.overrideRetrieveFile){h.length=0}h.unshift(r.retrieveFile)}if(r.retrieveSourceMap){if(r.overrideRetrieveSourceMap){d.length=0}d.unshift(r.retrieveSourceMap)}if(r.hookRequire&&!isInBrowser()){var n=dynamicRequire(e,"module");var t=n.prototype._compile;if(!t.__sourceMapSupport){n.prototype._compile=function(e,r){p[r]=e;f[r]=undefined;return t.call(this,e,r)};n.prototype._compile.__sourceMapSupport=true}}if(!l){l="emptyCacheBetweenOperations"in r?r.emptyCacheBetweenOperations:false}if(!u){u=true;Error.prepareStackTrace=prepareStackTrace}if(!s){var o="handleUncaughtExceptions"in r?r.handleUncaughtExceptions:true;try{var i=dynamicRequire(e,"worker_threads");if(i.isMainThread===false){o=false}}catch(e){}if(o&&hasGlobalProcessEventEmitter()){s=true;shimEmitUncaughtException()}}};r.resetRetrieveHandlers=function(){h.length=0;d.length=0;h=S.slice(0);d=_.slice(0);v=handlerExec(d);m=handlerExec(h)}},147:e=>{"use strict";e.exports=require("fs")},17:e=>{"use strict";e.exports=require("path")}};var r={};function __webpack_require__(n){var t=r[n];if(t!==undefined){return t.exports}var o=r[n]={id:n,loaded:false,exports:{}};var i=true;try{e[n](o,o.exports,__webpack_require__);i=false}finally{if(i)delete r[n]}o.loaded=true;return o.exports}(()=>{__webpack_require__.nmd=e=>{e.paths=[];if(!e.children)e.children=[];return e}})();if(typeof __webpack_require__!=="undefined")__webpack_require__.ab=__dirname+"/";var n={};(()=>{__webpack_require__(284).install()})();module.exports=n})();
--------------------------------------------------------------------------------
/funding.json:
--------------------------------------------------------------------------------
1 | {
2 | "opRetro": {
3 | "projectId": "0xb024f6abfa79f7eb7af334f56015509bb70355709991d31bc689c7bea3250f7e"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "foundry-gas-diff",
3 | "version": "3.18.0",
4 | "description": "Github Action reporting gas diff from Foundry gas reports",
5 | "author": {
6 | "name": "Romain (Rubilmax) Milon",
7 | "email": "rmilon@gmail.com",
8 | "url": "https://github.com/rubilmax"
9 | },
10 | "license": "MIT",
11 | "main": "lib/index.js",
12 | "packageManager": "yarn@1.22.22",
13 | "scripts": {
14 | "build": "rm -rf lib && tsc --build tsconfig.build.json",
15 | "format": "prettier --write '**/*.ts'",
16 | "format:check": "prettier --check '**/*.ts'",
17 | "package": " rm -rf dist && ncc build --source-map --no-cache --license licenses.txt",
18 | "release": "yarn build && yarn package",
19 | "test": "jest"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/Rubilmax/foundry-gas-diff.git"
24 | },
25 | "bugs": {
26 | "url": "https://github.com/Rubilmax/foundry-gas-diff/issues"
27 | },
28 | "homepage": "https://github.com/Rubilmax/foundry-gas-diff#readme",
29 | "keywords": [
30 | "foundry",
31 | "forge",
32 | "gas",
33 | "report",
34 | "hardhat"
35 | ],
36 | "dependencies": {
37 | "@actions/artifact": "^2.2.0",
38 | "@actions/core": "^1.11.1",
39 | "@actions/exec": "^1.1.1",
40 | "@actions/github": "^5.1.1",
41 | "@octokit/core": "^6.1.3",
42 | "adm-zip": "^0.5.16",
43 | "lodash": "^4.17.21",
44 | "minimatch": "^10.0.1"
45 | },
46 | "devDependencies": {
47 | "@trivago/prettier-plugin-sort-imports": "^4.3.0",
48 | "@types/adm-zip": "^0.5.5",
49 | "@types/jest": "^29.5.13",
50 | "@types/lodash": "^4.17.12",
51 | "@types/minimatch": "^5.1.2",
52 | "@types/node": "^22.7.8",
53 | "@vercel/ncc": "^0.36.1",
54 | "colors": "^1.4.0",
55 | "jest": "^29.7.0",
56 | "prettier": "^2.8.8",
57 | "ts-jest": "^29.2.5",
58 | "typescript": "^5.6.3"
59 | },
60 | "jest": {
61 | "clearMocks": true,
62 | "moduleFileExtensions": [
63 | "ts",
64 | "js"
65 | ],
66 | "testMatch": [
67 | "**/*.test.ts"
68 | ],
69 | "transform": {
70 | "^.+\\.ts$": "ts-jest"
71 | },
72 | "verbose": true
73 | }
74 | }
--------------------------------------------------------------------------------
/src/format.ts:
--------------------------------------------------------------------------------
1 | import colors from "colors";
2 | import _sortBy from "lodash/sortBy";
3 |
4 | import { DiffCell, DiffReport } from "./types";
5 |
6 | export enum TextAlign {
7 | LEFT = "left",
8 | RIGHT = "right",
9 | CENTER = "center",
10 | }
11 |
12 | const center = (text: string, length: number) =>
13 | text.padStart((text.length + length) / 2).padEnd(length);
14 |
15 | export const formatShellCell = (cell: DiffCell, length = 10) => {
16 | const format = colors[cell.delta > 0 ? "red" : cell.delta < 0 ? "green" : "reset"];
17 |
18 | return [
19 | cell.current.toLocaleString().padStart(length) +
20 | " " +
21 | format(("(" + (plusSign(cell.delta) + cell.delta.toLocaleString()) + ")").padEnd(length)),
22 | colors.bold(
23 | format(
24 | (
25 | plusSign(cell.prcnt) +
26 | (cell.prcnt === Infinity ? "∞" : cell.prcnt.toFixed(2)) +
27 | "%"
28 | ).padStart(9)
29 | )
30 | ),
31 | ];
32 | };
33 |
34 | export const formatShellDiff = (diffs: DiffReport[], summaryQuantile = 0.8) => {
35 | const maxContractLength = Math.max(8, ...diffs.map(({ name }) => name.length));
36 | const maxMethodLength = Math.max(
37 | 7,
38 | ...diffs.flatMap(({ methods }) => methods.map(({ name }) => name.length))
39 | );
40 |
41 | const SHELL_SUMMARY_COLS = [
42 | { txt: "", length: 0 },
43 | { txt: "Contract", length: maxContractLength },
44 | { txt: "Method", length: maxMethodLength },
45 | { txt: "Avg (+/-)", length: 33 },
46 | { txt: "", length: 0 },
47 | ];
48 |
49 | const SHELL_DIFF_COLS = [
50 | { txt: "", length: 0 },
51 | { txt: "Contract", length: maxContractLength },
52 | { txt: "Deployment Cost (+/-)", length: 33 },
53 | { txt: "Method", length: maxMethodLength },
54 | { txt: "Min (+/-)", length: 33 },
55 | { txt: "Avg (+/-)", length: 33 },
56 | { txt: "Median (+/-)", length: 33 },
57 | { txt: "Max (+/-)", length: 33 },
58 | { txt: "# Calls (+/-)", length: 13 },
59 | { txt: "", length: 0 },
60 | ];
61 |
62 | const summaryHeader = SHELL_SUMMARY_COLS.map((entry) =>
63 | colors.bold(center(entry.txt, entry.length || 0))
64 | )
65 | .join(" | ")
66 | .trim();
67 | const summarySeparator = SHELL_SUMMARY_COLS.map(({ length }) =>
68 | length > 0 ? "-".repeat(length + 2) : ""
69 | )
70 | .join("|")
71 | .trim();
72 |
73 | const diffHeader = SHELL_DIFF_COLS.map((entry) =>
74 | colors.bold(center(entry.txt, entry.length || 0))
75 | )
76 | .join(" | ")
77 | .trim();
78 | const diffSeparator = SHELL_DIFF_COLS.map(({ length }) =>
79 | length > 0 ? "-".repeat(length + 2) : ""
80 | )
81 | .join("|")
82 | .trim();
83 |
84 | const sortedMethods = _sortBy(
85 | diffs.flatMap((diff) => diff.methods),
86 | (method) => Math.abs(method.avg.prcnt)
87 | );
88 | const avgQuantile = Math.abs(
89 | sortedMethods[Math.floor((sortedMethods.length - 1) * summaryQuantile)]?.avg.prcnt ?? 0
90 | );
91 |
92 | return (
93 | colors.underline(
94 | colors.bold(
95 | colors.yellow(
96 | `🧾 Summary (${Math.round((1 - summaryQuantile) * 100)}% most significant diffs)\n\n`
97 | )
98 | )
99 | ) +
100 | [
101 | "",
102 | summaryHeader,
103 | ...diffs
104 | .map(({ methods, ...diff }) => ({
105 | ...diff,
106 | methods: methods.filter(
107 | (method) =>
108 | method.min.current >= 500 &&
109 | Math.abs(method.avg.prcnt) >= avgQuantile &&
110 | (method.min.delta !== 0 || method.median.delta !== 0 || method.max.delta !== 0)
111 | ),
112 | }))
113 | .filter((diff) => diff.methods.length > 0)
114 | .flatMap((diff) =>
115 | diff.methods
116 | .map((method, methodIndex) =>
117 | [
118 | "",
119 | colors.bold(
120 | colors.grey((methodIndex === 0 ? diff.name : "").padEnd(maxContractLength))
121 | ),
122 | colors.italic(method.name.padEnd(maxMethodLength)),
123 | ...formatShellCell(method.avg),
124 | "",
125 | ]
126 | .join(" | ")
127 | .trim()
128 | )
129 | .join("\n")
130 | .trim()
131 | ),
132 | "",
133 | ]
134 | .join(`\n${summarySeparator}\n`)
135 | .trim() +
136 | colors.underline(colors.bold(colors.yellow("\n\nFull diff report 👇\n\n"))) +
137 | [
138 | "",
139 | diffHeader,
140 | ...diffs.map((diff) =>
141 | diff.methods
142 | .map((method, methodIndex) =>
143 | [
144 | "",
145 | colors.bold(
146 | colors.grey((methodIndex === 0 ? diff.name : "").padEnd(maxContractLength))
147 | ),
148 | ...(methodIndex === 0 ? formatShellCell(diff.deploymentCost) : ["".padEnd(33)]),
149 | colors.italic(method.name.padEnd(maxMethodLength)),
150 | ...formatShellCell(method.min),
151 | ...formatShellCell(method.avg),
152 | ...formatShellCell(method.median),
153 | ...formatShellCell(method.max),
154 | formatShellCell(method.calls, 6)[0],
155 | "",
156 | ]
157 | .join(" | ")
158 | .trim()
159 | )
160 | .join("\n")
161 | .trim()
162 | ),
163 | "",
164 | ]
165 | .join(`\n${diffSeparator}\n`)
166 | .trim()
167 | );
168 | };
169 |
170 | const plusSign = (num: number) => (num > 0 ? "+" : "");
171 |
172 | const alignPattern = (align = TextAlign.LEFT) => {
173 | switch (align) {
174 | case TextAlign.LEFT:
175 | return ":-";
176 | case TextAlign.RIGHT:
177 | return "-:";
178 | case TextAlign.CENTER:
179 | return ":-:";
180 | }
181 | };
182 |
183 | const formatMarkdownSummaryCell = (rows: DiffCell[]) => [
184 | rows
185 | .map(
186 | (row) =>
187 | plusSign(row.delta) +
188 | row.delta.toLocaleString() +
189 | " " +
190 | (row.delta > 0 ? "❌" : row.delta < 0 ? "✅" : "➖")
191 | )
192 | .join("
"),
193 | rows
194 | .map(
195 | (row) =>
196 | "**" + plusSign(row.prcnt) + (row.prcnt === Infinity ? "∞" : row.prcnt.toFixed(2)) + "%**"
197 | )
198 | .join("
"),
199 | ];
200 |
201 | const formatMarkdownFullCell = (rows: DiffCell[]) => [
202 | rows
203 | .map(
204 | (row) =>
205 | row.current.toLocaleString() +
206 | " (" +
207 | plusSign(row.delta) +
208 | row.delta.toLocaleString() +
209 | ")"
210 | )
211 | .join("
"),
212 | rows
213 | .map(
214 | (row) =>
215 | "**" + plusSign(row.prcnt) + (row.prcnt === Infinity ? "∞" : row.prcnt.toFixed(2)) + "%**"
216 | )
217 | .join("
"),
218 | ];
219 |
220 | const MARKDOWN_SUMMARY_COLS = [
221 | { txt: "" },
222 | { txt: "Contract", align: TextAlign.LEFT },
223 | { txt: "Method", align: TextAlign.LEFT },
224 | { txt: "Avg (+/-)", align: TextAlign.RIGHT },
225 | { txt: "%", align: TextAlign.RIGHT },
226 | { txt: "" },
227 | ];
228 |
229 | const MARKDOWN_DIFF_COLS = [
230 | { txt: "" },
231 | { txt: "Contract", align: TextAlign.LEFT },
232 | { txt: "Deployment Cost (+/-)", align: TextAlign.RIGHT },
233 | { txt: "Method", align: TextAlign.LEFT },
234 | { txt: "Min (+/-)", align: TextAlign.RIGHT },
235 | { txt: "%", align: TextAlign.RIGHT },
236 | { txt: "Avg (+/-)", align: TextAlign.RIGHT },
237 | { txt: "%", align: TextAlign.RIGHT },
238 | { txt: "Median (+/-)", align: TextAlign.RIGHT },
239 | { txt: "%", align: TextAlign.RIGHT },
240 | { txt: "Max (+/-)", align: TextAlign.RIGHT },
241 | { txt: "%", align: TextAlign.RIGHT },
242 | { txt: "# Calls (+/-)", align: TextAlign.RIGHT },
243 | { txt: "" },
244 | ];
245 |
246 | export const formatMarkdownDiff = (
247 | header: string,
248 | diffs: DiffReport[],
249 | repository: string,
250 | commitHash: string,
251 | refCommitHash?: string,
252 | summaryQuantile = 0.8
253 | ) => {
254 | const diffReport = [
255 | header,
256 | "",
257 | `> Generated at commit: [${commitHash}](/${repository}/commit/${commitHash})` +
258 | (refCommitHash
259 | ? `, compared to commit: [${refCommitHash}](/${repository}/commit/${refCommitHash})`
260 | : ""),
261 | ];
262 | if (diffs.length === 0)
263 | return diffReport.concat(["", "### There are no changes in gas cost"]).join("\n").trim();
264 |
265 | const summaryHeader = MARKDOWN_SUMMARY_COLS.map((entry) => entry.txt)
266 | .join(" | ")
267 | .trim();
268 | const summaryHeaderSeparator = MARKDOWN_SUMMARY_COLS.map((entry) =>
269 | entry.txt ? alignPattern(entry.align) : ""
270 | )
271 | .join("|")
272 | .trim();
273 |
274 | const diffHeader = MARKDOWN_DIFF_COLS.map((entry) => entry.txt)
275 | .join(" | ")
276 | .trim();
277 | const diffHeaderSeparator = MARKDOWN_DIFF_COLS.map((entry) =>
278 | entry.txt ? alignPattern(entry.align) : ""
279 | )
280 | .join("|")
281 | .trim();
282 |
283 | const sortedMethods = _sortBy(
284 | diffs.flatMap((diff) => diff.methods),
285 | (method) => Math.abs(method.avg.prcnt)
286 | );
287 | const avgQuantile = Math.abs(
288 | sortedMethods[Math.floor((sortedMethods.length - 1) * summaryQuantile)]?.avg.prcnt ?? 0
289 | );
290 |
291 | return diffReport
292 | .concat([
293 | "",
294 | `### 🧾 Summary (${Math.round((1 - summaryQuantile) * 100)}% most significant diffs)`,
295 | "",
296 | summaryHeader,
297 | summaryHeaderSeparator,
298 | diffs
299 | .map(({ methods, ...diff }) => ({
300 | ...diff,
301 | methods: methods.filter(
302 | (method) =>
303 | method.min.current >= 500 &&
304 | Math.abs(method.avg.prcnt) >= avgQuantile &&
305 | (method.min.delta !== 0 || method.median.delta !== 0 || method.max.delta !== 0)
306 | ),
307 | }))
308 | .filter((diff) => diff.methods.length > 0)
309 | .flatMap((diff) =>
310 | [
311 | "",
312 | `**${diff.name}**`,
313 | diff.methods.map((method) => `_${method.name}_`).join("
"),
314 | ...formatMarkdownSummaryCell(diff.methods.map((method) => method.avg)),
315 | "",
316 | ]
317 | .join(" | ")
318 | .trim()
319 | )
320 | .join("\n"),
321 | "---",
322 | "",
323 | "",
324 | "Full diff report 👇
",
325 | "
",
326 | "",
327 | diffHeader,
328 | diffHeaderSeparator,
329 | diffs
330 | .flatMap((diff) =>
331 | [
332 | "",
333 | `**${diff.name}**`,
334 | formatMarkdownFullCell([diff.deploymentCost])[0],
335 | diff.methods.map((method) => `_${method.name}_`).join("
"),
336 | ...formatMarkdownFullCell(diff.methods.map((method) => method.min)),
337 | ...formatMarkdownFullCell(diff.methods.map((method) => method.avg)),
338 | ...formatMarkdownFullCell(diff.methods.map((method) => method.median)),
339 | ...formatMarkdownFullCell(diff.methods.map((method) => method.max)),
340 | formatMarkdownFullCell(diff.methods.map((method) => method.calls))[0],
341 | "",
342 | ]
343 | .join(" | ")
344 | .trim()
345 | )
346 | .join("\n"),
347 | " ",
348 | "",
349 | ])
350 | .join("\n")
351 | .trim();
352 | };
353 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Zip from "adm-zip";
2 | import * as fs from "fs";
3 | import { dirname, resolve } from "path";
4 |
5 | import artifactClient from "@actions/artifact";
6 | import * as core from "@actions/core";
7 | import { context, getOctokit } from "@actions/github";
8 |
9 | import { formatMarkdownDiff, formatShellDiff } from "./format";
10 | import { loadReports, computeDiffs } from "./report";
11 | import { isSortCriteriaValid, isSortOrdersValid } from "./types";
12 |
13 | const token = process.env.GITHUB_TOKEN || core.getInput("token");
14 | const report = core.getInput("report");
15 | const ignore = core.getInput("ignore").split(",");
16 | const match = (core.getInput("match") || undefined)?.split(",");
17 | const header = core.getInput("header");
18 | const summaryQuantile = parseFloat(core.getInput("summaryQuantile"));
19 | const sortCriteria = core.getInput("sortCriteria").split(",");
20 | const sortOrders = core.getInput("sortOrders").split(",");
21 | const baseBranch = core.getInput("base");
22 | const headBranch = core.getInput("head");
23 |
24 | const baseBranchEscaped = baseBranch.replace(/[/\\]/g, "-");
25 | const baseReport = `${baseBranchEscaped}.${report}`;
26 |
27 | const octokit = getOctokit(token);
28 | const localReportPath = resolve(report);
29 |
30 | const { owner, repo } = context.repo;
31 | const repository = owner + "/" + repo;
32 |
33 | let srcContent: string;
34 | let refCommitHash: string | undefined;
35 |
36 | async function run() {
37 | if (!isSortCriteriaValid(sortCriteria)) return;
38 | if (!isSortOrdersValid(sortOrders)) return;
39 |
40 | try {
41 | const headBranchEscaped = headBranch.replace(/[/\\]/g, "-");
42 | const outReport = `${headBranchEscaped}.${report}`;
43 |
44 | core.startGroup(`Upload new report from "${localReportPath}" as artifact named "${outReport}"`);
45 | const uploadResponse = await artifactClient.uploadArtifact(
46 | outReport,
47 | [localReportPath],
48 | dirname(localReportPath)
49 | );
50 |
51 | core.info(`Artifact has been successfully uploaded with id: "${uploadResponse.id}"`);
52 | } catch (error: any) {
53 | return core.setFailed(error.message);
54 | }
55 | core.endGroup();
56 |
57 | // cannot use artifactClient because downloads are limited to uploads in the same workflow run
58 | // cf. https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts#downloading-or-deleting-artifacts
59 | let artifactId: number | null = null;
60 | if (context.eventName === "pull_request") {
61 | try {
62 | core.startGroup(
63 | `Searching artifact "${baseReport}" on repository "${repository}", on branch "${baseBranch}"`
64 | );
65 |
66 | // Artifacts are returned in most recent first order.
67 | for await (const res of octokit.paginate.iterator(octokit.rest.actions.listArtifactsForRepo, {
68 | owner,
69 | repo,
70 | })) {
71 | const artifact = res.data.find(
72 | (artifact) => !artifact.expired && artifact.name === baseReport
73 | );
74 |
75 | if (!artifact) {
76 | await new Promise((resolve) => setTimeout(resolve, 900)); // avoid reaching the API rate limit
77 |
78 | continue;
79 | }
80 |
81 | artifactId = artifact.id;
82 | refCommitHash = artifact.workflow_run?.head_sha;
83 | core.info(
84 | `Found artifact named "${baseReport}" with ID "${artifactId}" from commit "${refCommitHash}"`
85 | );
86 | break;
87 | }
88 | core.endGroup();
89 |
90 | if (artifactId) {
91 | core.startGroup(
92 | `Downloading artifact "${baseReport}" of repository "${repository}" with ID "${artifactId}"`
93 | );
94 | const res = await octokit.rest.actions.downloadArtifact({
95 | owner,
96 | repo,
97 | artifact_id: artifactId,
98 | archive_format: "zip",
99 | });
100 |
101 | // @ts-ignore data is unknown
102 | const zip = new Zip(Buffer.from(res.data));
103 | for (const entry of zip.getEntries()) {
104 | core.info(`Loading gas reports from "${entry.entryName}"`);
105 | srcContent = zip.readAsText(entry);
106 | }
107 | core.endGroup();
108 | } else core.error(`No workflow run found with an artifact named "${baseReport}"`);
109 | } catch (error: any) {
110 | return core.setFailed(error.message);
111 | }
112 | }
113 |
114 | try {
115 | core.startGroup("Load gas reports");
116 | core.info(`Loading gas reports from "${localReportPath}"`);
117 | const compareContent = fs.readFileSync(localReportPath, "utf8");
118 | srcContent ??= compareContent; // if no source gas reports were loaded, defaults to the current gas reports
119 |
120 | const loadOptions = { ignorePatterns: ignore, matchPatterns: match };
121 | core.info(`Mapping reference gas reports`);
122 | const sourceReports = loadReports(srcContent, loadOptions);
123 | core.info(`Mapping compared gas reports`);
124 | const compareReports = loadReports(compareContent, loadOptions);
125 | core.endGroup();
126 |
127 | core.startGroup("Compute gas diff");
128 | const diffRows = computeDiffs(sourceReports, compareReports, sortCriteria, sortOrders);
129 | core.info(`Format markdown of ${diffRows.length} diffs`);
130 | const markdown = formatMarkdownDiff(
131 | header,
132 | diffRows,
133 | repository,
134 | context.sha,
135 | refCommitHash,
136 | summaryQuantile
137 | );
138 | core.info(`Format shell of ${diffRows.length} diffs`);
139 | const shell = formatShellDiff(diffRows, summaryQuantile);
140 | core.endGroup();
141 |
142 | console.log(shell);
143 |
144 | if (diffRows.length > 0) {
145 | core.setOutput("shell", shell);
146 | core.setOutput("markdown", markdown);
147 | }
148 | } catch (error: any) {
149 | core.setFailed(error.message);
150 | }
151 | }
152 |
153 | run();
154 |
--------------------------------------------------------------------------------
/src/report.ts:
--------------------------------------------------------------------------------
1 | import _orderBy from "lodash/orderBy";
2 | import { Minimatch } from "minimatch";
3 |
4 | import { DiffReport, GasReport, SortCriterion, SortOrder } from "./types";
5 |
6 | const reportHeaderRegex = /^\| .+:.+ contract +\|/gi;
7 | const reportSeparatorRegex = /^(?:[\|╭╰+][-=]+|\|[\s|]+$)/;
8 |
9 | export const variation = (current: number, previous: number) => {
10 | const delta = current - previous;
11 |
12 | return {
13 | previous,
14 | current,
15 | delta,
16 | prcnt: previous !== 0 ? (100 * delta) / previous : Infinity,
17 | };
18 | };
19 |
20 | export const loadReports = (
21 | content: string,
22 | {
23 | ignorePatterns,
24 | matchPatterns,
25 | }: {
26 | ignorePatterns?: string[];
27 | matchPatterns?: string[];
28 | }
29 | ): GasReport => {
30 | const ignoreMinimatchs = (ignorePatterns ?? [])
31 | .concat(["node_modules/**/*"])
32 | .map((pattern) => new Minimatch(pattern));
33 | const matchMinimatchs = matchPatterns?.map((pattern) => new Minimatch(pattern));
34 |
35 | const lines = content.split("\n");
36 |
37 | const reportHeaderIndexes = lines
38 | .map((line, index) => ({ isHeader: reportHeaderRegex.test(line), index }))
39 | .filter(({ isHeader }) => isHeader)
40 | .map(({ index }) => index);
41 |
42 | return Object.fromEntries(
43 | reportHeaderIndexes
44 | .map((reportHeaderIndex) =>
45 | lines.slice(
46 | reportHeaderIndex,
47 | reportHeaderIndex + lines.slice(reportHeaderIndex).findIndex((line) => line.trim() === "")
48 | )
49 | )
50 | .map((reportLines) => {
51 | const filteredReportLines = reportLines.filter((line) => !reportSeparatorRegex.test(line));
52 | const [filePath, name] = filteredReportLines[0].split("|")[1].trim().split(":");
53 |
54 | return {
55 | name: name.replace(/ contract/gi, ""),
56 | filePath,
57 | reportLines: filteredReportLines.slice(2),
58 | };
59 | })
60 | .filter(
61 | matchMinimatchs
62 | ? ({ filePath }) => matchMinimatchs.some((minimatch) => minimatch.match(filePath))
63 | : ({ filePath }) => !ignoreMinimatchs.some((minimatch) => minimatch.match(filePath))
64 | )
65 | .map(({ name, filePath, reportLines }) => {
66 | const [deploymentCost, deploymentSize] = reportLines[0].match(/\d+/g) || [];
67 | if (!deploymentCost || !deploymentSize)
68 | throw Error("No depoyment cost or deployment size found. Is this a forge gas report?");
69 |
70 | return {
71 | name,
72 | filePath,
73 | deploymentCost: parseFloat(deploymentCost),
74 | deploymentSize: parseFloat(deploymentSize),
75 | methods: Object.fromEntries(
76 | reportLines
77 | .slice(2)
78 | .map((line) => {
79 | const [method, min, avg, median, max, calls] = line.split("|").slice(1);
80 |
81 | return {
82 | name: method.trim(),
83 | min: parseFloat(min),
84 | avg: parseFloat(avg),
85 | median: parseFloat(median),
86 | max: parseFloat(max),
87 | calls: parseFloat(calls),
88 | };
89 | })
90 | .map((methodReport) => [methodReport.name, methodReport])
91 | ),
92 | };
93 | })
94 | .map((report) => [report.name, report])
95 | );
96 | };
97 |
98 | export const computeDiffs = (
99 | sourceReports: GasReport,
100 | compareReports: GasReport,
101 | sortCriteria: SortCriterion[],
102 | sortOrders: SortOrder[]
103 | ): DiffReport[] => {
104 | const sourceReportNames = Object.keys(sourceReports);
105 | const commonReportNames = Object.keys(compareReports).filter((name) =>
106 | sourceReportNames.includes(name)
107 | );
108 |
109 | return commonReportNames
110 | .map((reportName) => {
111 | const srcReport = sourceReports[reportName];
112 | const cmpReport = compareReports[reportName];
113 |
114 | return {
115 | ...srcReport,
116 | deploymentCost: variation(cmpReport.deploymentCost, srcReport.deploymentCost),
117 | deploymentSize: variation(cmpReport.deploymentSize, srcReport.deploymentSize),
118 | methods: _orderBy(
119 | Object.values(srcReport.methods)
120 | .filter(
121 | (methodReport) =>
122 | cmpReport.methods[methodReport.name] && srcReport.methods[methodReport.name]
123 | )
124 | .map((methodReport) => ({
125 | ...methodReport,
126 | min: variation(
127 | cmpReport.methods[methodReport.name].min,
128 | srcReport.methods[methodReport.name].min
129 | ),
130 | avg: variation(
131 | cmpReport.methods[methodReport.name].avg,
132 | srcReport.methods[methodReport.name].avg
133 | ),
134 | median: variation(
135 | cmpReport.methods[methodReport.name].median,
136 | srcReport.methods[methodReport.name].median
137 | ),
138 | max: variation(
139 | cmpReport.methods[methodReport.name].max,
140 | srcReport.methods[methodReport.name].max
141 | ),
142 | calls: variation(
143 | cmpReport.methods[methodReport.name].calls,
144 | srcReport.methods[methodReport.name].calls
145 | ),
146 | }))
147 | .filter(
148 | (row) =>
149 | row.min.delta !== 0 ||
150 | row.avg.delta !== 0 ||
151 | row.median.delta !== 0 ||
152 | row.max.delta !== 0
153 | ),
154 | sortCriteria,
155 | sortOrders
156 | ),
157 | };
158 | })
159 | .filter(
160 | (diff) => diff.methods.length > 0 || diff.deploymentCost.delta !== 0
161 | // || diff.deploymentSize.delta !== 0 not displayed yet
162 | )
163 | .sort(
164 | (diff1, diff2) =>
165 | Math.max(...diff2.methods.map((method) => Math.abs(method.avg.prcnt))) -
166 | Math.max(...diff1.methods.map((method) => Math.abs(method.avg.prcnt)))
167 | );
168 | };
169 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import * as core from "@actions/core";
2 |
3 | export interface MethodReport {
4 | name: string;
5 | min: number;
6 | avg: number;
7 | median: number;
8 | max: number;
9 | calls: number;
10 | }
11 |
12 | export interface ContractReport {
13 | name: string;
14 | filePath: string;
15 | deploymentCost: number;
16 | deploymentSize: number;
17 | methods: {
18 | [name: string]: MethodReport;
19 | };
20 | }
21 |
22 | export interface GasReport {
23 | [name: string]: ContractReport;
24 | }
25 |
26 | export interface DiffMethod {
27 | name: string;
28 | min: DiffCell;
29 | avg: DiffCell;
30 | median: DiffCell;
31 | max: DiffCell;
32 | calls: DiffCell;
33 | }
34 |
35 | export interface DiffReport {
36 | name: string;
37 | filePath: string;
38 | deploymentCost: DiffCell;
39 | deploymentSize: DiffCell;
40 | methods: DiffMethod[];
41 | }
42 |
43 | export interface DiffCell {
44 | previous: number;
45 | current: number;
46 | delta: number;
47 | prcnt: number;
48 | }
49 |
50 | export type SortCriterion = keyof DiffMethod;
51 | export type SortOrder = "asc" | "desc";
52 |
53 | const validSortCriteria = ["name", "min", "avg", "median", "max", "calls"] as SortCriterion[];
54 | const validSortOrders = ["asc", "desc"] as SortOrder[];
55 |
56 | export const isSortCriteriaValid = (sortCriteria: string[]): sortCriteria is SortCriterion[] => {
57 | const invalidSortCriterion = sortCriteria.find(
58 | (criterion) => !validSortCriteria.includes(criterion as SortCriterion)
59 | );
60 | if (invalidSortCriterion) core.setFailed(`Invalid sort criterion "${invalidSortCriterion}"`);
61 |
62 | return !invalidSortCriterion;
63 | };
64 |
65 | export const isSortOrdersValid = (sortOrders: string[]): sortOrders is SortOrder[] => {
66 | const invalidSortOrder = sortOrders.find(
67 | (order) => !validSortOrders.includes(order as SortOrder)
68 | );
69 | if (invalidSortOrder) core.setFailed(`Invalid sort order "${invalidSortOrder}"`);
70 |
71 | return !invalidSortOrder;
72 | };
73 |
--------------------------------------------------------------------------------
/tests/mocks/1-1.md:
--------------------------------------------------------------------------------
1 | # Changes to gas cost
2 |
3 | > Generated at commit: [d62d23148ca73df77cd4378ee1b3c17f1f303dbf](/Rubilmax/foundry-gas-diff/commit/d62d23148ca73df77cd4378ee1b3c17f1f303dbf)
4 |
5 | ### There are no changes in gas cost
--------------------------------------------------------------------------------
/tests/mocks/1-2.md:
--------------------------------------------------------------------------------
1 | # Changes to gas cost
2 |
3 | > Generated at commit: [d62d23148ca73df77cd4378ee1b3c17f1f303dbf](/Rubilmax/foundry-gas-diff/commit/d62d23148ca73df77cd4378ee1b3c17f1f303dbf)
4 |
5 | ### 🧾 Summary (20% most significant diffs)
6 |
7 | | Contract | Method | Avg (+/-) | % |
8 | |:-|:-|-:|-:|
9 | | **Morpho** | _borrow(address,uint256)_
_claimToTreasury_
_defaultMaxGasForMatching_
_liquidate_
_marketStatus_
_repay(address,uint256)_
_setDefaultMaxGasForMatching_
_setP2PIndexCursor_
_supply(address,address,uint256,uint256)_
_updateP2PIndexes_ | -43,939 ✅
+3,963 ❌
-67 ✅
-69,325 ✅
+622 ❌
-22,934 ✅
-872 ✅
+25,350 ❌
+402,561 ❌
-2,300 ✅ | **-7.91%**
**+46.86%**
**-10.55%**
**-21.50%**
**+53.99%**
**-8.94%**
**-10.08%**
**+512.43%**
**+2898.00%**
**-9.16%** |
10 | | **Lens** | _getCurrentCompBorrowIndex_
_getCurrentCompSupplyIndex_
_getCurrentP2PBorrowIndex_
_getIndexes_
_getUserMaxCapacitiesForAsset_ | -2,022 ✅
-1,989 ✅
-29,179 ✅
-4,769 ✅
-7,961 ✅ | **-15.48%**
**-16.86%**
**-31.35%**
**-9.78%**
**-7.77%** |
11 | | **PositionsManager** | _liquidateLogic_
_repayLogic_ | -82,027 ✅
-25,060 ✅ | **-24.90%**
**-10.26%** |
12 | | **InterestRatesManager** | _updateP2PIndexes_ | -3,004 ✅ | **-11.05%** |
13 | ---
14 |
15 |
16 | Full diff report 👇
17 |
18 |
19 | | Contract | Deployment Cost (+/-) | Method | Min (+/-) | % | Avg (+/-) | % | Median (+/-) | % | Max (+/-) | % | # Calls (+/-) |
20 | |:-|-:|:-|-:|-:|-:|-:|-:|-:|-:|-:|-:|
21 | | **Morpho** | 3,381,129 (+113,744) | _borrow(address,uint256)_
_borrow(address,uint256,uint256)_
_borrowBalanceInOf_
_cEth_
_claimRewards_
_claimToTreasury_
_createMarket_
_defaultMaxGasForMatching_
_deltas_
_enteredMarkets_
_getEnteredMarkets_
_getNext_
_initialize_
_interestRatesManager_
_isClaimRewardsPaused_
_lastPoolIndexes_
_liquidate_
_marketParameters_
_marketStatus_
_maxSortedUsers_
_p2pBorrowIndex_
_p2pSupplyIndex_
_positionsManager_
_repay(address,address,uint256)_
_repay(address,uint256)_
_rewardsManager_
_setDefaultMaxGasForMatching_
_setIncentivesVault_
_setInterestRatesManager_
_setMaxSortedUsers_
_setP2PIndexCursor_
_setPositionsManager_
_setReserveFactor_
_setRewardsManager_
_setTreasuryVault_
_supply(address,address,uint256)_
_supply(address,address,uint256,uint256)_
_supply(address,uint256)_
_supplyBalanceInOf_
_treasuryVault_
_updateP2PIndexes_
_wEth_
_withdraw(address,uint256)_
_withdraw(address,uint256,address)_ | 13,703 (+9,785)
11,078 (-382,423)
894 (0)
405 (0)
5,871 (0)
1,194 (+22)
2,736 (+22)
568 (-67)
1,050 (0)
616 (+22)
1,256 (+22)
1,057 (+22)
3,765 (-45)
471 (+22)
443 (+44)
742 (0)
11,771 (+7,706)
669 (0)
774 (+22)
373 (+22)
593 (+22)
591 (+22)
493 (+22)
8,542 (-517)
13,873 (+9,949)
471 (+22)
1,113 (-66)
2,695 (0)
2,673 (+44)
621 (+44)
2,717 (-2,230)
2,697 (+67)
2,717 (-67)
2,631 (0)
2,629 (0)
469,051 (-4,937)
11,502 (-2,389)
13,923 (+10,006)
980 (+44)
493 (+22)
1,766 (-45)
2,426 (+22)
5,649 (+1,772)
327,424 (-8,284) | **+249.74%**
**-97.18%**
**0.00%**
**0.00%**
**0.00%**
**+1.88%**
**+0.81%**
**-10.55%**
**0.00%**
**+3.70%**
**+1.78%**
**+2.13%**
**-1.18%**
**+4.90%**
**+11.03%**
**0.00%**
**+189.57%**
**0.00%**
**+2.93%**
**+6.27%**
**+3.85%**
**+3.87%**
**+4.67%**
**-5.71%**
**+253.54%**
**+4.90%**
**-5.60%**
**0.00%**
**+1.67%**
**+7.63%**
**-45.08%**
**+2.55%**
**-2.41%**
**0.00%**
**0.00%**
**-1.04%**
**-17.20%**
**+255.45%**
**+4.70%**
**+4.67%**
**-2.48%**
**+0.92%**
**+45.71%**
**-2.47%** | 511,848 (-43,939)
453,367 (+4,279)
1,410 (-14)
655 (-10)
162,715 (+28)
12,420 (+3,963)
259,951 (-802)
568 (-67)
2,171 (-77)
834 (+22)
1,514 (+25)
1,127 (+22)
247,044 (+84)
476 (+23)
443 (+44)
914 (-5)
253,108 (-69,325)
1,079 (-21)
1,774 (+622)
373 (+22)
705 (+18)
710 (+18)
493 (+22)
57,644 (-2,020)
233,500 (-22,934)
676 (+18)
7,779 (-872)
23,750 (+19)
5,732 (+62)
4,598 (+49)
30,297 (+25,350)
5,756 (+85)
42,724 (-1,062)
21,643 (+25)
23,553 (+39)
469,051 (-4,937)
416,452 (+402,561)
348,636 (-22,356)
1,181 (+35)
1,293 (+22)
22,820 (-2,300)
2,426 (+22)
249,208 (-15,968)
327,424 (-8,284) | **-7.91%**
**+0.95%**
**-0.98%**
**-1.50%**
**+0.02%**
**+46.86%**
**-0.31%**
**-10.55%**
**-3.43%**
**+2.71%**
**+1.68%**
**+1.99%**
**+0.03%**
**+5.08%**
**+11.03%**
**-0.54%**
**-21.50%**
**-1.91%**
**+53.99%**
**+6.27%**
**+2.62%**
**+2.60%**
**+4.67%**
**-3.39%**
**-8.94%**
**+2.74%**
**-10.08%**
**+0.08%**
**+1.09%**
**+1.08%**
**+512.43%**
**+1.50%**
**-2.43%**
**+0.12%**
**+0.17%**
**-1.04%**
**+2898.00%**
**-6.03%**
**+3.05%**
**+1.73%**
**-9.16%**
**+0.92%**
**-6.02%**
**-2.47%** | 398,171 (-236,518)
430,755 (-8,480)
894 (0)
405 (0)
177,285 (+22)
7,913 (+2,917)
254,459 (-930)
568 (-67)
1,050 (0)
889 (+22)
1,530 (+22)
1,130 (+22)
247,904 (-45)
471 (+22)
443 (+44)
742 (0)
379,725 (-8,991)
669 (0)
1,774 (+1,022)
373 (+22)
593 (+22)
591 (+22)
493 (+22)
11,007 (-1,471)
148,456 (-17,582)
471 (+22)
8,238 (-66)
23,878 (0)
5,732 (+62)
4,598 (+49)
4,881 (-66)
5,756 (+85)
2,717 (-67)
21,814 (0)
23,812 (0)
469,051 (-4,937)
540,732 (+526,841)
345,216 (-49,628)
980 (+44)
493 (+22)
21,822 (-2,657)
2,426 (+22)
182,413 (-2,833)
327,424 (-8,284) | **-37.27%**
**-1.93%**
**0.00%**
**0.00%**
**+0.01%**
**+58.39%**
**-0.36%**
**-10.55%**
**0.00%**
**+2.54%**
**+1.46%**
**+1.99%**
**-0.02%**
**+4.90%**
**+11.03%**
**0.00%**
**-2.31%**
**0.00%**
**+135.90%**
**+6.27%**
**+3.85%**
**+3.87%**
**+4.67%**
**-11.79%**
**-10.59%**
**+4.90%**
**-0.79%**
**0.00%**
**+1.09%**
**+1.08%**
**-1.33%**
**+1.50%**
**-2.41%**
**0.00%**
**0.00%**
**-1.04%**
**+3792.68%**
**-12.57%**
**+4.70%**
**+4.67%**
**-10.85%**
**+0.92%**
**-1.53%**
**-2.47%** | 1,084,878 (-14,126)
926,748 (+244,864)
4,894 (0)
2,405 (0)
314,724 (+66)
23,501 (-35)
305,124 (-2,270)
568 (-67)
9,050 (0)
889 (+22)
2,911 (+22)
1,193 (+22)
247,904 (-45)
2,471 (+22)
443 (+44)
2,742 (0)
546,086 (-10,768)
2,669 (0)
2,774 (+22)
373 (+22)
2,593 (+22)
2,591 (+22)
493 (+22)
200,020 (-4,621)
2,293,327 (-8,768)
2,471 (+22)
10,238 (-66)
23,878 (0)
8,791 (+79)
8,576 (+55)
108,709 (+103,762)
8,815 (+102)
108,692 (-2,679)
21,814 (0)
23,812 (0)
469,051 (-4,937)
697,123 (+683,232)
2,095,559 (-5,545)
4,980 (+44)
2,493 (+22)
51,876 (-2,657)
2,426 (+22)
1,930,391 (-13,111)
327,424 (-8,284) | **-1.29%**
**+35.91%**
**0.00%**
**0.00%**
**+0.02%**
**-0.15%**
**-0.74%**
**-10.55%**
**0.00%**
**+2.54%**
**+0.76%**
**+1.88%**
**-0.02%**
**+0.90%**
**+11.03%**
**0.00%**
**-1.93%**
**0.00%**
**+0.80%**
**+6.27%**
**+0.86%**
**+0.86%**
**+4.67%**
**-2.26%**
**-0.38%**
**+0.90%**
**-0.64%**
**0.00%**
**+0.91%**
**+0.65%**
**+2097.47%**
**+1.17%**
**-2.41%**
**0.00%**
**0.00%**
**-1.04%**
**+4918.52%**
**-0.26%**
**+0.89%**
**+0.89%**
**-4.87%**
**+0.92%**
**-0.67%**
**-2.47%** | 331 (+74)
43 (+3)
519 (-1)
24 (+1)
17 (0)
5 (-5)
1,717 (+223)
4 (+3)
214 (+7)
5 (0)
87 (-1)
36 (0)
284 (+37)
2,546 (+401)
1 (0)
347 (-2)
32 (+4)
156 (+3)
4 (-6)
1 (0)
482 (+2)
453 (0)
1 (0)
4 (0)
69 (+5)
2,829 (+438)
39 (+10)
285 (+37)
2 (0)
4 (0)
4 (+3)
2 (0)
21 (0)
286 (+37)
287 (+37)
1 (0)
3 (+2)
673 (+165)
476 (0)
5 (0)
1,313 (+3)
1 (0)
93 (+8)
1 (0) |
22 | | **Lens** | 5,038,336 (+162,625) | _computeLiquidationRepayAmount_
_getAverageBorrowRatePerBlock_
_getAverageSupplyRatePerBlock_
_getCurrentBorrowBalanceInOf_
_getCurrentCompBorrowIndex_
_getCurrentCompSupplyIndex_
_getCurrentP2PBorrowIndex_
_getCurrentP2PSupplyIndex_
_getCurrentSupplyBalanceInOf_
_getCurrentUserBorrowRatePerBlock_
_getCurrentUserSupplyRatePerBlock_
_getEnteredMarkets_
_getIndexes_
_getMainMarketData_
_getMarketConfiguration_
_getNextUserBorrowRatePerBlock_
_getNextUserSupplyRatePerBlock_
_getRatesPerBlock_
_getTotalBorrow_
_getTotalMarketBorrow_
_getTotalMarketSupply_
_getTotalSupply_
_getUserBalanceStates_
_getUserHealthFactor_
_getUserLiquidityDataForAsset_
_getUserMaxCapacitiesForAsset_
_getUserUnclaimedRewards_
_initialize_
_isLiquidatable_ | 56,582 (-729)
45,579 (-1,293)
40,161 (+55)
13,719 (+33)
9,005 (-4,055)
7,809 (-3,989)
35,121 (-57,951)
11,002 (+11)
13,792 (+77)
54,976 (-1,943)
56,438 (-1,943)
6,942 (+22)
15,791 (-894)
90,113 (-2,142)
49,352 (-91)
61,611 (-1,950)
61,634 (-1,906)
38,277 (-1,016)
224,694 (-5,038)
22,924 (-1,249)
21,263 (-891)
338,240 (-4,603)
53,745 (-707)
45,883 (+77)
24,787 (+66)
11,926 (+44)
20,058 (+33)
2,776 (0)
53,725 (-762) | **-1.27%**
**-2.76%**
**+0.14%**
**+0.24%**
**-31.05%**
**-33.81%**
**-62.26%**
**+0.10%**
**+0.56%**
**-3.41%**
**-3.33%**
**+0.32%**
**-5.36%**
**-2.32%**
**-0.18%**
**-3.07%**
**-3.00%**
**-2.59%**
**-2.19%**
**-5.17%**
**-4.02%**
**-1.34%**
**-1.30%**
**+0.17%**
**+0.27%**
**+0.37%**
**+0.16%**
**0.00%**
**-1.40%** | 107,868 (-766)
63,276 (-1,086)
61,078 (-652)
20,763 (-106)
11,038 (-2,022)
9,809 (-1,989)
63,893 (-29,179)
51,850 (-198)
21,092 (-496)
68,345 (-1,527)
68,703 (-1,527)
6,942 (+22)
44,014 (-4,769)
90,113 (-2,142)
50,122 (-102)
87,213 (-1,524)
87,093 (-1,505)
42,021 (-1,014)
224,694 (-5,038)
25,531 (-1,882)
24,107 (-1,659)
338,240 (-4,603)
65,796 (-886)
58,997 (-666)
59,939 (-435)
94,500 (-7,961)
44,422 (-167)
71,274 (+58)
58,632 (-548) | **-0.71%**
**-1.69%**
**-1.06%**
**-0.51%**
**-15.48%**
**-16.86%**
**-31.35%**
**-0.38%**
**-2.30%**
**-2.19%**
**-2.17%**
**+0.32%**
**-9.78%**
**-2.32%**
**-0.20%**
**-1.72%**
**-1.70%**
**-2.36%**
**-2.19%**
**-6.87%**
**-6.44%**
**-1.34%**
**-1.33%**
**-1.12%**
**-0.72%**
**-7.77%**
**-0.37%**
**+0.08%**
**-0.93%** | 95,073 (-1,579)
47,579 (-1,293)
45,937 (-1,002)
13,719 (+33)
11,038 (-2,022)
9,809 (-1,989)
63,893 (-29,179)
51,850 (-198)
17,836 (-861)
56,976 (-1,943)
57,068 (-1,943)
6,942 (+22)
37,682 (+1,099)
90,113 (-2,142)
50,122 (-102)
75,810 (-1,434)
75,807 (-1,390)
38,277 (-1,016)
224,694 (-5,038)
25,531 (-1,882)
24,107 (-1,659)
338,240 (-4,603)
57,745 (-707)
58,187 (-773)
39,287 (+66)
85,085 (-5,690)
41,887 (+33)
71,517 (+22)
54,553 (+66) | **-1.63%**
**-2.65%**
**-2.13%**
**+0.24%**
**-15.48%**
**-16.86%**
**-31.35%**
**-0.38%**
**-4.61%**
**-3.30%**
**-3.29%**
**+0.32%**
**+3.00%**
**-2.32%**
**-0.20%**
**-1.86%**
**-1.80%**
**-2.59%**
**-2.19%**
**-6.87%**
**-6.44%**
**-1.34%**
**-1.21%**
**-1.31%**
**+0.17%**
**-6.27%**
**+0.08%**
**+0.03%**
**+0.12%** | 168,711 (-1,137)
125,931 (-576)
124,748 (-482)
34,852 (-385)
13,071 (+11)
11,809 (+11)
92,665 (-407)
92,699 (-407)
34,905 (-341)
99,989 (-385)
100,061 (-385)
6,942 (+22)
92,709 (-374)
90,113 (-2,142)
50,892 (-113)
158,653 (-1,390)
158,646 (-1,390)
99,277 (-1,016)
224,694 (-5,038)
28,138 (-2,516)
26,952 (-2,427)
338,240 (-4,603)
109,136 (-1,469)
75,210 (-253)
136,952 (-352)
165,329 (-198)
101,399 (-2,050)
71,517 (+22)
93,044 (-541) | **-0.67%**
**-0.46%**
**-0.38%**
**-1.09%**
**+0.08%**
**+0.09%**
**-0.44%**
**-0.44%**
**-0.97%**
**-0.38%**
**-0.38%**
**+0.32%**
**-0.40%**
**-2.32%**
**-0.22%**
**-0.87%**
**-0.87%**
**-1.01%**
**-2.19%**
**-8.21%**
**-8.26%**
**-1.34%**
**-1.33%**
**-0.34%**
**-0.26%**
**-0.12%**
**-1.98%**
**+0.03%**
**-0.58%** | 19 (-1)
6 (0)
6 (0)
3 (0)
2 (+1)
2 (+1)
2 (+1)
2 (0)
4 (0)
5 (0)
5 (0)
1 (0)
5 (+2)
1 (0)
2 (0)
9 (0)
9 (0)
21 (0)
2 (0)
4 (0)
4 (0)
2 (0)
9 (0)
10 (0)
10 (0)
23 (+1)
10 (0)
284 (+37)
24 (-1) |
23 | | **PositionsManager** | 4,592,720 (+291,600) | _borrowLogic_
_liquidateLogic_
_repayLogic_
_supplyLogic_
_withdrawLogic_ | 556 (-147,533)
1,046 (-1,855)
679 (0)
759 (+22)
657 (0) | **-99.62%**
**-63.94%**
**0.00%**
**+2.99%**
**0.00%** | 503,082 (-39,042)
247,375 (-82,027)
219,166 (-25,060)
344,282 (-21,036)
244,934 (-20,058) | **-7.20%**
**-24.90%**
**-10.26%**
**-5.76%**
**-7.57%** | 426,548 (-15,648)
376,224 (-8,359)
141,582 (-19,981)
342,120 (-53,095)
178,893 (-2,526) | **-3.54%**
**-2.17%**
**-12.37%**
**-13.43%**
**-1.39%** | 1,081,377 (-13,795)
542,584 (-10,136)
2,289,787 (-8,443)
2,092,034 (-5,214)
1,926,871 (-12,804) | **-1.26%**
**-1.83%**
**-0.37%**
**-0.25%**
**-0.66%** | 374 (+79)
32 (+5)
73 (+6)
677 (+169)
94 (+9) |
24 | | **InterestRatesManager** | 845,047 (+38,239) | _updateP2PIndexes_ | 618 (0) | **0.00%** | 24,182 (-3,004) | **-11.05%** | 20,674 (-2,612) | **-11.22%** | 102,527 (-320) | **-0.31%** | 2,551 (+237) |
25 | | **RewardsManager** | 972,578 (0) | _accrueUserBorrowUnclaimedRewards_
_accrueUserSupplyUnclaimedRewards_
_claimRewards_
_compBorrowerIndex_
_compSupplierIndex_
_getLocalCompBorrowState_
_getLocalCompSupplyState_
_initialize_ | 2,107 (0)
2,129 (0)
20,828 (+88)
708 (0)
731 (0)
826 (0)
849 (0)
2,753 (0) | **0.00%**
**0.00%**
**+0.42%**
**0.00%**
**0.00%**
**0.00%**
**0.00%**
**0.00%** | 23,043 (-185)
28,068 (+27)
68,539 (-36)
1,088 (-64)
912 (-29)
1,270 (-89)
1,071 (-44)
68,914 (+35) | **-0.80%**
**+0.10%**
**-0.05%**
**-5.56%**
**-3.08%**
**-6.55%**
**-3.95%**
**+0.05%** | 23,802 (0)
25,824 (0)
67,447 (+71)
708 (0)
731 (0)
826 (0)
849 (0)
69,148 (0) | **0.00%**
**0.00%**
**+0.11%**
**0.00%**
**0.00%**
**0.00%**
**0.00%**
**0.00%** | 56,277 (0)
57,058 (0)
182,933 (-2,066)
2,708 (0)
2,731 (0)
2,826 (0)
2,849 (0)
69,148 (0) | **0.00%**
**0.00%**
**-1.12%**
**0.00%**
**0.00%**
**0.00%**
**0.00%**
**0.00%** | 669 (+93)
959 (+174)
24 (0)
21 (+3)
22 (+3)
18 (+3)
18 (+3)
284 (+37) |
26 | | **IncentivesVault** | 623,575 (-17,019) | _tradeCompForMorphoTokens_ | 443 (0) | **0.00%** | 33,621 (-51) | **-0.15%** | 36,308 (-78) | **-0.21%** | 65,565 (-86) | **-0.13%** | 6 (0) |
27 |
--------------------------------------------------------------------------------
/tests/mocks/gas_report.2.ansi:
--------------------------------------------------------------------------------
1 | Compiling 95 files with 0.8.13
2 | Solc 0.8.13 finished in 40.41s
3 | Compiler run successful
4 |
5 | Running 6 tests for test-foundry/compound/TestInterestRates.t.sol:TestInterestRates
6 | [PASS] testIndexComputation() (gas: 23197)
7 | [PASS] testIndexComputationWhenPoolSupplyIndexHasJumpedWithDelta() (gas: 23374)
8 | [PASS] testIndexComputationWhenPoolSupplyIndexHasJumpedWithoutDelta() (gas: 19079)
9 | [PASS] testIndexComputationWithDelta() (gas: 27405)
10 | [PASS] testIndexComputationWithDeltaAndReserveFactor() (gas: 27514)
11 | [PASS] testIndexComputationWithReserveFactor() (gas: 23241)
12 | Test result: ok. 6 passed; 0 failed; finished in 1.18s
13 |
14 | Running 1 test for test-foundry/compound/TestMorphoUtils.t.sol:TestMorphoUtils
15 | [PASS] testUserShouldNotUpdateP2PIndexesOfMarketNotCreated() (gas: 21091)
16 | Test result: ok. 1 passed; 0 failed; finished in 31.83s
17 |
18 | Running 12 tests for test-foundry/compound/TestUpgradeable.t.sol:TestUpgradeable
19 | [PASS] testLensImplementationsShouldBeInitialized() (gas: 15184)
20 | [PASS] testOnlyProxyOwnerCanUpgradeAndCallLens() (gas: 4949299)
21 | [PASS] testOnlyProxyOwnerCanUpgradeAndCallMorpho() (gas: 3337874)
22 | [PASS] testOnlyProxyOwnerCanUpgradeAndCallRewardsManager() (gas: 1041352)
23 | [PASS] testOnlyProxyOwnerCanUpgradeLens() (gas: 4946393)
24 | [PASS] testOnlyProxyOwnerCanUpgradeMorpho() (gas: 3335994)
25 | [PASS] testOnlyProxyOwnerCanUpgradeRewardsManager() (gas: 1038531)
26 | [PASS] testPositionsManagerImplementationsShouldBeInitialized() (gas: 21578)
27 | [PASS] testRewardsManagerImplementationsShouldBeInitialized() (gas: 15181)
28 | [PASS] testUpgradeLens() (gas: 4945107)
29 | [PASS] testUpgradeMorpho() (gas: 3848175)
30 | [PASS] testUpgradeRewardsManager() (gas: 1037158)
31 | Test result: ok. 12 passed; 0 failed; finished in 33.04s
32 |
33 | Running 4 tests for test-foundry/compound/TestMarketMember.t.sol:TestMarketMember
34 | [PASS] testShouldNotLiquidateUserNotOnMemberOfMarketAsBorrow() (gas: 1281816)
35 | [PASS] testShouldNotLiquidateUserNotOnMemberOfMarketAsCollateral() (gas: 1282029)
36 | [PASS] testShouldNotRepayWhenNotMarketMember() (gas: 69917)
37 | [PASS] testShouldNotWithdrawWhenNotMarketMember() (gas: 41425)
38 | Test result: ok. 4 passed; 0 failed; finished in 34.35s
39 |
40 | Running 16 tests for test-foundry/compound/TestGovernance.t.sol:TestGovernance
41 | [PASS] testOnlyOwnerCanCreateMarkets() (gas: 538212)
42 | [PASS] testOnlyOwnerCanSetClaimRewardsStatus() (gas: 56030)
43 | [PASS] testOnlyOwnerCanSetMaxSortedUsers() (gas: 46630)
44 | [PASS] testOnlyOwnerCanSetReserveFactor() (gas: 340275)
45 | [PASS] testOnlyOwnerShouldFlipMarketStrategy() (gas: 89931)
46 | [PASS] testOnlyOwnerShouldSetIncentivesVault() (gas: 719067)
47 | [PASS] testOnlyOwnerShouldSetInterestRatesManager() (gas: 878760)
48 | [PASS] testOnlyOwnerShouldSetPositionsManager() (gas: 4377220)
49 | [PASS] testOnlyOwnerShouldSetRewardsManager() (gas: 1044749)
50 | [PASS] testOnlyOwnerShouldSetTreasuryVault() (gas: 39055)
51 | [PASS] testReserveFactorShouldBeUpdatedWithRightValue() (gas: 126155)
52 | [PASS] testShouldCreateMarketWithRightParams() (gas: 333238)
53 | [PASS] testShouldCreateMarketWithTheRightValues() (gas: 315158)
54 | [PASS] testShouldDeployContractWithTheRightValues() (gas: 30349)
55 | [PASS] testShouldRevertWhenCreatingMarketWithAnImproperMarket() (gas: 60328)
56 | [PASS] testShouldSetMaxGasWithRightValues() (gas: 52385)
57 | Test result: ok. 16 passed; 0 failed; finished in 36.14s
58 |
59 | Running 2 tests for test-foundry/compound/TestP2PDisable.t.sol:TestP2PDisable
60 | [PASS] testShouldMatchBorrowDeltaWithP2PDisabled() (gas: 3700337)
61 | [PASS] testShouldMatchSupplyDeltaWithP2PDisabled() (gas: 2763816)
62 | Test result: ok. 2 passed; 0 failed; finished in 37.53s
63 |
64 | Running 4 tests for test-foundry/compound/TestMarketStrategy.t.sol:TestMarketStrategy
65 | [PASS] testShouldPutBorrowerOnPool() (gas: 1640976)
66 | [PASS] testShouldPutBorrowersOnPool() (gas: 2952983)
67 | [PASS] testShouldPutSupplierOnPool() (gas: 1641073)
68 | [PASS] testShouldPutSuppliersOnPool() (gas: 2188191)
69 | Test result: ok. 4 passed; 0 failed; finished in 1.99s
70 |
71 | Running 11 tests for test-foundry/compound/TestEth.t.sol:TestEth
72 | [PASS] testBorrowEthInP2P() (gas: 1671732)
73 | [PASS] testBorrowEthOnPool() (gas: 1280154)
74 | [PASS] testRepayEthInP2P() (gas: 2114965)
75 | [PASS] testRepayEthOnPool() (gas: 1450846)
76 | [PASS] testShouldGetEthMarketConfiguration() (gas: 62784)
77 | [PASS] testShouldLiquidateUserWithEthAsCollateral() (gas: 2681002)
78 | [PASS] testShouldLiquidateUserWithEthBorrowed() (gas: 2533744)
79 | [PASS] testSupplyEthInP2P() (gas: 1769181)
80 | [PASS] testSupplyEthOnPool() (gas: 543015)
81 | [PASS] testWithdrawEthInP2P() (gas: 1942079)
82 | [PASS] testWithdrawEthOnPool() (gas: 756578)
83 | Test result: ok. 11 passed; 0 failed; finished in 37.75s
84 |
85 | Running 9 tests for test-foundry/compound/TestIncentivesVault.t.sol:TestIncentivesVault
86 | [PASS] testFailWhenContractNotActive() (gas: 39047)
87 | [PASS] testOnlyMorphoShouldTriggerCompConvertFunction() (gas: 239422)
88 | [PASS] testOnlyOwnerShouldSetBonus() (gas: 40136)
89 | [PASS] testOnlyOwnerShouldSetIncentivesTreasuryVault() (gas: 23500)
90 | [PASS] testOnlyOwnerShouldSetOracle() (gas: 23478)
91 | [PASS] testOnlyOwnerShouldSetPauseStatus() (gas: 30768)
92 | [PASS] testOnlyOwnerShouldTransferTokensToDao() (gas: 57845)
93 | [PASS] testShouldGiveTheRightAmountOfRewards() (gas: 275005)
94 | [PASS] testShouldNotSetBonusAboveMaxBasisPoints() (gas: 13934)
95 | Test result: ok. 9 passed; 0 failed; finished in 1.72s
96 |
97 | Running 7 tests for test-foundry/compound/TestPausableMarket.t.sol:TestPausableMarket
98 | [PASS] testOnlyOwnerShouldTriggerPartialPauseFunction() (gas: 50052)
99 | [PASS] testOnlyOwnerShouldTriggerPauseFunction() (gas: 50120)
100 | [PASS] testPartialPausePartialUnpause() (gas: 28161)
101 | [PASS] testPauseUnpause() (gas: 28138)
102 | [PASS] testShouldDisableMarketWhenPaused() (gas: 3488221)
103 | [PASS] testShouldOnlyEnableRepayWithdrawLiquidateWhenPartiallyPaused() (gas: 4095974)
104 | [PASS] testShouldTriggerFunctionsWhenNotPaused() (gas: 2725953)
105 | Test result: ok. 7 passed; 0 failed; finished in 38.42s
106 |
107 | Running 10 tests for test-foundry/compound/TestFees.t.sol:TestFees
108 | [PASS] testOnlyOwnerCanSetTreasuryVault() (gas: 27636)
109 | [PASS] testOwnerShouldBeAbleToClaimFees() (gas: 3964440)
110 | [PASS] testShouldCollectTheRightAmountOfFees() (gas: 3971181)
111 | [PASS] testShouldNotBePossibleToSetFeesHigherThan100Percent() (gas: 22965)
112 | [PASS] testShouldNotBePossibleToSetP2PIndexCursorHigherThan100Percent() (gas: 22898)
113 | [PASS] testShouldNotClaimCompRewards() (gas: 3763399)
114 | [PASS] testShouldNotClaimFeesIfFactorIsZero() (gas: 3929661)
115 | [PASS] testShouldPayFee() (gas: 4581806)
116 | [PASS] testShouldReduceTheFeeToRepay() (gas: 4612967)
117 | [PASS] testShouldRevertWhenClaimingToZeroAddress() (gas: 3950381)
118 | Test result: ok. 10 passed; 0 failed; finished in 38.59s
119 |
120 | Running 3 tests for test-foundry/compound/TestRoundings.t.sol:TestRounding
121 | [PASS] testRoundingError1() (gas: 529193)
122 | [PASS] testRoundingError2() (gas: 414862)
123 | [PASS] testRoundingError3() (gas: 595486)
124 | Test result: ok. 3 passed; 0 failed; finished in 1.68s
125 |
126 | Running 40 tests for test-foundry/compound/TestRatesLens.t.sol:TestRatesLens
127 | [PASS] testAverageBorrowRateShouldEqual0WhenNoBorrow() (gas: 139298)
128 | [PASS] testAverageBorrowRateShouldEqualMidrateWhenHalfMatched() (gas: 1790646)
129 | [PASS] testAverageBorrowRateShouldEqualPoolRateWhenNoMatch() (gas: 1310310)
130 | [PASS] testAverageBorrowRateShouldEqualPoolRateWithFullBorrowDelta() (gas: 1860327)
131 | [PASS] testAverageRatesShouldEqualP2PRatesWhenFullyMatched() (gas: 2266758)
132 | [PASS] testAverageSupplyRateShouldEqual0WhenNoSupply() (gas: 138034)
133 | [PASS] testAverageSupplyRateShouldEqualMidrateWhenHalfMatched() (gas: 1788024)
134 | [PASS] testAverageSupplyRateShouldEqualPoolRateWhenNoMatch() (gas: 589542)
135 | [PASS] testAverageSupplyRateShouldEqualPoolRateWithFullSupplyDelta() (gas: 2130617)
136 | [PASS] testBorrowRateShouldEqual0WhenNoBorrow() (gas: 115216)
137 | [PASS] testBorrowRateShouldEqualPoolRateWithFullBorrowDelta() (gas: 1846073)
138 | [PASS] testGetRatesPerBlock() (gas: 159624)
139 | [PASS] testNextBorrowRateShouldEqual0WhenNoBorrow() (gas: 116180)
140 | [PASS] testNextBorrowRateShouldEqualCurrentRateWhenNoNewBorrow() (gas: 1449908)
141 | [PASS] testNextBorrowRateShouldEqualMidrateWhenHalfMatch() (gas: 677084)
142 | [PASS] testNextBorrowRateShouldEqualMidrateWithHalfSupplyDeltaAndNoSupplierOnPool() (gas: 1910500)
143 | [PASS] testNextBorrowRateShouldEqualP2PRateWhenDoubleBorrow() (gas: 1917172)
144 | [PASS] testNextBorrowRateShouldEqualP2PRateWhenFullMatch() (gas: 703437)
145 | [PASS] testNextBorrowRateShouldEqualP2PRateWithFullSupplyDeltaAndNoSupplierOnPool() (gas: 1888773)
146 | [PASS] testNextBorrowRateShouldEqualPoolRateWhenFullMatchButP2PDisabled() (gas: 663391)
147 | [PASS] testNextBorrowRateShouldEqualPoolRateWhenNoSupplierOnPool() (gas: 184375)
148 | [PASS] testNextSupplyRateShouldEqual0WhenNoSupply() (gas: 116067)
149 | [PASS] testNextSupplyRateShouldEqualCurrentRateWhenNoNewSupply() (gas: 729238)
150 | [PASS] testNextSupplyRateShouldEqualMidrateWhenHalfMatch() (gas: 1382115)
151 | [PASS] testNextSupplyRateShouldEqualMidrateWithHalfBorrowDeltaAndNoBorrowerOnPool() (gas: 1934137)
152 | [PASS] testNextSupplyRateShouldEqualP2PRateWhenDoubleSupply() (gas: 2023373)
153 | [PASS] testNextSupplyRateShouldEqualP2PRateWhenFullMatch() (gas: 1423980)
154 | [PASS] testNextSupplyRateShouldEqualP2PRateWithFullBorrowDeltaAndNoBorrowerOnPool() (gas: 1912401)
155 | [PASS] testNextSupplyRateShouldEqualPoolRateWhenFullMatchButP2PDisabled() (gas: 1401598)
156 | [PASS] testNextSupplyRateShouldEqualPoolRateWhenNoBorrowerOnPool() (gas: 215049)
157 | [PASS] testPoolBorrowAmountShouldBeEqualToPoolAmount() (gas: 1341349)
158 | [PASS] testPoolSupplyAmountShouldBeEqualToPoolAmount() (gas: 625018)
159 | [PASS] testRatesShouldBeConstantWhenSupplyDeltaWithoutInteraction() (gas: 2719936)
160 | [PASS] testSupplyRateShouldEqual0WhenNoSupply() (gas: 115223)
161 | [PASS] testSupplyRateShouldEqualPoolRateWithFullSupplyDelta() (gas: 1822395)
162 | [PASS] testUserBorrowRateShouldEqualMidrateWhenHalfMatched() (gas: 2232643)
163 | [PASS] testUserBorrowRateShouldEqualPoolRateWhenNotMatched() (gas: 1320142)
164 | [PASS] testUserRatesShouldEqualP2PRatesWhenFullyMatched() (gas: 2287753)
165 | [PASS] testUserSupplyRateShouldEqualMidrateWhenHalfMatched() (gas: 2044864)
166 | [PASS] testUserSupplyRateShouldEqualPoolRateWhenNotMatched() (gas: 601447)
167 | Test result: ok. 40 passed; 0 failed; finished in 42.60s
168 |
169 | Running 7 tests for test-foundry/compound/TestLiquidate.t.sol:TestLiquidate
170 | [PASS] testCannotBorrowLiquidateInSameBlock() (gas: 1785286)
171 | [PASS] testFailLiquidateZero() (gas: 29108)
172 | [PASS] testLiquidateUpdateIndexesSameAsCompound() (gas: 5196888)
173 | [PASS] testShouldLiquidateUser() (gas: 2231999)
174 | [PASS] testShouldLiquidateWhileInP2PAndPool() (gas: 3491945)
175 | [PASS] testShouldNotBePossibleToLiquidateUserAboveWater() (gas: 1342386)
176 | [PASS] testShouldPartiallyLiquidateWhileInP2PAndPool() (gas: 3500148)
177 | Test result: ok. 7 passed; 0 failed; finished in 43.19s
178 |
179 | Running 19 tests for test-foundry/compound/TestRewards.t.sol:TestRewards
180 | [PASS] testFailShouldNotClaimRewardsWhenRewardsManagerIsAddressZero() (gas: 3549514)
181 | [PASS] testShouldAllowClaimingRewardsOfMarketAlreadyClaimed() (gas: 1838613)
182 | [PASS] testShouldClaimOnSeveralMarkets() (gas: 1593885)
183 | [PASS] testShouldClaimRewardsAndTradeForMorpkoTokens() (gas: 836705)
184 | [PASS] testShouldClaimRewardsOnSeveralMarketsAtOnce() (gas: 2146792)
185 | [PASS] testShouldClaimRightAmountOfBorrowRewards() (gas: 1475514)
186 | [PASS] testShouldClaimRightAmountOfSupplyRewards() (gas: 1077285)
187 | [PASS] testShouldClaimTheSameAmountOfRewards() (gas: 4303721)
188 | [PASS] testShouldComputeCorrectBorrowIndex() (gas: 1702880)
189 | [PASS] testShouldComputeCorrectSupplyIndex() (gas: 838612)
190 | [PASS] testShouldGetRightAmountOfBorrowRewards() (gas: 1530824)
191 | [PASS] testShouldGetRightAmountOfSupplyRewards() (gas: 1105680)
192 | [PASS] testShouldNotBePossibleToClaimRewardsOnOtherMarket() (gas: 1140124)
193 | [PASS] testShouldRevertWhenClaimRewardsIsPaused() (gas: 51327)
194 | [PASS] testShouldUpdateCorrectBorrowIndex() (gas: 2041505)
195 | [PASS] testShouldUpdateCorrectBorrowIndexWhenSpeedIs0() (gas: 1740759)
196 | [PASS] testShouldUpdateCorrectSupplyIndex() (gas: 1458311)
197 | [PASS] testShouldUpdateCorrectSupplyIndexWhenSpeedIs0() (gas: 1277580)
198 | [PASS] testUsersShouldClaimRewardsIndependently() (gas: 4616239)
199 | Test result: ok. 19 passed; 0 failed; finished in 45.70s
200 |
201 | Running 5 tests for test-foundry/compound/TestMorphoGetters.t.sol:TestMorphoGetters
202 | [PASS] testEnteredMarkets() (gas: 1201763)
203 | [PASS] testFailUserLeftMarkets() (gas: 953791)
204 | [PASS] testGetAllMarkets() (gas: 48327)
205 | [PASS] testGetHead() (gas: 1748827)
206 | [PASS] testGetNext() (gas: 39673437)
207 | Test result: ok. 5 passed; 0 failed; finished in 69.92s
208 |
209 | Running 11 tests for test-foundry/compound/TestSupply.t.sol:TestSupply
210 | [PASS] testFailSupplyZero() (gas: 26462)
211 | [PASS] testSupply1() (gas: 536616)
212 | [PASS] testSupply2() (gas: 1708434)
213 | [PASS] testSupply3() (gas: 1823713)
214 | [PASS] testSupply4() (gas: 74816497)
215 | [PASS] testSupply5() (gas: 74935488)
216 | [PASS] testSupplyMultipleTimes() (gas: 616865)
217 | [PASS] testSupplyOnBehalf() (gas: 542532)
218 | [PASS] testSupplyOnPoolThreshold() (gas: 419860)
219 | [PASS] testSupplyRepayOnBehalf() (gas: 1714768)
220 | [PASS] testSupplyUpdateIndexesSameAsCompound() (gas: 1626194)
221 | Test result: ok. 11 passed; 0 failed; finished in 110.80s
222 |
223 | Running 8 tests for test-foundry/compound/TestBorrow.t.sol:TestBorrow
224 | [PASS] testBorrow1() (gas: 888535)
225 | [PASS] testBorrow2() (gas: 1264502)
226 | [PASS] testBorrow3() (gas: 1644098)
227 | [PASS] testBorrow4() (gas: 1907702)
228 | [PASS] testBorrow5() (gas: 9945040)
229 | [PASS] testBorrow6() (gas: 10209702)
230 | [PASS] testBorrowMultipleAssets() (gas: 1440991)
231 | [PASS] testBorrowOnPoolThreshold() (gas: 1183252)
232 | Test result: ok. 8 passed; 0 failed; finished in 112.34s
233 |
234 | Running 44 tests for test-foundry/compound/TestLens.t.sol:TestLens
235 | [PASS] testComputeLiquidation() (gas: 1802209)
236 | [PASS] testComputeLiquidation2() (gas: 1334556)
237 | [PASS] testComputeLiquidation3() (gas: 2199816)
238 | [PASS] testComputeLiquidation4() (gas: 1861807)
239 | [PASS] testFuzzLiquidation(uint64,uint80) (runs: 1024, μ: 2420440, ~: 2401447)
240 | [PASS] testFuzzLiquidationAboveIncentiveThreshold(uint64) (runs: 1024, μ: 4047053, ~: 3958960)
241 | [PASS] testFuzzLiquidationUnderIncentiveThreshold(uint64) (runs: 1024, μ: 4461214, ~: 4458560)
242 | [PASS] testGetAllMarkets() (gas: 54137)
243 | [PASS] testGetEnteredMarkets() (gas: 1441603)
244 | [PASS] testGetMainMarketData() (gas: 1267124)
245 | [PASS] testGetMarketConfiguration() (gas: 80170)
246 | [PASS] testGetOutdatedIndexes() (gas: 1289285)
247 | [PASS] testGetUpdatedIndexes() (gas: 1333199)
248 | [PASS] testGetUpdatedIndexesWithTransferToCTokenContract() (gas: 196296)
249 | [PASS] testGetUpdatedP2PBorrowIndex() (gas: 170467)
250 | [PASS] testGetUpdatedP2PSupplyIndex() (gas: 170509)
251 | [PASS] testGetterUserWithNothing() (gas: 168087)
252 | [PASS] testHealthFactorAbove1() (gas: 1753272)
253 | [PASS] testHealthFactorAbove1WhenHalfMatched() (gas: 2381274)
254 | [PASS] testHealthFactorAbove1WithUpdatedMarkets() (gas: 1903207)
255 | [PASS] testHealthFactorBelow1() (gas: 1815502)
256 | [PASS] testHealthFactorEqual1() (gas: 2447116)
257 | [PASS] testHealthFactorEqual1WhenBorrowingMaxCapacity() (gas: 1666276)
258 | [PASS] testHealthFactorShouldBeInfinityForPureSuppliers() (gas: 604926)
259 | [PASS] testIsLiquidatableFalse() (gas: 1331591)
260 | [PASS] testIsLiquidatableTrue() (gas: 1758919)
261 | [PASS] testLiquidatableWithUpdatedP2PIndexes() (gas: 2056354)
262 | [PASS] testLiquidationWithUpdatedPoolIndexes() (gas: 1578662)
263 | [PASS] testLiquidityDataFailsWhenOracleFails() (gas: 982164)
264 | [PASS] testLiquidityDataForUSDT() (gas: 781149)
265 | [PASS] testLiquidityDataWithMultipleAssetsAndUSDT() (gas: 2383371)
266 | [PASS] testMaxCapacitiesWithNothingWithSupplyWithMultipleAssetsAndBorrow() (gas: 2172103)
267 | [PASS] testMaxCapacitiesWithSupply() (gas: 844737)
268 | [PASS] testNoRepayLiquidation() (gas: 4916426)
269 | [PASS] testTotalSupplyBorrowWithHalfBorrowDelta() (gas: 3095325)
270 | [PASS] testTotalSupplyBorrowWithHalfSupplyDelta() (gas: 3033000)
271 | [PASS] testUserBalanceStatesWithSupplyAndBorrow() (gas: 1348929)
272 | [PASS] testUserBalanceStatesWithSupplyAndBorrowWithMultipleAssets() (gas: 2457912)
273 | [PASS] testUserBalanceWithMatching() (gas: 1828644)
274 | [PASS] testUserBalanceWithoutMatching() (gas: 1319574)
275 | [PASS] testUserLiquidityDataForAssetWithNothing() (gas: 161934)
276 | [PASS] testUserLiquidityDataForAssetWithSupply() (gas: 595131)
277 | [PASS] testUserLiquidityDataForAssetWithSupplyAndBorrow() (gas: 1237029)
278 | [PASS] testUserLiquidityDataForAssetWithSupplyAndBorrowWithMultipleAssets() (gas: 1350454)
279 | Test result: ok. 44 passed; 0 failed; finished in 127.60s
280 |
281 | Running 16 tests for test-foundry/compound/TestRepay.t.sol:TestRepay
282 | [PASS] testCannotBorrowRepayInSameBlock() (gas: 1303041)
283 | [PASS] testCannotBorrowRepayOnBehalfInSameBlock() (gas: 1309508)
284 | [PASS] testDeltaRepay() (gas: 109541916)
285 | [PASS] testDeltaRepayAll() (gas: 107509144)
286 | [PASS] testFailRepayZero() (gas: 28544)
287 | [PASS] testRepay1() (gas: 1411307)
288 | [PASS] testRepay2_1() (gas: 2584249)
289 | [PASS] testRepay2_2() (gas: 75379737)
290 | [PASS] testRepay2_3() (gas: 2025953)
291 | [PASS] testRepay2_4() (gas: 75537340)
292 | [PASS] testRepayAll() (gas: 1327886)
293 | [PASS] testRepayOnBehalf() (gas: 1425444)
294 | [PASS] testRepayOnPoolThreshold() (gas: 1427782)
295 | [PASS] testRepayRepayOnBehalf() (gas: 1456175)
296 | [PASS] testRepayUpdateIndexesSameAsCompound() (gas: 3328237)
297 | [PASS] testRepayWithMaxP2PSupplyDelta() (gas: 1451529)
298 | Test result: ok. 16 passed; 0 failed; finished in 151.90s
299 |
300 | Running 17 tests for test-foundry/compound/TestWithdraw.t.sol:TestWithdraw
301 | [PASS] testDeltaWithdraw() (gas: 115663600)
302 | [PASS] testDeltaWithdrawAll() (gas: 81821866)
303 | [PASS] testFailInfiniteWithdraw() (gas: 2911336)
304 | [PASS] testFailWithdrawZero() (gas: 28398)
305 | [PASS] testShouldBeAbleToWithdrawAfterDelayWhenPartiallyMatched() (gas: 1976348)
306 | [PASS] testShouldNotFreezeMarketWithExchangeRatePump() (gas: 837898)
307 | [PASS] testShouldNotWithdrawWhenUnderCollaterized() (gas: 1577514)
308 | [PASS] testShouldWithdrawToReceiver() (gas: 860995)
309 | [PASS] testWithdraw1() (gas: 1311588)
310 | [PASS] testWithdraw2() (gas: 910838)
311 | [PASS] testWithdraw3_1() (gas: 2131635)
312 | [PASS] testWithdraw3_2() (gas: 67873628)
313 | [PASS] testWithdraw3_3() (gas: 2157323)
314 | [PASS] testWithdraw3_4() (gas: 68231234)
315 | [PASS] testWithdrawAll() (gas: 796657)
316 | [PASS] testWithdrawWhileAttackerSendsCToken() (gas: 2286449)
317 | [PASS] testWithdrawnOnPoolThreshold() (gas: 587851)
318 | Test result: ok. 17 passed; 0 failed; finished in 118.90s
319 | ╭-----------------------------------------------------------------+-----------------+-------+--------+-------+---------╮
320 | | contracts/compound/IncentivesVault.sol:IncentivesVault Contract | | | | | |
321 | +======================================================================================================================+
322 | | Deployment Cost | Deployment Size | | | | |
323 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
324 | | 640594 | 3430 | | | | |
325 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
326 | | | | | | | |
327 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
328 | | Function Name | min | avg | median | max | # calls |
329 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
330 | | MAX_BASIS_POINTS | 305 | 305 | 305 | 305 | 1 |
331 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
332 | | bonus | 318 | 318 | 318 | 318 | 1 |
333 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
334 | | incentivesTreasuryVault | 415 | 415 | 415 | 415 | 1 |
335 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
336 | | isPaused | 377 | 377 | 377 | 377 | 2 |
337 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
338 | | oracle | 393 | 393 | 393 | 393 | 1 |
339 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
340 | | setBonus | 2452 | 15520 | 21480 | 25580 | 5 |
341 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
342 | | setIncentivesTreasuryVault | 2605 | 7111 | 8614 | 8614 | 4 |
343 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
344 | | setOracle | 2606 | 5610 | 5610 | 8615 | 2 |
345 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
346 | | setPauseStatus | 1436 | 13858 | 14151 | 25694 | 4 |
347 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
348 | | tradeCompForMorphoTokens | 443 | 33672 | 36386 | 65651 | 6 |
349 | |-----------------------------------------------------------------+-----------------+-------+--------+-------+---------|
350 | | transferTokensToDao | 2604 | 20542 | 20542 | 38481 | 2 |
351 | ╰-----------------------------------------------------------------+-----------------+-------+--------+-------+---------╯
352 |
353 |
354 | | contracts/compound/InterestRatesManager.sol:InterestRatesManager contract | | | | | |
355 | |---------------------------------------------------------------------------|-----------------|-------|--------|--------|---------|
356 | | Deployment Cost | Deployment Size | | | | |
357 | | 806808 | 4186 | | | | |
358 | | Function Name | min | avg | median | max | # calls |
359 | | updateP2PIndexes | 618 | 27186 | 23286 | 102847 | 2314 |
360 |
361 |
362 | | contracts/compound/Morpho.sol:Morpho contract | | | | | |
363 | |-----------------------------------------------|-----------------|--------|--------|---------|---------|
364 | | Deployment Cost | Deployment Size | | | | |
365 | | 3267385 | 16488 | | | | |
366 | | Function Name | min | avg | median | max | # calls |
367 | | borrow(address,uint256) | 3918 | 555787 | 634689 | 1099004 | 257 |
368 | | borrow(address,uint256,uint256) | 393501 | 449088 | 439235 | 681884 | 40 |
369 | | borrowBalanceInOf | 894 | 1424 | 894 | 4894 | 520 |
370 | | cEth | 405 | 665 | 405 | 2405 | 23 |
371 | | claimRewards | 5871 | 162687 | 177263 | 314658 | 17 |
372 | | claimToTreasury | 1172 | 8457 | 4996 | 23536 | 10 |
373 | | comptroller | 450 | 456 | 450 | 2450 | 2637 |
374 | | createMarket | 2714 | 260753 | 255389 | 307394 | 1494 |
375 | | defaultMaxGasForMatching | 635 | 635 | 635 | 635 | 1 |
376 | | deltas | 1050 | 2248 | 1050 | 9050 | 207 |
377 | | enteredMarkets | 594 | 812 | 867 | 867 | 5 |
378 | | fallback | 55 | 55 | 55 | 55 | 71 |
379 | | getAllMarkets | 2433 | 10433 | 16433 | 16433 | 7 |
380 | | getEnteredMarkets | 1234 | 1489 | 1508 | 2889 | 88 |
381 | | getHead | 830 | 1341 | 966 | 2966 | 19 |
382 | | getNext | 1035 | 1105 | 1108 | 1171 | 36 |
383 | | incentivesVault | 427 | 427 | 427 | 427 | 1 |
384 | | initialize | 3810 | 246960 | 247949 | 247949 | 247 |
385 | | interestRatesManager | 449 | 453 | 449 | 2449 | 2145 |
386 | | isClaimRewardsPaused | 399 | 399 | 399 | 399 | 1 |
387 | | lastPoolIndexes | 742 | 919 | 742 | 2742 | 349 |
388 | | liquidate | 4065 | 322433 | 388716 | 556854 | 28 |
389 | | marketParameters | 669 | 1100 | 669 | 2669 | 153 |
390 | | marketStatus | 752 | 1152 | 752 | 2752 | 10 |
391 | | maxSortedUsers | 351 | 351 | 351 | 351 | 1 |
392 | | p2pBorrowIndex | 571 | 687 | 571 | 2571 | 480 |
393 | | p2pDisabled | 638 | 1138 | 638 | 2638 | 16 |
394 | | p2pSupplyIndex | 569 | 692 | 569 | 2569 | 453 |
395 | | positionsManager | 471 | 471 | 471 | 471 | 1 |
396 | | repay(address,address,uint256) | 9059 | 59664 | 12478 | 204641 | 4 |
397 | | repay(address,uint256) | 3924 | 256434 | 166038 | 2302095 | 64 |
398 | | rewardsManager | 449 | 658 | 449 | 2449 | 2391 |
399 | | setClaimRewardsPauseStatus | 2693 | 18083 | 25778 | 25778 | 3 |
400 | | setDefaultMaxGasForMatching | 1179 | 8651 | 8304 | 10304 | 29 |
401 | | setIncentivesVault | 2695 | 23731 | 23878 | 23878 | 248 |
402 | | setInterestRatesManager | 2629 | 5670 | 5670 | 8712 | 2 |
403 | | setMaxSortedUsers | 577 | 4549 | 4549 | 8521 | 4 |
404 | | setP2PDisabled | 2762 | 22242 | 24538 | 28538 | 12 |
405 | | setP2PIndexCursor | 4947 | 4947 | 4947 | 4947 | 1 |
406 | | setPartialPauseStatus | 2103 | 6092 | 6428 | 9428 | 6 |
407 | | setPauseStatus | 2086 | 6072 | 6407 | 9407 | 6 |
408 | | setPositionsManager | 2630 | 5671 | 5671 | 8713 | 2 |
409 | | setReserveFactor | 2784 | 43786 | 2784 | 111371 | 21 |
410 | | setRewardsManager | 2631 | 21618 | 21814 | 21814 | 249 |
411 | | setTreasuryVault | 2629 | 23514 | 23812 | 23812 | 250 |
412 | | supply(address,address,uint256) | 473988 | 473988 | 473988 | 473988 | 1 |
413 | | supply(address,address,uint256,uint256) | 13891 | 13891 | 13891 | 13891 | 1 |
414 | | supply(address,uint256) | 3917 | 370992 | 394844 | 2101104 | 508 |
415 | | supplyBalanceInOf | 936 | 1146 | 936 | 4936 | 476 |
416 | | treasuryVault | 471 | 1271 | 471 | 2471 | 5 |
417 | | updateP2PIndexes | 1811 | 25120 | 24479 | 54533 | 1310 |
418 | | wEth | 2404 | 2404 | 2404 | 2404 | 1 |
419 | | withdraw(address,uint256) | 3877 | 265176 | 185246 | 1943502 | 85 |
420 | | withdraw(address,uint256,address) | 335708 | 335708 | 335708 | 335708 | 1 |
421 |
422 |
423 | | contracts/compound/PositionsManager.sol:PositionsManager contract | | | | | |
424 | |-------------------------------------------------------------------|-----------------|--------|--------|---------|---------|
425 | | Deployment Cost | Deployment Size | | | | |
426 | | 4301120 | 21650 | | | | |
427 | | Function Name | min | avg | median | max | # calls |
428 | | borrowLogic | 148089 | 542124 | 442196 | 1095172 | 295 |
429 | | liquidateLogic | 2901 | 329402 | 384583 | 552720 | 27 |
430 | | repayLogic | 679 | 244226 | 161563 | 2298230 | 67 |
431 | | supplyLogic | 737 | 365318 | 395215 | 2097248 | 508 |
432 | | withdrawLogic | 657 | 264992 | 181419 | 1939675 | 85 |
433 |
434 |
435 | | contracts/compound/RewardsManager.sol:RewardsManager contract | | | | | |
436 | |---------------------------------------------------------------|-----------------|-------|--------|--------|---------|
437 | | Deployment Cost | Deployment Size | | | | |
438 | | 972578 | 5015 | | | | |
439 | | Function Name | min | avg | median | max | # calls |
440 | | accrueUserBorrowUnclaimedRewards | 2107 | 23228 | 23802 | 56277 | 576 |
441 | | accrueUserSupplyUnclaimedRewards | 2129 | 28041 | 25824 | 57058 | 785 |
442 | | claimRewards | 20740 | 68575 | 67376 | 184999 | 24 |
443 | | compBorrowerIndex | 708 | 1152 | 708 | 2708 | 18 |
444 | | compSupplierIndex | 731 | 941 | 731 | 2731 | 19 |
445 | | fallback | 70 | 70 | 70 | 70 | 1 |
446 | | getLocalCompBorrowState | 826 | 1359 | 826 | 2826 | 15 |
447 | | getLocalCompSupplyState | 849 | 1115 | 849 | 2849 | 15 |
448 | | initialize | 2753 | 68879 | 69148 | 69148 | 247 |
449 | | userUnclaimedCompRewards | 601 | 601 | 601 | 601 | 10 |
450 |
451 |
452 | | contracts/compound/lens/Lens.sol:Lens contract | | | | | |
453 | |------------------------------------------------|-----------------|--------|--------|--------|---------|
454 | | Deployment Cost | Deployment Size | | | | |
455 | | 4875711 | 24519 | | | | |
456 | | Function Name | min | avg | median | max | # calls |
457 | | computeLiquidationRepayAmount | 57311 | 108634 | 96652 | 169848 | 20 |
458 | | fallback | 70 | 70 | 70 | 70 | 1 |
459 | | getAllMarkets | 31231 | 31231 | 31231 | 31231 | 1 |
460 | | getAverageBorrowRatePerBlock | 46872 | 64362 | 48872 | 126507 | 6 |
461 | | getAverageSupplyRatePerBlock | 40106 | 61730 | 46939 | 125230 | 6 |
462 | | getCurrentBorrowBalanceInOf | 13686 | 20869 | 13686 | 35237 | 3 |
463 | | getCurrentCompBorrowIndex | 13060 | 13060 | 13060 | 13060 | 1 |
464 | | getCurrentCompSupplyIndex | 11798 | 11798 | 11798 | 11798 | 1 |
465 | | getCurrentP2PBorrowIndex | 93072 | 93072 | 93072 | 93072 | 1 |
466 | | getCurrentP2PSupplyIndex | 10991 | 52048 | 52048 | 93106 | 2 |
467 | | getCurrentSupplyBalanceInOf | 13715 | 21588 | 18697 | 35246 | 4 |
468 | | getCurrentUserBorrowRatePerBlock | 56919 | 69872 | 58919 | 100374 | 5 |
469 | | getCurrentUserSupplyRatePerBlock | 58381 | 70230 | 59011 | 100446 | 5 |
470 | | getEnteredMarkets | 6920 | 6920 | 6920 | 6920 | 1 |
471 | | getIndexes | 16685 | 48783 | 36583 | 93083 | 3 |
472 | | getMainMarketData | 92255 | 92255 | 92255 | 92255 | 1 |
473 | | getMarketConfiguration | 49443 | 50224 | 50224 | 51005 | 2 |
474 | | getNextUserBorrowRatePerBlock | 63561 | 88737 | 77244 | 160043 | 9 |
475 | | getNextUserSupplyRatePerBlock | 63540 | 88598 | 77197 | 160036 | 9 |
476 | | getRatesPerBlock | 39293 | 43035 | 39293 | 100293 | 21 |
477 | | getTotalBorrow | 229732 | 229732 | 229732 | 229732 | 2 |
478 | | getTotalMarketBorrow | 24173 | 27413 | 27413 | 30654 | 4 |
479 | | getTotalMarketSupply | 22154 | 25766 | 25766 | 29379 | 4 |
480 | | getTotalSupply | 342843 | 342843 | 342843 | 342843 | 2 |
481 | | getUserBalanceStates | 54452 | 66682 | 58452 | 110605 | 9 |
482 | | getUserHealthFactor | 45806 | 59663 | 58960 | 75463 | 10 |
483 | | getUserLiquidityDataForAsset | 24721 | 60374 | 39221 | 137304 | 10 |
484 | | getUserMaxCapacitiesForAsset | 11882 | 102461 | 90775 | 165527 | 22 |
485 | | getUserUnclaimedRewards | 20025 | 44589 | 41854 | 103449 | 10 |
486 | | initialize | 2776 | 71216 | 71495 | 71495 | 247 |
487 | | isLiquidatable | 54487 | 59180 | 54487 | 93585 | 25 |
488 |
489 |
490 | | node_modules/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol:ProxyAdmin contract | | | | | |
491 | |-------------------------------------------------------------------------------------------|-----------------|------|--------|-------|---------|
492 | | Deployment Cost | Deployment Size | | | | |
493 | | 389546 | 1953 | | | | |
494 | | Function Name | min | avg | median | max | # calls |
495 | | upgrade | 2684 | 9837 | 14497 | 14497 | 9 |
496 | | upgradeAndCall | 2960 | 9642 | 9527 | 16440 | 6 |
497 |
498 |
499 | | node_modules/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy contract | | | | | |
500 | |-----------------------------------------------------------------------------------------------------------------------------|-----------------|--------|--------|---------|---------|
501 | | Deployment Cost | Deployment Size | | | | |
502 | | 467312 | 4010 | | | | |
503 | | Function Name | min | avg | median | max | # calls |
504 | | accrueUserBorrowUnclaimedRewards | 2911 | 24032 | 24606 | 57081 | 576 |
505 | | accrueUserSupplyUnclaimedRewards | 2933 | 30302 | 26628 | 63253 | 785 |
506 | | borrow(address,uint256) | 4720 | 556580 | 635487 | 1099643 | 257 |
507 | | borrow(address,uint256,uint256) | 394144 | 449739 | 439879 | 682688 | 40 |
508 | | borrowBalanceInOf | 1698 | 2253 | 1698 | 12198 | 520 |
509 | | cEth | 1200 | 2026 | 1200 | 9700 | 23 |
510 | | changeAdmin | 2419 | 2419 | 2419 | 2419 | 246 |
511 | | claimRewards(address[],address)(uint256) | 21559 | 69282 | 68032 | 185824 | 24 |
512 | | claimRewards(address[],bool)(uint256) | 6685 | 163444 | 178076 | 315477 | 17 |
513 | | claimToTreasury | 1998 | 9247 | 5818 | 24193 | 10 |
514 | | compBorrowerIndex | 1509 | 1953 | 1509 | 3509 | 18 |
515 | | compSupplierIndex | 1532 | 1742 | 1532 | 3532 | 19 |
516 | | comptroller | 1245 | 1251 | 1245 | 3245 | 2637 |
517 | | computeLiquidationRepayAmount | 58130 | 110428 | 97471 | 170667 | 20 |
518 | | createMarket | 10031 | 261636 | 256193 | 308198 | 1494 |
519 | | defaultMaxGasForMatching | 1442 | 1442 | 1442 | 1442 | 1 |
520 | | deltas | 1860 | 3058 | 1860 | 9860 | 207 |
521 | | enteredMarkets | 1393 | 1613 | 1668 | 1668 | 5 |
522 | | fallback | 726 | 726 | 726 | 726 | 70 |
523 | | getAllMarkets | 3264 | 16301 | 17264 | 38562 | 8 |
524 | | getAverageBorrowRatePerBlock | 47676 | 70583 | 56176 | 133811 | 6 |
525 | | getAverageSupplyRatePerBlock | 47410 | 69034 | 54243 | 132534 | 6 |
526 | | getCurrentBorrowBalanceInOf | 14493 | 21676 | 14493 | 36044 | 3 |
527 | | getCurrentCompBorrowIndex | 20358 | 20358 | 20358 | 20358 | 1 |
528 | | getCurrentCompSupplyIndex | 19096 | 19096 | 19096 | 19096 | 1 |
529 | | getCurrentP2PBorrowIndex | 100370 | 100370 | 100370 | 100370 | 1 |
530 | | getCurrentP2PSupplyIndex | 18289 | 59346 | 59346 | 100404 | 2 |
531 | | getCurrentSupplyBalanceInOf | 14522 | 25645 | 26004 | 36053 | 4 |
532 | | getCurrentUserBorrowRatePerBlock | 57720 | 74573 | 66220 | 107675 | 5 |
533 | | getCurrentUserSupplyRatePerBlock | 65682 | 76231 | 66312 | 107747 | 5 |
534 | | getEnteredMarkets | 2038 | 2505 | 2318 | 14236 | 89 |
535 | | getHead | 1631 | 2142 | 1767 | 3767 | 19 |
536 | | getIndexes | 23998 | 56096 | 43896 | 100396 | 3 |
537 | | getLocalCompBorrowState | 1627 | 2160 | 1627 | 3627 | 15 |
538 | | getLocalCompSupplyState | 1650 | 1916 | 1650 | 3650 | 15 |
539 | | getMainMarketData | 99577 | 99577 | 99577 | 99577 | 1 |
540 | | getMarketConfiguration | 56777 | 57558 | 57558 | 58339 | 2 |
541 | | getNext | 1842 | 1912 | 1915 | 1978 | 36 |
542 | | getNextUserBorrowRatePerBlock | 70877 | 96053 | 84560 | 167359 | 9 |
543 | | getNextUserSupplyRatePerBlock | 70856 | 95914 | 84513 | 167352 | 9 |
544 | | getRatesPerBlock | 40103 | 45083 | 40103 | 107603 | 21 |
545 | | getTotalBorrow | 230533 | 230533 | 230533 | 230533 | 2 |
546 | | getTotalMarketBorrow | 24974 | 28214 | 28214 | 31455 | 4 |
547 | | getTotalMarketSupply | 22955 | 26567 | 26567 | 30180 | 4 |
548 | | getTotalSupply | 350144 | 350144 | 350144 | 350144 | 2 |
549 | | getUserBalanceStates | 55265 | 72551 | 65765 | 117918 | 9 |
550 | | getUserHealthFactor | 53113 | 63721 | 66267 | 76276 | 10 |
551 | | getUserLiquidityDataForAsset | 25546 | 65099 | 46546 | 144629 | 10 |
552 | | getUserMaxCapacitiesForAsset | 19184 | 107106 | 96337 | 172831 | 22 |
553 | | getUserUnclaimedRewards | 20838 | 48654 | 49167 | 104274 | 10 |
554 | | incentivesVault | 1222 | 1222 | 1222 | 1222 | 1 |
555 | | initialize(address) | 69943 | 71116 | 71116 | 72290 | 492 |
556 | | initialize(address,address,address,(uint64,uint64,uint64,uint64),uint256,uint256,address,address) | 248801 | 248801 | 248801 | 248801 | 246 |
557 | | interestRatesManager | 1244 | 1248 | 1244 | 3244 | 2145 |
558 | | isClaimRewardsPaused | 1194 | 1194 | 1194 | 1194 | 1 |
559 | | isLiquidatable | 55294 | 61550 | 55294 | 94404 | 25 |
560 | | lastPoolIndexes | 1546 | 1872 | 1546 | 10046 | 349 |
561 | | liquidate | 4879 | 323361 | 389364 | 557502 | 28 |
562 | | marketParameters | 1470 | 1943 | 1470 | 9970 | 153 |
563 | | marketStatus | 1556 | 1956 | 1556 | 3556 | 10 |
564 | | maxSortedUsers | 1146 | 1146 | 1146 | 1146 | 1 |
565 | | p2pBorrowIndex | 1369 | 1485 | 1369 | 3369 | 480 |
566 | | p2pDisabled | 1436 | 1936 | 1436 | 3436 | 16 |
567 | | p2pSupplyIndex | 1367 | 1504 | 1367 | 9867 | 453 |
568 | | positionsManager | 1266 | 1266 | 1266 | 1266 | 1 |
569 | | repay(address,address,uint256) | 9867 | 62055 | 16536 | 205284 | 4 |
570 | | repay(address,uint256) | 4726 | 257179 | 166676 | 2302733 | 64 |
571 | | rewardsManager | 1244 | 1453 | 1244 | 3244 | 2391 |
572 | | setClaimRewardsPauseStatus | 10004 | 25383 | 33073 | 33073 | 3 |
573 | | setDefaultMaxGasForMatching | 2002 | 12371 | 9114 | 17614 | 29 |
574 | | setIncentivesVault | 10006 | 24579 | 24673 | 24673 | 248 |
575 | | setInterestRatesManager | 9940 | 12973 | 12973 | 16007 | 2 |
576 | | setMaxSortedUsers | 1388 | 8602 | 8602 | 15816 | 4 |
577 | | setP2PDisabled | 10076 | 26834 | 25336 | 35836 | 12 |
578 | | setP2PIndexCursor | 12249 | 12249 | 12249 | 12249 | 1 |
579 | | setPartialPauseStatus | 2741 | 10116 | 9140 | 16726 | 6 |
580 | | setPauseStatus | 2724 | 10096 | 9119 | 16705 | 6 |
581 | | setPositionsManager | 9941 | 12974 | 12974 | 16008 | 2 |
582 | | setReserveFactor | 10098 | 50475 | 10098 | 118669 | 21 |
583 | | setRewardsManager | 6008 | 22464 | 22609 | 22609 | 249 |
584 | | setTreasuryVault | 9940 | 24408 | 24607 | 24607 | 250 |
585 | | supply(address,address,uint256) | 481292 | 481292 | 481292 | 481292 | 1 |
586 | | supply(address,address,uint256,uint256) | 21205 | 21205 | 21205 | 21205 | 1 |
587 | | supply(address,uint256) | 4719 | 373650 | 395877 | 2101743 | 508 |
588 | | supplyBalanceInOf | 1740 | 1977 | 1740 | 12240 | 476 |
589 | | treasuryVault | 1266 | 3366 | 1266 | 9766 | 5 |
590 | | updateP2PIndexes | 2606 | 25920 | 25274 | 55328 | 1310 |
591 | | upgradeTo | 5047 | 8380 | 9047 | 9047 | 6 |
592 | | upgradeToAndCall | 10174 | 10400 | 10514 | 10514 | 3 |
593 | | userUnclaimedCompRewards | 1399 | 1399 | 1399 | 1399 | 10 |
594 | | wEth | 3199 | 3199 | 3199 | 3199 | 1 |
595 | | withdraw(address,uint256) | 4679 | 265984 | 185884 | 1944140 | 85 |
596 | | withdraw(address,uint256,address) | 336351 | 336351 | 336351 | 336351 | 1 |
597 |
598 |
599 | | test-foundry/common/helpers/MorphoToken.sol:MorphoToken contract | | | | | |
600 | |------------------------------------------------------------------|-----------------|-------|--------|-------|---------|
601 | | Deployment Cost | Deployment Size | | | | |
602 | | 792324 | 4514 | | | | |
603 | | Function Name | min | avg | median | max | # calls |
604 | | balanceOf | 608 | 941 | 608 | 2608 | 6 |
605 | | transfer | 2921 | 24802 | 24821 | 29621 | 251 |
606 |
607 |
608 | | test-foundry/compound/helpers/Attacker.sol:Attacker contract | | | | | |
609 | |--------------------------------------------------------------|-----------------|--------|--------|--------|---------|
610 | | Deployment Cost | Deployment Size | | | | |
611 | | 155602 | 809 | | | | |
612 | | Function Name | min | avg | median | max | # calls |
613 | | approve | 25269 | 25269 | 25269 | 25269 | 1 |
614 | | deposit | 142627 | 142627 | 142627 | 142627 | 1 |
615 | | transfer | 26107 | 26107 | 26107 | 26107 | 1 |
616 |
617 |
618 | | test-foundry/compound/helpers/DumbOracle.sol:DumbOracle contract | | | | | |
619 | |------------------------------------------------------------------|-----------------|-----|--------|-----|---------|
620 | | Deployment Cost | Deployment Size | | | | |
621 | | 31081 | 185 | | | | |
622 | | Function Name | min | avg | median | max | # calls |
623 | | consult | 235 | 235 | 235 | 235 | 4 |
624 |
625 |
626 | | test-foundry/compound/helpers/SimplePriceOracle.sol:SimplePriceOracle contract | | | | | |
627 | |--------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------|
628 | | Deployment Cost | Deployment Size | | | | |
629 | | 195438 | 1008 | | | | |
630 | | Function Name | min | avg | median | max | # calls |
631 | | getUnderlyingPrice | 618 | 1472 | 1570 | 1592 | 517 |
632 | | setDirectPrice | 384 | 474 | 479 | 479 | 23 |
633 | | setUnderlyingPrice | 486 | 21641 | 23443 | 27965 | 189 |
634 |
635 |
636 | | test-foundry/compound/helpers/User.sol:User contract | | | | | |
637 | |------------------------------------------------------|-----------------|--------|--------|---------|---------|
638 | | Deployment Cost | Deployment Size | | | | |
639 | | 1276426 | 6509 | | | | |
640 | | Function Name | min | avg | median | max | # calls |
641 | | approve(address,address,uint256) | 23134 | 30855 | 25328 | 41681 | 31 |
642 | | approve(address,uint256) | 25319 | 29756 | 28672 | 43672 | 492 |
643 | | approve(address,uint256)(bool) | 25319 | 31294 | 28672 | 43672 | 65 |
644 | | balanceOf | 1298 | 2837 | 1552 | 6052 | 37 |
645 | | borrow(address,uint256) | 5619 | 557477 | 636389 | 1100364 | 257 |
646 | | borrow(address,uint256,uint256) | 394918 | 450522 | 440652 | 683655 | 40 |
647 | | claimRewards | 11196 | 174555 | 183109 | 317048 | 16 |
648 | | compoundBorrow | 72966 | 177507 | 193358 | 280870 | 10 |
649 | | compoundClaimRewards | 91818 | 91818 | 91818 | 91818 | 3 |
650 | | compoundRepay | 89260 | 89260 | 89260 | 89260 | 1 |
651 | | compoundSupply | 71305 | 166572 | 173107 | 268507 | 15 |
652 | | createMarket | 15730 | 15730 | 15730 | 15730 | 12 |
653 | | liquidate | 6001 | 346908 | 390268 | 558405 | 26 |
654 | | repay | 5659 | 257973 | 167425 | 2303482 | 64 |
655 | | setDefaultMaxGasForMatching | 5318 | 5318 | 5318 | 5318 | 2 |
656 | | setMaxSortedUsers | 4177 | 4177 | 4177 | 4177 | 2 |
657 | | setP2PDisabled | 15600 | 15600 | 15600 | 15600 | 2 |
658 | | setPartialPauseStatus | 15513 | 15513 | 15513 | 15513 | 1 |
659 | | setPauseStatus | 15536 | 15536 | 15536 | 15536 | 1 |
660 | | setReserveFactor | 15578 | 15578 | 15578 | 15578 | 12 |
661 | | setTreasuryVault | 15284 | 15284 | 15284 | 15284 | 1 |
662 | | supply | 5595 | 375253 | 398006 | 2102446 | 508 |
663 | | withdraw(address,uint256) | 5577 | 269668 | 186605 | 1944861 | 84 |
664 | | withdraw(address,uint256,address) | 337208 | 337208 | 337208 | 337208 | 1 |
665 |
--------------------------------------------------------------------------------
/tests/report.test.ts:
--------------------------------------------------------------------------------
1 | // import * as cp from "child_process";
2 | import * as fs from "fs";
3 |
4 | // import * as path from "path";
5 | // import * as process from "process";
6 | // import { expect, test } from "@jest/globals";
7 | import { formatMarkdownDiff, formatShellDiff } from "../src/format";
8 | import { loadReports, computeDiffs } from "../src/report";
9 |
10 | const srcContent = fs.readFileSync("tests/mocks/gas_report.2.ansi", "utf8");
11 | const cmpContent = fs.readFileSync("tests/mocks/gas_report.1.ansi", "utf8");
12 |
13 | describe("", () => {
14 | // shows how the runner will run a javascript action with env / stdout protocol
15 | // it("should run action", () => {
16 | // const np = process.execPath;
17 | // const ip = path.join(__dirname, "..", "dist", "index.js");
18 | // console.log(
19 | // cp
20 | // .execFileSync(np, [ip], {
21 | // env: {
22 | // ...process.env,
23 | // INPUT_WORKFLOWID: "test",
24 | // INPUT_BASE: "base",
25 | // INPUT_HEAD: "head",
26 | // GITHUB_TOKEN: "token",
27 | // INPUT_REPORT: "report",
28 | // },
29 | // })
30 | // .toString()
31 | // );
32 | // });
33 |
34 | it("should compare 1 to 2 with shell format", () => {
35 | const loadOptions = { ignorePatterns: ["test-foundry/**/*"] };
36 | console.log(
37 | formatShellDiff(
38 | computeDiffs(
39 | loadReports(srcContent, loadOptions),
40 | loadReports(cmpContent, loadOptions),
41 | [],
42 | []
43 | )
44 | )
45 | );
46 | });
47 |
48 | it("should compare 2 to 1 with shell format", () => {
49 | const loadOptions = { ignorePatterns: ["**/User.sol"] };
50 | console.log(
51 | formatShellDiff(
52 | computeDiffs(
53 | loadReports(srcContent, loadOptions),
54 | loadReports(cmpContent, loadOptions),
55 | [],
56 | []
57 | )
58 | )
59 | );
60 | });
61 |
62 | it("should compare 1 to 2 with markdown format", () => {
63 | const loadOptions = { ignorePatterns: ["test-foundry/**/*"] };
64 | fs.writeFileSync(
65 | "tests/mocks/1-2.md",
66 | formatMarkdownDiff(
67 | "# Changes to gas cost",
68 | computeDiffs(
69 | loadReports(srcContent, loadOptions),
70 | loadReports(cmpContent, loadOptions),
71 | [],
72 | []
73 | ),
74 | "Rubilmax/foundry-gas-diff",
75 | "d62d23148ca73df77cd4378ee1b3c17f1f303dbf",
76 | undefined,
77 | 0.8
78 | )
79 | );
80 | });
81 |
82 | it("should compare 1 to 1 with markdown format", () => {
83 | const loadOptions = { ignorePatterns: ["test-foundry/**/*"] };
84 | fs.writeFileSync(
85 | "tests/mocks/1-1.md",
86 | formatMarkdownDiff(
87 | "# Changes to gas cost",
88 | computeDiffs(
89 | loadReports(srcContent, loadOptions),
90 | loadReports(srcContent, loadOptions),
91 | [],
92 | []
93 | ),
94 | "Rubilmax/foundry-gas-diff",
95 | "d62d23148ca73df77cd4378ee1b3c17f1f303dbf"
96 | )
97 | );
98 | });
99 |
100 | it("should compare 1 to 1 with shell format", () => {
101 | const loadOptions = { ignorePatterns: ["test-foundry/**/*"] };
102 | console.log(
103 | formatShellDiff(
104 | computeDiffs(
105 | loadReports(srcContent, loadOptions),
106 | loadReports(srcContent, loadOptions),
107 | [],
108 | []
109 | )
110 | )
111 | );
112 | });
113 | });
114 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "rootDir": "src"
5 | },
6 | "include": ["src"]
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "commonjs",
5 | "outDir": "lib",
6 | "rootDir": ".",
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "esModuleInterop": true
10 | },
11 | "include": ["src", "tests"]
12 | }
13 |
--------------------------------------------------------------------------------