-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build: write Hardhat task for gas benchmark
- Loading branch information
Showing
4 changed files
with
1,006 additions
and
340 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
import fs from 'fs'; | ||
import { task } from 'hardhat/config'; | ||
import { Align, getMarkdownTable, Row } from 'markdown-table-ts'; | ||
|
||
task('gas-benchmark', 'Benchmark gas usage of the smart contracts based on predefined scenarios') | ||
.addParam( | ||
'compare', | ||
'The `.json` file that contains the gas costs of the currently compiled contracts (e.g: current working branch)', | ||
) | ||
.addParam( | ||
'against', | ||
'The `.json` file that contains the gas costs to compare against (e.g: the `develop` branch)', | ||
) | ||
.setAction(async function (args, hre, runSuper) { | ||
const currentBenchmark = JSON.parse(fs.readFileSync(args.compare, 'utf8')); | ||
const baseBenchmark = JSON.parse(fs.readFileSync(args.against, 'utf8')); | ||
|
||
const casesEOAExecute: Row[] = []; | ||
const casesEOASetData: Row[] = []; | ||
const casesEOAToken: Row[] = []; | ||
|
||
const casesExecute: Row[] = []; | ||
const casesSetData: Row[] = []; | ||
|
||
const numberWithCommas = (value) => { | ||
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); | ||
}; | ||
|
||
const displayGasDiffText = (gasDiff) => { | ||
const gasDiffNumber = parseInt(gasDiff); | ||
|
||
let emoji; | ||
|
||
if (gasDiffNumber === 0) { | ||
emoji = ''; | ||
} else { | ||
emoji = parseInt(gasDiff) < 0 ? '✅' : '❌'; | ||
} | ||
|
||
return ` ( ${numberWithCommas(gasDiff)} ${emoji})`; | ||
}; | ||
|
||
// EOA - Execute | ||
for (const [key, value] of Object.entries( | ||
currentBenchmark['runtime_costs']['EOA_owner']['execute'], | ||
)) { | ||
const gasDiff = | ||
value['gas_cost'] - baseBenchmark['runtime_costs']['EOA_owner']['execute'][key]['gas_cost']; | ||
|
||
casesEOAExecute.push([ | ||
value['description'], | ||
numberWithCommas(value['gas_cost']) + displayGasDiffText(gasDiff), | ||
]); | ||
} | ||
|
||
const generatedExecuteEOATable = getMarkdownTable({ | ||
table: { | ||
head: ['`execute` scenarios - UP Owner as EOA 🔑', '⛽ Gas change'], | ||
body: casesEOAExecute, | ||
}, | ||
alignment: [Align.Left], | ||
}); | ||
|
||
// EOA - Set Data | ||
for (const [key, value] of Object.entries( | ||
currentBenchmark['runtime_costs']['EOA_owner']['setData'], | ||
)) { | ||
const gasDiff = | ||
value['gas_cost'] - baseBenchmark['runtime_costs']['EOA_owner']['setData'][key]['gas_cost']; | ||
|
||
casesEOASetData.push([ | ||
value['description'], | ||
numberWithCommas(value['gas_cost']) + displayGasDiffText(gasDiff), | ||
]); | ||
} | ||
|
||
const generatedSetDataEOATable = getMarkdownTable({ | ||
table: { | ||
head: ['`setData` scenarios - UP Owner as EOA 🔑', '⛽ Gas change'], | ||
body: casesEOASetData, | ||
}, | ||
alignment: [Align.Left], | ||
}); | ||
|
||
// EOA - Token | ||
for (const [key, value] of Object.entries( | ||
currentBenchmark['runtime_costs']['EOA_owner']['tokens'], | ||
)) { | ||
const gasDiff = | ||
value['gas_cost'] - baseBenchmark['runtime_costs']['EOA_owner']['tokens'][key]['gas_cost']; | ||
|
||
casesEOAToken.push([ | ||
value['description'], | ||
numberWithCommas(value['gas_cost']) + displayGasDiffText(gasDiff), | ||
]); | ||
} | ||
|
||
const generatedTokenEOATable = getMarkdownTable({ | ||
table: { | ||
head: ['`execute` scenarios - UP Owner as EOA 🔑', '⛽ Gas change'], | ||
body: casesEOAToken, | ||
}, | ||
alignment: [Align.Left], | ||
}); | ||
|
||
// Key Manager - Execute | ||
for (const [key, value] of Object.entries( | ||
currentBenchmark['runtime_costs']['KeyManager_owner']['execute'], | ||
)) { | ||
const gasDiffMainController = | ||
value['main_controller'] - | ||
baseBenchmark['runtime_costs']['KeyManager_owner']['execute'][key]['main_controller']; | ||
|
||
const gasDiffRestrictedController = | ||
value['restricted_controller'] - | ||
baseBenchmark['runtime_costs']['KeyManager_owner']['execute'][key]['restricted_controller']; | ||
|
||
casesExecute.push([ | ||
value['description'], | ||
numberWithCommas(value['main_controller']) + displayGasDiffText(gasDiffMainController), | ||
numberWithCommas(value['restricted_controller']) + | ||
displayGasDiffText(gasDiffRestrictedController), | ||
]); | ||
} | ||
|
||
const generatedExecuteTable = getMarkdownTable({ | ||
table: { | ||
head: ['`execute` scenarios', '👑 main controller', '🛃 restricted controller'], | ||
body: casesExecute, | ||
}, | ||
alignment: [Align.Left], | ||
}); | ||
|
||
// Key Manager - Set Data | ||
for (const [key, value] of Object.entries( | ||
currentBenchmark['runtime_costs']['KeyManager_owner']['setData'], | ||
)) { | ||
const gasDiffMainController = | ||
value['main_controller'] - | ||
baseBenchmark['runtime_costs']['KeyManager_owner']['setData'][key]['main_controller']; | ||
|
||
const gasDiffRestrictedController = | ||
value['restricted_controller'] - | ||
baseBenchmark['runtime_costs']['KeyManager_owner']['setData'][key]['restricted_controller']; | ||
|
||
casesSetData.push([ | ||
value['description'], | ||
numberWithCommas(value['main_controller']) + displayGasDiffText(gasDiffMainController), | ||
numberWithCommas(value['restricted_controller']) + | ||
displayGasDiffText(gasDiffRestrictedController), | ||
]); | ||
} | ||
|
||
const generatedSetDataTable = getMarkdownTable({ | ||
table: { | ||
head: ['`setData` scenarios', '👑 main controller', '🛃 restricted controller'], | ||
body: casesSetData, | ||
}, | ||
alignment: [Align.Left], | ||
}); | ||
|
||
const file = 'gas_benchmark.md'; | ||
|
||
const markdown = ` | ||
👋 Hello | ||
⛽ I am the Gas Bot Reporter. I keep track of the gas costs of common interactions using Universal Profiles 🆙 ! | ||
📊 Here is a summary of the gas cost with the code introduced by this PR. | ||
<details> | ||
<summary>⛽📊 See Gas Benchmark report of Using UniversalProfile owned by an LSP6KeyManager</summary> | ||
This document contains the gas usage for common interactions and scenarios when using UniversalProfile smart contracts. | ||
> **Note:** the \`execute\` and \`setData\` scenarios are executed on a fresh UniversalProfile and LSP6KeyManager smart contracts, deployed as standard contracts (not as proxy behind a base contract implementation). | ||
### 🔀 \`execute\` scenarios | ||
${generatedExecuteTable} | ||
### 🗄️ \`setData\` scenarios | ||
${generatedSetDataTable} | ||
</details> | ||
<details> | ||
<summary>⛽📊 See Gas Benchmark report of Using UniversalProfile owned by an LSP6KeyManager</summary> | ||
### 🔀 \`execute\` scenarios | ||
${generatedExecuteEOATable} | ||
### 🗄️ \`setData\` scenarios | ||
${generatedSetDataEOATable} | ||
### 🗄️ \`Tokens\` scenarios | ||
${generatedTokenEOATable} | ||
</details> | ||
`; | ||
|
||
fs.writeFileSync(file, markdown); | ||
}); |
Oops, something went wrong.