Skip to content

Commit

Permalink
build: write Hardhat task for gas benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
CJ42 committed Sep 28, 2023
1 parent b846c77 commit ab9d17c
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 3 deletions.
3 changes: 3 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import '@nomicfoundation/hardhat-toolbox';
import 'hardhat-packager';
import 'hardhat-contract-sizer';
import 'hardhat-deploy';

// custom built hardhat plugins for CI
import './scripts/ci/docs-generate';
import './scripts/ci/gas_benchmark';

// Typescript types for web3.js
import '@nomiclabs/hardhat-web3';
Expand Down
48 changes: 48 additions & 0 deletions scripts/ci/gas_benchmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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) {

Check warning on line 14 in scripts/ci/gas_benchmark.ts

View workflow job for this annotation

GitHub Actions / build

'hre' is defined but never used

Check warning on line 14 in scripts/ci/gas_benchmark.ts

View workflow job for this annotation

GitHub Actions / build

'runSuper' is defined but never used
// TODO: WIP
const currentBenchmark = JSON.parse(fs.readFileSync(args.compare, 'utf8'));
const baseBenchmark = JSON.parse(fs.readFileSync(args.against, 'utf8'));

const casesExecute: Row[] = [];

for (const [key, value] of Object.entries(currentBenchmark['runtime_costs']['execute'])) {
const gasDiffMainController =
value['main_controller'] -
baseBenchmark['runtime_costs']['execute'][key]['main_controller'];

const gasDiffRestrictedController =
value['restricted_controller'] -
baseBenchmark['runtime_costs']['execute'][key]['restricted_controller'];

casesExecute.push([
value['description'],
value['main_controller'] + ` (${gasDiffMainController})`,
value['restricted_controller'] + ` (${gasDiffRestrictedController})`,
]);
}

const generatedTable = getMarkdownTable({
table: {
head: ['`execute` scenarios', '👑 main controller', '🛃 restricted controller'],
body: casesExecute,
},
alignment: [Align.Left],
});

const file = 'new_gas_benchmark.md';

fs.writeFileSync(file, generatedTable);
});
90 changes: 90 additions & 0 deletions scripts/ci/gas_benchmark_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"deployment_costs": {
"Universal Profile": "",
"Key Manager": "",
"LSP1 Delegate UP": "",
"LSP7 Digital Asset": "",
"LSP8 Identifiable Digital Asset": ""
},
"runtime_costs": {
"execute": {
"case_1": {
"description": "LYX transfer --> to an EOA",
"main_controller": "",
"restricted_controller": ""
},
"case_2": {
"description": "LYX transfer --> to a UP",
"main_controller": "",
"restricted_controller": ""
},
"case_3": {
"description": "LSP7 token transfer --> to an EOA",
"main_controller": "",
"restricted_controller": ""
},
"case_4": {
"description": "LSP7 token transfer --> to a UP",
"main_controller": "",
"restricted_controller": ""
},
"case_5": {
"description": "LSP8 NFT transfer --> to an EOA",
"main_controller": "",
"restricted_controller": ""
},
"case_6": {
"description": "LSP8 NFT transfer --> to a UP",
"main_controller": "",
"restricted_controller": ""
}
},
"setData": {
"case_1": {
"description": "Update Profile details (LSP3Profile Metadata)",
"main_controller": "",
"restricted_controller": ""
},
"case_2": {
"description": "Add a new controller with permission to `SET_DATA` + 3x allowed data keys: <br/> `AddressPermissions[]` <br/> + `AddressPermissions[index]` <br/> + `AddressPermissions:Permissions:<controller>` <br/> + `AddressPermissions:AllowedERC725YDataKeys:<controller`)",
"main_controller": "",
"restricted_controller": ""
},
"case_3": {
"description": "Update permissions of previous controller. Allow it now to `SUPER_SETDATA`",
"main_controller": "",
"restricted_controller": ""
},
"case_4": {
"description": "Remove a controller: <br/> 1. decrease `AddressPermissions[]` Array length <br/> 2. remove the controller address at `AddressPermissions[index]` <br/> 3. set \"0x\" for the controller permissions under AddressPermissions:Permissions:<controller-address>",
"main_controller": "",
"restricted_controller": ""
},
"case_5": {
"description": "Write 5x new LSP12 Issued Assets",
"main_controller": "",
"restricted_controller": ""
},
"case_6": {
"description": "Update 3x data keys (first 3)",
"main_controller": "",
"restricted_controller": ""
},
"case_7": {
"description": "Update 3x data keys (middle 3)",
"main_controller": "",
"restricted_controller": ""
},
"case_8": {
"description": "Update 3x data keys (last 3)",
"main_controller": "",
"restricted_controller": ""
},
"case_9": {
"description": "Set 2 x new data keys + add 3x new controllers",
"main_controller": "",
"restricted_controller": ""
}
}
}
}
69 changes: 66 additions & 3 deletions tests/Benchmark.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,18 @@ describe('⛽📊 Gas Benchmark', () => {
});

