├── .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 | image 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 | --------------------------------------------------------------------------------