describe('KeyManager', () => {
let gasBenchmark;

before('setup benchmark file', async () => {
gasBenchmark = JSON.parse(
fs.readFileSync('./scripts/ci/gas_benchmark_template.json', 'utf8'),
);
});

after(async () => {
fs.writeFileSync('gas_benchmark_result.json', JSON.stringify(gasBenchmark, null, 2));
});

describe('`execute(...)` via Key Manager', () => {
describe('main controller (this browser extension)', () => {
const casesExecuteMainController: Row[] = [];
Expand Down Expand Up @@ -718,6 +730,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfer LYX to an EOA',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_1']['main_controller'] =
receipt.gasUsed.toNumber();
});

it('transfers some LYXes to a UP', async () => {
Expand All @@ -731,6 +746,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfer LYX to a UP',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_2']['main_controller'] =
receipt.gasUsed.toNumber();
});

it('transfers some tokens (LSP7) to an EOA (no data)', async () => {
Expand All @@ -755,6 +773,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfer tokens (LSP7) to an EOA (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_3']['main_controller'] =
receipt.gasUsed.toNumber();
});

it('transfer some tokens (LSP7) to a UP (no data)', async () => {
Expand All @@ -779,6 +800,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfer tokens (LSP7) to a UP (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_4']['main_controller'] =
receipt.gasUsed.toNumber();
});

it('transfer a NFT (LSP8) to a EOA (no data)', async () => {
Expand All @@ -803,6 +827,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfer a NFT (LSP8) to a EOA (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_5']['main_controller'] =
receipt.gasUsed.toNumber();
});

it('transfer a NFT (LSP8) to a UP (no data)', async () => {
Expand All @@ -827,6 +854,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfer a NFT (LSP8) to a UP (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_6']['main_controller'] =
receipt.gasUsed.toNumber();
});

after(async () => {
Expand Down Expand Up @@ -944,7 +974,7 @@ describe('⛽📊 Gas Benchmark', () => {
PERMISSIONS.TRANSFERVALUE,
PERMISSIONS.CALL,
PERMISSIONS.CALL,
combineAllowedCalls([CALLTYPE.VALUE], [allowedAddressToTransferValue], ["0xffffffff"], ["0xffffffff"]),
combineAllowedCalls([CALLTYPE.VALUE, CALLTYPE.VALUE], [allowedAddressToTransferValue, aliceUP.address], ["0xffffffff", "0xffffffff"], ["0xffffffff", "0xffffffff"]),
combineAllowedCalls(
[CALLTYPE.CALL, CALLTYPE.CALL],
[lsp7MetaCoin.address, lsp7LyxDai.address],
Expand All @@ -961,7 +991,7 @@ describe('⛽📊 Gas Benchmark', () => {
)
});

it('transfer some LYXes to an EOA - restricted to 1 x allowed address only (TRANSFERVALUE + 1x AllowedCalls)', async () => {
it('transfer some LYXes to an EOA - restricted to 2 x allowed address only (TRANSFERVALUE + 2x AllowedCalls)', async () => {
const lyxAmount = 10;

const tx = await context.universalProfile
Expand All @@ -970,9 +1000,30 @@ describe('⛽📊 Gas Benchmark', () => {
const receipt = await tx.wait();

casesExecuteRestrictedController.push([
'transfer some LYXes to an EOA - restricted to 1 x allowed address only (TRANSFERVALUE + 1x AllowedCalls)',
'transfer some LYXes to an EOA - restricted to 2 x allowed address only (an EOA + a UP) (TRANSFERVALUE + 2x AllowedCalls)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_1']['restricted_controller'] =
receipt.gasUsed.toNumber();
});

it('transfer some LYXes to a UP - restricted to 2 x allowed address only (an EOA + a UP) (TRANSFERVALUE + 2x AllowedCalls)', async () => {
// ...
const lyxAmount = 10;

const tx = await context.universalProfile
.connect(canTransferValueToOneAddress)
.execute(OPERATION_TYPES.CALL, aliceUP.address, lyxAmount, '0x');
const receipt = await tx.wait();

casesExecuteRestrictedController.push([
'transfer some LYXes to a UP - restricted to 2 x allowed address only (an EOA + a UP) (TRANSFERVALUE + 2x AllowedCalls)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_2']['restricted_controller'] =
receipt.gasUsed.toNumber();
});

it('transfers some tokens (LSP7) to an EOA - restricted to LSP7 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)', async () => {
Expand All @@ -997,6 +1048,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfers some tokens (LSP7) to an EOA - restricted to LSP7 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_3']['restricted_controller'] =
receipt.gasUsed.toNumber();
});

it('transfers some tokens (LSP7) to an other UP - restricted to LSP7 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)', async () => {
Expand All @@ -1021,6 +1075,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfers some tokens (LSP7) to an other UP - restricted to LSP7 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_4']['restricted_controller'] =
receipt.gasUsed.toNumber();
});

it('transfers a NFT (LSP8) to an EOA - restricted to LSP8 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)', async () => {
Expand All @@ -1045,6 +1102,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfers a NFT (LSP8) to an EOA - restricted to LSP8 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_5']['restricted_controller'] =
receipt.gasUsed.toNumber();
});

it('transfers a NFT (LSP8) to an other UP - restricted to LSP8 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)', async () => {
Expand All @@ -1069,6 +1129,9 @@ describe('⛽📊 Gas Benchmark', () => {
'transfers a NFT (LSP8) to an other UP - restricted to LSP8 + 2x allowed contracts only (CALL + 2x AllowedCalls) (no data)',
receipt.gasUsed.toNumber().toString(),
]);

gasBenchmark['runtime_costs']['execute']['case_6']['restricted_controller'] =
receipt.gasUsed.toNumber();
});

after(async () => {
Expand Down

0 comments on commit ab9d17c

Please sign in to comment.