From 53154603ac1103353db34134d1c35da7026cde44 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Sat, 17 Jun 2023 18:30:44 -0700 Subject: [PATCH 01/11] copy over vulcan gas report strategy --- packages/store/test/Gas.t.sol | 9 +++++++-- packages/store/test/StoreTest.sol | 33 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 packages/store/test/StoreTest.sol diff --git a/packages/store/test/Gas.t.sol b/packages/store/test/Gas.t.sol index ae5e0842b2..272eaa657a 100644 --- a/packages/store/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { console } from "forge-std/Test.sol"; +import { StoreTest } from "./StoreTest.sol"; import { Bytes } from "../src/Bytes.sol"; import { SliceLib } from "../src/Slice.sol"; import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; @@ -17,7 +18,7 @@ contract SomeContract { function doSomethingWithBytes(bytes memory data) public {} } -contract GasTest is Test { +contract GasTest is StoreTest { SomeContract someContract = new SomeContract(); function testCompareAbiEncodeVsCustom() public { @@ -27,10 +28,14 @@ contract GasTest is Test { mixed.a32[2] = 3; // !gasreport abi encode + startGasReport("abi encode"); bytes memory abiEncoded = abi.encode(mixed); + endGasReport(); // !gasreport abi decode + startGasReport("abi decode"); Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed)); + endGasReport(); // !gasreport custom encode bytes memory customEncoded = customEncode(mixed); diff --git a/packages/store/test/StoreTest.sol b/packages/store/test/StoreTest.sol new file mode 100644 index 0000000000..8f9dcb6b60 --- /dev/null +++ b/packages/store/test/StoreTest.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Test, console } from "forge-std/Test.sol"; + +contract StoreTest is Test { + function startGasReport(string memory name) internal { + if (bytes(name).length > 32) { + revert("ctx.startGasReport: Gas report name can't have more than 32 characters"); + } + bytes32 b32Name = bytes32(bytes(name)); + bytes32 slot = keccak256(bytes("mud.gasReport.name")); + vm.store(address(vm), slot, b32Name); + bytes32 valueSlot = keccak256(abi.encodePacked("mud.gasReport", b32Name)); + // warm the slot + vm.store(address(vm), valueSlot, bytes32(type(uint256).max)); + vm.pauseGasMetering(); + vm.store(address(vm), valueSlot, bytes32(gasleft())); + vm.resumeGasMetering(); + } + + function endGasReport() internal view { + uint256 gas = gasleft(); + bytes32 slot = keccak256(bytes("mud.gasReport.name")); + bytes32 b32Name = vm.load(address(vm), slot); + bytes32 valueSlot = keccak256(abi.encodePacked("mud.gasReport", b32Name)); + uint256 prevGas = uint256(vm.load(address(vm), valueSlot)); + if (gas > prevGas) { + revert("ctx.endGasReport: Gas used can't have a negative value"); + } + console.log(string.concat("gas(", string(abi.encodePacked(b32Name)), "):", vm.toString(prevGas - gas))); + } +} From a5c74a8a94ad7c17f1f6918c10492b930b7109a1 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 10:57:49 +0100 Subject: [PATCH 02/11] simplify --- packages/store/test/StoreTest.sol | 32 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/store/test/StoreTest.sol b/packages/store/test/StoreTest.sol index 8f9dcb6b60..3fbd215300 100644 --- a/packages/store/test/StoreTest.sol +++ b/packages/store/test/StoreTest.sol @@ -4,30 +4,26 @@ pragma solidity >=0.8.0; import { Test, console } from "forge-std/Test.sol"; contract StoreTest is Test { + string private __currentGasReportName; + uint256 private __currentGasReportValue = gasleft(); + function startGasReport(string memory name) internal { - if (bytes(name).length > 32) { - revert("ctx.startGasReport: Gas report name can't have more than 32 characters"); - } - bytes32 b32Name = bytes32(bytes(name)); - bytes32 slot = keccak256(bytes("mud.gasReport.name")); - vm.store(address(vm), slot, b32Name); - bytes32 valueSlot = keccak256(abi.encodePacked("mud.gasReport", b32Name)); - // warm the slot - vm.store(address(vm), valueSlot, bytes32(type(uint256).max)); + __currentGasReportName = name; vm.pauseGasMetering(); - vm.store(address(vm), valueSlot, bytes32(gasleft())); + __currentGasReportValue = gasleft(); vm.resumeGasMetering(); } function endGasReport() internal view { uint256 gas = gasleft(); - bytes32 slot = keccak256(bytes("mud.gasReport.name")); - bytes32 b32Name = vm.load(address(vm), slot); - bytes32 valueSlot = keccak256(abi.encodePacked("mud.gasReport", b32Name)); - uint256 prevGas = uint256(vm.load(address(vm), valueSlot)); - if (gas > prevGas) { - revert("ctx.endGasReport: Gas used can't have a negative value"); - } - console.log(string.concat("gas(", string(abi.encodePacked(b32Name)), "):", vm.toString(prevGas - gas))); + uint256 gasUsed = __currentGasReportValue - gasleft(); + require(gasUsed > 0, "gas-report: gas used can't have a negative value, did you forget to call startGasReport?"); + console.log(string.concat("gas-report(", __currentGasReportName, "):", vm.toString(gasUsed))); + } + + modifier gasReport(string memory name) { + startGasReport(name); + _; + endGasReport(); } } From 971afaec7627226ac34bacf9774f4324394e7dbf Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 11:28:09 +0100 Subject: [PATCH 03/11] flesh it out a bit more --- packages/store/test/StoreTest.sol | 32 +++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/store/test/StoreTest.sol b/packages/store/test/StoreTest.sol index 3fbd215300..b7d9aef421 100644 --- a/packages/store/test/StoreTest.sol +++ b/packages/store/test/StoreTest.sol @@ -6,19 +6,33 @@ import { Test, console } from "forge-std/Test.sol"; contract StoreTest is Test { string private __currentGasReportName; uint256 private __currentGasReportValue = gasleft(); + mapping(string => uint256) private __gasReports; + string[] private __gasReportNames; function startGasReport(string memory name) internal { + require( + bytes(__currentGasReportName).length == 0, + string.concat( + 'gas report "', + __currentGasReportName, + '" is already running and only one report can be run at a time' + ) + ); + require(__gasReports[name] == 0, string.concat('gas report name "', name, '" already used')); __currentGasReportName = name; vm.pauseGasMetering(); __currentGasReportValue = gasleft(); vm.resumeGasMetering(); } - function endGasReport() internal view { + function endGasReport() internal { uint256 gas = gasleft(); - uint256 gasUsed = __currentGasReportValue - gasleft(); - require(gasUsed > 0, "gas-report: gas used can't have a negative value, did you forget to call startGasReport?"); - console.log(string.concat("gas-report(", __currentGasReportName, "):", vm.toString(gasUsed))); + uint256 gasUsed = __currentGasReportValue - gas; + require(gasUsed > 0, "no or negative gas used, did you forget to call startGasReport?"); + __gasReports[__currentGasReportName] = gasUsed; + __gasReportNames.push(__currentGasReportName); + printGasReport(__currentGasReportName); + __currentGasReportName = ""; } modifier gasReport(string memory name) { @@ -26,4 +40,14 @@ contract StoreTest is Test { _; endGasReport(); } + + function printGasReport(string memory name) internal view { + console.log(string.concat("GAS REPORT: ", vm.toString(__gasReports[name]), " ", name)); + } + + function printGasReports() internal view { + for (uint256 i = 0; i < __gasReportNames.length; i++) { + printGasReport(__gasReportNames[i]); + } + } } From e1917b776bc5923c72ae50d9c4d137c67892104a Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 11:36:05 +0100 Subject: [PATCH 04/11] move to std-contracts --- .../src/test/GasReporter.sol} | 2 +- packages/store/package.json | 1 + packages/store/test/Gas.t.sol | 6 +++--- pnpm-lock.yaml | 3 +++ 4 files changed, 8 insertions(+), 4 deletions(-) rename packages/{store/test/StoreTest.sol => std-contracts/src/test/GasReporter.sol} (98%) diff --git a/packages/store/test/StoreTest.sol b/packages/std-contracts/src/test/GasReporter.sol similarity index 98% rename from packages/store/test/StoreTest.sol rename to packages/std-contracts/src/test/GasReporter.sol index b7d9aef421..7a47fcd3cb 100644 --- a/packages/store/test/StoreTest.sol +++ b/packages/std-contracts/src/test/GasReporter.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import { Test, console } from "forge-std/Test.sol"; -contract StoreTest is Test { +contract GasReporter is Test { string private __currentGasReportName; uint256 private __currentGasReportValue = gasleft(); mapping(string => uint256) private __gasReports; diff --git a/packages/store/package.json b/packages/store/package.json index 4c5aa23ee2..a05e328c5b 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -62,6 +62,7 @@ "zod": "^3.21.4" }, "devDependencies": { + "@latticexyz/std-contracts": "workspace:*", "@typechain/ethers-v5": "^10.2.0", "@types/ejs": "^3.1.1", "@types/mocha": "^9.1.1", diff --git a/packages/store/test/Gas.t.sol b/packages/store/test/Gas.t.sol index 272eaa657a..41a32e7196 100644 --- a/packages/store/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/Test.sol"; -import { StoreTest } from "./StoreTest.sol"; +import { Test, console } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Bytes } from "../src/Bytes.sol"; import { SliceLib } from "../src/Slice.sol"; import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; @@ -18,7 +18,7 @@ contract SomeContract { function doSomethingWithBytes(bytes memory data) public {} } -contract GasTest is StoreTest { +contract GasTest is Test, GasReporter { SomeContract someContract = new SomeContract(); function testCompareAbiEncodeVsCustom() public { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72b2c939c8..b57a697d59 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -941,6 +941,9 @@ importers: specifier: ^3.21.4 version: 3.21.4 devDependencies: + '@latticexyz/std-contracts': + specifier: workspace:* + version: link:../std-contracts '@typechain/ethers-v5': specifier: ^10.2.0 version: 10.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.1.1)(typescript@4.9.5) From f8eb4dba87b979c7a2d318f12a9ad5a7758ed6cc Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 11:57:49 +0100 Subject: [PATCH 05/11] subtract constant gas, add gas reporter test --- .../std-contracts/src/test/GasReporter.sol | 9 +++++++-- .../ComponentGas.t.sol} | 18 +++++++++--------- packages/std-contracts/test/GasReporter.t.sol | 14 ++++++++++++++ .../test/SystemCallbackComponent.t.sol | 8 ++++---- packages/store/test/Gas.t.sol | 16 ++++++++-------- 5 files changed, 42 insertions(+), 23 deletions(-) rename packages/std-contracts/{src/test/GasComponent.t.sol => test/ComponentGas.t.sol} (90%) create mode 100644 packages/std-contracts/test/GasReporter.t.sol rename packages/std-contracts/{src => }/test/SystemCallbackComponent.t.sol (94%) diff --git a/packages/std-contracts/src/test/GasReporter.sol b/packages/std-contracts/src/test/GasReporter.sol index 7a47fcd3cb..ee668a7b68 100644 --- a/packages/std-contracts/src/test/GasReporter.sol +++ b/packages/std-contracts/src/test/GasReporter.sol @@ -27,8 +27,9 @@ contract GasReporter is Test { function endGasReport() internal { uint256 gas = gasleft(); - uint256 gasUsed = __currentGasReportValue - gas; - require(gasUsed > 0, "no or negative gas used, did you forget to call startGasReport?"); + // 160 is the gas used by the GasReporter itself + uint256 gasUsed = __currentGasReportValue - gas - 160; + require(gasUsed >= 0, "negative gas used, did you forget to call startGasReport?"); __gasReports[__currentGasReportName] = gasUsed; __gasReportNames.push(__currentGasReportName); printGasReport(__currentGasReportName); @@ -41,6 +42,10 @@ contract GasReporter is Test { endGasReport(); } + function getGasUsed(string memory name) internal view returns (uint256) { + return __gasReports[name]; + } + function printGasReport(string memory name) internal view { console.log(string.concat("GAS REPORT: ", vm.toString(__gasReports[name]), " ", name)); } diff --git a/packages/std-contracts/src/test/GasComponent.t.sol b/packages/std-contracts/test/ComponentGas.t.sol similarity index 90% rename from packages/std-contracts/src/test/GasComponent.t.sol rename to packages/std-contracts/test/ComponentGas.t.sol index c7604739f5..bd52295196 100644 --- a/packages/std-contracts/src/test/GasComponent.t.sol +++ b/packages/std-contracts/test/ComponentGas.t.sol @@ -5,15 +5,15 @@ import { Vm } from "forge-std/Vm.sol"; import { console } from "forge-std/console.sol"; import { World } from "solecs/World.sol"; -import { BoolComponent } from "../components/BoolComponent.sol"; -import { BoolBareComponent } from "../components/BoolBareComponent.sol"; -import { Uint256Component } from "../components/Uint256Component.sol"; -import { Uint256BareComponent } from "../components/Uint256BareComponent.sol"; -import { CoordComponent, Coord } from "../components/CoordComponent.sol"; -import { CoordBareComponent, Coord as BareCoord } from "../components/CoordBareComponent.sol"; - -import { TestComponent, TestStruct } from "./TestComponent.sol"; -import { TestBareComponent, TestStructBare } from "./TestBareComponent.sol"; +import { BoolComponent } from "../src/components/BoolComponent.sol"; +import { BoolBareComponent } from "../src/components/BoolBareComponent.sol"; +import { Uint256Component } from "../src/components/Uint256Component.sol"; +import { Uint256BareComponent } from "../src/components/Uint256BareComponent.sol"; +import { CoordComponent, Coord } from "../src/components/CoordComponent.sol"; +import { CoordBareComponent, Coord as BareCoord } from "../src/components/CoordBareComponent.sol"; + +import { TestComponent, TestStruct } from "../src/test/TestComponent.sol"; +import { TestBareComponent, TestStructBare } from "../src/test/TestBareComponent.sol"; contract ComponentTest is DSTestPlus { Vm internal immutable vm = Vm(HEVM_ADDRESS); diff --git a/packages/std-contracts/test/GasReporter.t.sol b/packages/std-contracts/test/GasReporter.t.sol new file mode 100644 index 0000000000..1d0eb14c16 --- /dev/null +++ b/packages/std-contracts/test/GasReporter.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "../src/test/GasReporter.sol"; + +contract GasReporterTest is Test, GasReporter { + function testGasReporterGas() public { + startGasReport("empty gas report"); + endGasReport(); + // An empty gas report should always result in zero gas. If not, update GasReporter's gasUsed calculation to make this zero. + assertEq(getGasUsed("empty gas report"), 0); + } +} diff --git a/packages/std-contracts/src/test/SystemCallbackComponent.t.sol b/packages/std-contracts/test/SystemCallbackComponent.t.sol similarity index 94% rename from packages/std-contracts/src/test/SystemCallbackComponent.t.sol rename to packages/std-contracts/test/SystemCallbackComponent.t.sol index f8df440727..36ee594fad 100644 --- a/packages/std-contracts/src/test/SystemCallbackComponent.t.sol +++ b/packages/std-contracts/test/SystemCallbackComponent.t.sol @@ -6,10 +6,10 @@ import { Vm } from "forge-std/Vm.sol"; import { console } from "forge-std/console.sol"; import { World } from "solecs/World.sol"; -import { Uint256BareComponent } from "../components/Uint256BareComponent.sol"; -import { TestSystem } from "./TestSystem.sol"; -import { SystemCallbackComponent } from "../components/SystemCallbackComponent.sol"; -import { SystemCallbackBareComponent, SystemCallback, executeSystemCallback } from "../components/SystemCallbackBareComponent.sol"; +import { Uint256BareComponent } from "../src/components/Uint256BareComponent.sol"; +import { TestSystem } from "../src/test/TestSystem.sol"; +import { SystemCallbackComponent } from "../src/components/SystemCallbackComponent.sol"; +import { SystemCallbackBareComponent, SystemCallback, executeSystemCallback } from "../src/components/SystemCallbackBareComponent.sol"; contract SystemCallbackComponentTest is DSTest { Vm internal immutable vm = Vm(HEVM_ADDRESS); diff --git a/packages/store/test/Gas.t.sol b/packages/store/test/Gas.t.sol index 41a32e7196..e5663afda4 100644 --- a/packages/store/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -27,29 +27,31 @@ contract GasTest is Test, GasReporter { mixed.a32[1] = 2; mixed.a32[2] = 3; - // !gasreport abi encode startGasReport("abi encode"); bytes memory abiEncoded = abi.encode(mixed); endGasReport(); - // !gasreport abi decode startGasReport("abi decode"); Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed)); endGasReport(); - // !gasreport custom encode + startGasReport("custom encode"); bytes memory customEncoded = customEncode(mixed); + endGasReport(); - // !gasreport custom decode + startGasReport("custom decode"); Mixed memory customDecoded = customDecode(customEncoded); + endGasReport(); console.log("Length comparison: abi encode %s, custom %s", abiEncoded.length, customEncoded.length); - // !gasreport pass abi encoded bytes to external contract + startGasReport("pass abi encoded bytes to external contract"); someContract.doSomethingWithBytes(abiEncoded); + endGasReport(); - // !gasreport pass custom encoded bytes to external contract + startGasReport("pass custom encoded bytes to external contract"); someContract.doSomethingWithBytes(customEncoded); + endGasReport(); assertEq(keccak256(abi.encode(abiDecoded)), keccak256(abi.encode(customDecoded))); } @@ -60,8 +62,6 @@ function customEncode(Mixed memory mixed) pure returns (bytes memory) { } function customDecode(bytes memory input) view returns (Mixed memory) { - console.log(input.length); - return Mixed({ u32: uint32(Bytes.slice4(input, 0)), From ac28fae85e6e5ce8fe2b793ddbbd8978a4c27e71 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 12:47:46 +0100 Subject: [PATCH 06/11] update gas report command --- packages/cli/package.json | 1 + packages/cli/src/commands/gas-report.ts | 146 ++--- packages/store/gas-report.json | 744 +----------------------- packages/store/package.json | 2 +- pnpm-lock.yaml | 15 + 5 files changed, 93 insertions(+), 815 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 6da0bbd994..6c94e5b3c4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,6 +51,7 @@ "nice-grpc-web": "^2.0.1", "openurl": "^1.1.1", "path": "^0.12.7", + "strip-ansi": "^7.1.0", "table": "^6.8.1", "throttle-debounce": "^5.0.0", "typechain": "^8.1.1", diff --git a/packages/cli/src/commands/gas-report.ts b/packages/cli/src/commands/gas-report.ts index 378dffc123..737b546a0c 100644 --- a/packages/cli/src/commands/gas-report.ts +++ b/packages/cli/src/commands/gas-report.ts @@ -1,8 +1,9 @@ import type { CommandModule } from "yargs"; -import { readFileSync, writeFileSync, rmSync } from "fs"; +import { readFileSync, writeFileSync } from "fs"; import { execa } from "execa"; import chalk from "chalk"; import { table, getBorderCharacters } from "table"; +import stripAnsi from "strip-ansi"; /** * Print the gas report to the console, save it to a file and compare it to a previous gas report if provided. @@ -30,17 +31,15 @@ type Options = { }; type GasReportEntry = { - source: string; + file: string; + test: string; name: string; - functionCall: string; gasUsed: number; prevGasUsed?: number; }; type GasReport = GasReportEntry[]; -const tempFileSuffix = "MudGasReport"; - const commandModule: CommandModule = { command: "gas-report", @@ -48,30 +47,19 @@ const commandModule: CommandModule = { builder(yargs) { return yargs.options({ - path: { type: "array", string: true, default: ["Gas.t.sol"], desc: "File containing the gas tests" }, save: { type: "string", desc: "Save the gas report to a file" }, compare: { type: "string", desc: "Compare to an existing gas report" }, }); }, - async handler({ path: files, save, compare }) { - const validFiles = files.filter((file) => file.endsWith(".t.sol")); - const tempFiles = await Promise.all(validFiles.map((file) => createGasReport(file))); - - process.once("SIGINT", () => { - console.log("caught sigint, deleting temp files"); - tempFiles.forEach((file) => rmSync(file)); - }); - + async handler({ save, compare }) { let gasReport: GasReport; try { gasReport = await runGasReport(); - } catch { + } catch (error) { + console.error(error); setTimeout(() => process.exit()); return; - } finally { - // Delete the temporary files - tempFiles.forEach((file) => rmSync(file)); } // If this gas report should be compared to an existing one, load the existing one @@ -80,9 +68,7 @@ const commandModule: CommandModule = { const compareGasReport: GasReport = JSON.parse(readFileSync(compare, "utf8")); // Merge the previous gas report with the new one gasReport = gasReport.map((entry) => { - const prevEntry = compareGasReport.find( - (e) => e.name === entry.name && e.functionCall === entry.functionCall - ); + const prevEntry = compareGasReport.find((e) => e.file === entry.file && e.name === entry.name); return { ...entry, prevGasUsed: prevEntry?.gasUsed }; }); } catch { @@ -103,66 +89,18 @@ const commandModule: CommandModule = { export default commandModule; -async function createGasReport(filename: string): Promise { - console.log("Creating gas report for", chalk.bold(filename)); - - // Parse the given test file, and add gas reporting wherever requested by a `// !gasreport` comment - const fileContents = readFileSync(filename, "utf8"); - let newFile = fileContents; - - // Use a regex to find first line of each function - const functionRegex = new RegExp(/function (.*){/g); - // Insert a line to declare the _gasreport variable at the start of each function - let functionMatch; - while ((functionMatch = functionRegex.exec(fileContents)) !== null) { - const functionSignature = functionMatch[0]; - newFile = newFile.replace(functionSignature, `${functionSignature}\nuint256 _gasreport;`); - } - - // A gasreport comment has a name (written after the comment) and a function call (written on the next line) - // Create a regex to extract both the name and the function call - const regex = new RegExp(/\/\/ !gasreport (.*)\n(.*)/g); - - // Apply the regex and loop through the matches, - // and create a new file with the gasreport comments replaced by the gas report - let match; - while ((match = regex.exec(fileContents)) !== null) { - const name = match[1]; - const functionCall = match[2].trim(); - - newFile = newFile.replace( - match[0], - ` -_gasreport = gasleft(); -${functionCall} -_gasreport = _gasreport - gasleft(); -console.log("GAS REPORT(${filename}): ${name} [${functionCall.replaceAll('"', '\\"')}]:", _gasreport);` - ); - } - - // Remove all occurrences of `pure` with `view` - newFile = newFile.replace(/pure/g, "view"); - - // Write the new file to disk (temporarily) - // Create the temp file by replacing the previous file name with MudGasReport - const tempFileName = filename.replace(/\.t\.sol$/, `${tempFileSuffix}.t.sol`); - writeFileSync(tempFileName, newFile); - - return tempFileName; -} - async function runGasReport(): Promise { console.log("Running gas report"); const gasReport: GasReport = []; // Extract the logs from the child process - let logs = ""; + let stdout: string; try { // Run the generated file using forge - const child = execa("forge", ["test", "--match-path", `*${tempFileSuffix}*`, "-vvv"], { + const child = execa("forge", ["test", "-vvv"], { stdio: ["inherit", "pipe", "inherit"], }); - logs = (await child).stdout; + stdout = (await child).stdout; } catch (error: any) { console.log(error.stdout ?? error); console.log(chalk.red("\n-----------\nError while running the gas report (see above)")); @@ -170,21 +108,53 @@ async function runGasReport(): Promise { } // Extract the gas reports from the logs + const lines = stdout.split("\n").map(stripAnsi); + const gasReportPattern = /^\s*GAS REPORT: (\d+) (.*)$/; + const testFunctionPattern = /^\[(?:PASS|FAIL).*\] (\w+)\(\)/; + const testFilePattern = /^Running \d+ tests? for (.*):(.*)$/; + + function nearestLine(pattern: RegExp, startIndex: number = lines.length - 1): number { + for (let i = startIndex; i >= 0; i--) { + const line = lines[i]; + if (pattern.test(line)) { + return i; + } + } + return -1; + } + + for (let i = 0; i < lines.length; i++) { + const matches = lines[i].match(gasReportPattern); + if (!matches) continue; + + const gasUsed = parseInt(matches[1]); + const name = matches[2]; + + const testFunctionLineIndex = nearestLine(testFunctionPattern, i); + if (testFunctionLineIndex === -1) { + throw new Error("Could not find nearest test function, did `forge test` output change?"); + } + const testFileLineIndex = nearestLine(testFilePattern, testFunctionLineIndex); + if (testFileLineIndex === -1) { + throw new Error("Could not find nearest test filename, did `forge test` output change?"); + } + + const functionMatches = lines[testFunctionLineIndex].match(testFunctionPattern); + if (!functionMatches) { + throw new Error("Could not parse test function name, did `forge test` output change?"); + } + const fileMatches = lines[testFileLineIndex].match(testFilePattern); + if (!fileMatches) { + throw new Error("Could not parse test filename, did `forge test` output change?"); + } + + const test = functionMatches[1]; + const file = fileMatches[1]; - // Create a regex to find all lines starting with `GAS REPORT:` and extract the name, function call and gas used - const gasReportRegex = new RegExp(/GAS REPORT\((.*)\): (.*) \[(.*)\]: (.*)/g); - - // Loop through the matches and print the gas report - let gasReportMatch; - while ((gasReportMatch = gasReportRegex.exec(logs)) !== null) { - const source = gasReportMatch[1]; - const name = gasReportMatch[2]; - const functionCall = gasReportMatch[3].replace(";", ""); - const gasUsed = parseInt(gasReportMatch[4]); - gasReport.push({ source, name, functionCall, gasUsed }); + gasReport.push({ file, test, name, gasUsed }); } - gasReport.sort((a, b) => a.source.localeCompare(b.source)); + gasReport.sort((a, b) => a.file.localeCompare(b.file)); return gasReport; } @@ -193,9 +163,9 @@ function printGasReport(gasReport: GasReport, compare?: string) { if (compare) console.log(chalk.bold(`Gas report compared to ${compare}`)); const headers = [ - chalk.bold("Source"), + chalk.bold("File"), + chalk.bold("Test"), chalk.bold("Name"), - chalk.bold("Function call"), chalk.bold("Gas used"), ...(compare ? [chalk.bold("Prev gas used"), chalk.bold("Difference")] : []), ]; @@ -205,7 +175,7 @@ function printGasReport(gasReport: GasReport, compare?: string) { const diffEntry = diff > 0 ? chalk.red(`+${diff}`) : diff < 0 ? chalk.green(`${diff}`) : diff; const compareColumns = compare ? [entry.prevGasUsed ?? "n/a", diffEntry] : []; const gasUsedEntry = diff > 0 ? chalk.red(entry.gasUsed) : diff < 0 ? chalk.green(entry.gasUsed) : entry.gasUsed; - return [entry.source, entry.name, entry.functionCall, gasUsedEntry, ...compareColumns]; + return [entry.file, entry.test, entry.name, gasUsedEntry, ...compareColumns]; }); const rows = [headers, ...values]; diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 7ca8be11cd..8e77fb77b1 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -1,746 +1,38 @@ [ { - "source": "test/Bytes.t.sol", - "name": "compare equal bytes", - "functionCall": "bool equals = Bytes.equals(a, b)", - "gasUsed": 202 - }, - { - "source": "test/Bytes.t.sol", - "name": "compare unequal bytes", - "functionCall": "bool equals = Bytes.equals(a, b)", - "gasUsed": 202 - }, - { - "source": "test/Bytes.t.sol", - "name": "set bytes1 in bytes32", - "functionCall": "Bytes.setBytes1(input, 8, 0xff)", - "gasUsed": 7 - }, - { - "source": "test/Bytes.t.sol", - "name": "set bytes2 in bytes32", - "functionCall": "Bytes.setBytes2(input, 8, 0xffff)", - "gasUsed": 7 - }, - { - "source": "test/Bytes.t.sol", - "name": "set bytes4 in bytes32", - "functionCall": "Bytes.setBytes4(input, 8, 0xffffffff)", - "gasUsed": 7 - }, - { - "source": "test/Bytes.t.sol", - "name": "set bytes4 in bytes memory", - "functionCall": "bytes memory result = Bytes.setBytes4(input, 0, overwrite)", - "gasUsed": 43 - }, - { - "source": "test/Bytes.t.sol", - "name": "slice bytes3 with offset 1", - "functionCall": "bytes3 b = Bytes.slice3(a, 1)", - "gasUsed": 77 - }, - { - "source": "test/Bytes.t.sol", - "name": "slice bytes32 with offset 10", - "functionCall": "bytes32 output = Bytes.slice32(input, 10)", - "gasUsed": 74 - }, - { - "source": "test/Bytes.t.sol", - "name": "create bytes32 from bytes memory with offset 0", - "functionCall": "bytes32 output = Bytes.toBytes32(input, 0)", - "gasUsed": 22 - }, - { - "source": "test/Bytes.t.sol", - "name": "create bytes32 from bytes memory with offset 16", - "functionCall": "bytes32 output = Bytes.toBytes32(input, 16)", - "gasUsed": 22 - }, - { - "source": "test/Gas.t.sol", + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", "name": "abi encode", - "functionCall": "bytes memory abiEncoded = abi.encode(mixed)", - "gasUsed": 969 + "gasUsed": 956 }, { - "source": "test/Gas.t.sol", + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", "name": "abi decode", - "functionCall": "Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed))", - "gasUsed": 1755 + "gasUsed": 1745 }, { - "source": "test/Gas.t.sol", + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", "name": "custom encode", - "functionCall": "bytes memory customEncoded = customEncode(mixed)", - "gasUsed": 1826 + "gasUsed": 1811 }, { - "source": "test/Gas.t.sol", + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", "name": "custom decode", - "functionCall": "Mixed memory customDecoded = customDecode(customEncoded)", - "gasUsed": 2446 + "gasUsed": 2123 }, { - "source": "test/Gas.t.sol", + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", "name": "pass abi encoded bytes to external contract", - "functionCall": "someContract.doSomethingWithBytes(abiEncoded)", - "gasUsed": 6554 + "gasUsed": 6549 }, { - "source": "test/Gas.t.sol", + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", "name": "pass custom encoded bytes to external contract", - "functionCall": "someContract.doSomethingWithBytes(customEncoded)", - "gasUsed": 1381 - }, - { - "source": "test/KeyEncoding.t.sol", - "name": "register KeyEncoding schema", - "functionCall": "KeyEncoding.registerSchema()", - "gasUsed": 64695 - }, - { - "source": "test/Mixed.t.sol", - "name": "store Mixed struct in storage (native solidity)", - "functionCall": "testMixed = mixed", - "gasUsed": 92050 - }, - { - "source": "test/Mixed.t.sol", - "name": "register Mixed schema", - "functionCall": "Mixed.registerSchema()", - "gasUsed": 61228 - }, - { - "source": "test/Mixed.t.sol", - "name": "set record in Mixed", - "functionCall": "Mixed.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })", - "gasUsed": 112165 - }, - { - "source": "test/Mixed.t.sol", - "name": "get record from Mixed", - "functionCall": "MixedData memory mixed = Mixed.get(key)", - "gasUsed": 13460 - }, - { - "source": "test/PackedCounter.t.sol", - "name": "get value at index of PackedCounter", - "functionCall": "packedCounter.atIndex(3)", - "gasUsed": 261 - }, - { - "source": "test/PackedCounter.t.sol", - "name": "set value at index of PackedCounter", - "functionCall": "packedCounter = packedCounter.setAtIndex(2, 5)", - "gasUsed": 799 - }, - { - "source": "test/PackedCounter.t.sol", - "name": "pack uint40 array into PackedCounter", - "functionCall": "PackedCounter packedCounter = PackedCounterLib.pack(counters)", - "gasUsed": 2152 - }, - { - "source": "test/PackedCounter.t.sol", - "name": "get total of PackedCounter", - "functionCall": "packedCounter.total()", - "gasUsed": 33 - }, - { - "source": "test/Schema.t.sol", - "name": "get schema type at index", - "functionCall": "SchemaType schemaType1 = schema.atIndex(0)", - "gasUsed": 191 - }, - { - "source": "test/Schema.t.sol", - "name": "get number of dynamic fields from schema", - "functionCall": "uint256 num = schema.numDynamicFields()", - "gasUsed": 80 - }, - { - "source": "test/Schema.t.sol", - "name": "get number of static fields from schema", - "functionCall": "uint256 num = schema.numStaticFields()", - "gasUsed": 91 - }, - { - "source": "test/Schema.t.sol", - "name": "get static data length from schema", - "functionCall": "uint256 length = schema.staticDataLength()", - "gasUsed": 39 - }, - { - "source": "test/Schema.t.sol", - "name": "check if schema is empty (non-empty schema)", - "functionCall": "bool empty = encodedSchema.isEmpty()", - "gasUsed": 13 - }, - { - "source": "test/Schema.t.sol", - "name": "check if schema is empty (empty schema)", - "functionCall": "bool empty = encodedSchema.isEmpty()", - "gasUsed": 13 - }, - { - "source": "test/Schema.t.sol", - "name": "validate schema", - "functionCall": "encodedSchema.validate({ allowEmpty: false })", - "gasUsed": 18272 - }, - { - "source": "test/Slice.t.sol", - "name": "make Slice from bytes", - "functionCall": "Slice slice = SliceLib.fromBytes(data)", - "gasUsed": 40 - }, - { - "source": "test/Slice.t.sol", - "name": "get Slice length", - "functionCall": "slice.length()", - "gasUsed": 7 - }, - { - "source": "test/Slice.t.sol", - "name": "get Slice pointer", - "functionCall": "slice.pointer()", - "gasUsed": 33 - }, - { - "source": "test/Slice.t.sol", - "name": "subslice bytes (no copy) [1:4]", - "functionCall": "Slice slice1 = SliceLib.getSubslice(data, 1, 1 + 3)", - "gasUsed": 317 - }, - { - "source": "test/Slice.t.sol", - "name": "subslice bytes (no copy) [4:37]", - "functionCall": "Slice slice2 = SliceLib.getSubslice(data, 4, 4 + 33)", - "gasUsed": 317 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice (0 bytes) to bytes memory", - "functionCall": "bytes memory sliceData0 = slice.toBytes()", - "gasUsed": 610 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice (2 bytes) to bytes memory", - "functionCall": "bytes memory sliceData2 = slice.toBytes()", - "gasUsed": 644 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice (32 bytes) to bytes memory", - "functionCall": "bytes memory sliceData32 = slice.toBytes()", - "gasUsed": 644 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice (34 bytes) to bytes memory", - "functionCall": "bytes memory sliceData34 = slice.toBytes()", - "gasUsed": 722 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice (1024 bytes) to bytes memory", - "functionCall": "bytes memory sliceData1024 = slice.toBytes()", - "gasUsed": 4979 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice (1024x1024 bytes) to bytes memory", - "functionCall": "bytes memory sliceData1024x1024 = slice.toBytes()", - "gasUsed": 6606528 - }, - { - "source": "test/Slice.t.sol", - "name": "Slice to bytes32", - "functionCall": "bytes32 output = slice.toBytes32()", - "gasUsed": 87 - }, - { - "source": "test/Storage.t.sol", - "name": "store 1 storage slot", - "functionCall": "Storage.store({ storagePointer: storagePointer, data: originalDataFirstSlot })", - "gasUsed": 22509 - }, - { - "source": "test/Storage.t.sol", - "name": "store 34 bytes over 3 storage slots (with offset and safeTrail))", - "functionCall": "Storage.store({ storagePointer: storagePointer, offset: 31, data: data1 })", - "gasUsed": 23164 - }, - { - "source": "test/Storage.t.sol", - "name": "load 34 bytes over 3 storage slots (with offset and safeTrail))", - "functionCall": "bytes memory data = Storage.load({ storagePointer: storagePointer, length: 34, offset: 31 })", - "gasUsed": 1108 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field slice (cold, 1 slot)", - "functionCall": "bytes memory secondFieldSlice = StoreCore.getFieldSlice(_table, _key, 1, schema, 0, 4)", - "gasUsed": 8161 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field slice (warm, 1 slot)", - "functionCall": "secondFieldSlice = StoreCore.getFieldSlice(_table, _key, 1, schema, 4, 8)", - "gasUsed": 2188 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field slice (semi-cold, 1 slot)", - "functionCall": "bytes memory thirdFieldSlice = StoreCore.getFieldSlice(_table, _key, 2, schema, 4, 32)", - "gasUsed": 4193 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field slice (warm, 2 slots)", - "functionCall": "thirdFieldSlice = StoreCore.getFieldSlice(_table, _key, 2, schema, 8, 40)", - "gasUsed": 4475 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field length (cold, 1 slot)", - "functionCall": "uint256 length = StoreCore.getFieldLength(_table, _key, 1, schema)", - "gasUsed": 7968 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field length (warm, 1 slot)", - "functionCall": "length = StoreCore.getFieldLength(_table, _key, 1, schema)", - "gasUsed": 1964 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field length (warm due to , 2 slots)", - "functionCall": "uint256 length = StoreCore.getFieldLength(_table, _key, 2, schema)", - "gasUsed": 7968 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "get field length (warm, 2 slots)", - "functionCall": "length = StoreCore.getFieldLength(_table, _key, 2, schema)", - "gasUsed": 1964 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "pop from field (cold, 1 slot, 1 uint32 item)", - "functionCall": "StoreCore.popFromField(_table, _key, 1, byteLengthToPop)", - "gasUsed": 29270 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "pop from field (warm, 1 slot, 1 uint32 item)", - "functionCall": "StoreCore.popFromField(_table, _key, 1, byteLengthToPop)", - "gasUsed": 19348 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "pop from field (cold, 2 slots, 10 uint32 items)", - "functionCall": "StoreCore.popFromField(_table, _key, 2, byteLengthToPop)", - "gasUsed": 31149 - }, - { - "source": "test/StoreCoreDynamic.t.sol", - "name": "pop from field (warm, 2 slots, 10 uint32 items)", - "functionCall": "StoreCore.popFromField(_table, _key, 2, byteLengthToPop)", - "gasUsed": 19231 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "access non-existing record", - "functionCall": "StoreCore.getRecord(table, key)", - "gasUsed": 7310 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "access static field of non-existing record", - "functionCall": "StoreCore.getField(table, key, 0)", - "gasUsed": 2982 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "access dynamic field of non-existing record", - "functionCall": "StoreCore.getField(table, key, 1)", - "gasUsed": 3593 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "access length of dynamic field of non-existing record", - "functionCall": "StoreCore.getFieldLength(table, key, 1, schema)", - "gasUsed": 1331 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "access slice of dynamic field of non-existing record", - "functionCall": "StoreCore.getFieldSlice(table, key, 1, schema, 0, 0)", - "gasUsed": 1331 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "delete record (complex data, 3 slots)", - "functionCall": "StoreCore.deleteRecord(table, key)", - "gasUsed": 11001 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "Check for existence of table (existent)", - "functionCall": "StoreCore.hasTable(table)", - "gasUsed": 1111 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "check for existence of table (non-existent)", - "functionCall": "StoreCore.hasTable(table2)", - "gasUsed": 3138 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "register subscriber", - "functionCall": "StoreCore.registerStoreHook(table, subscriber)", - "gasUsed": 65566 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set record on table with subscriber", - "functionCall": "StoreCore.setRecord(table, key, data)", - "gasUsed": 73972 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set static field on table with subscriber", - "functionCall": "StoreCore.setField(table, key, 0, data)", - "gasUsed": 31960 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "delete record on table with subscriber", - "functionCall": "StoreCore.deleteRecord(table, key)", - "gasUsed": 24553 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "register subscriber", - "functionCall": "StoreCore.registerStoreHook(table, subscriber)", - "gasUsed": 65566 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set (dynamic) record on table with subscriber", - "functionCall": "StoreCore.setRecord(table, key, data)", - "gasUsed": 167409 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set (dynamic) field on table with subscriber", - "functionCall": "StoreCore.setField(table, key, 1, arrayDataBytes)", - "gasUsed": 34927 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "delete (dynamic) record on table with subscriber", - "functionCall": "StoreCore.deleteRecord(table, key)", - "gasUsed": 26059 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "push to field (1 slot, 1 uint32 item)", - "functionCall": "StoreCore.pushToField(table, key, 1, secondDataToPush)", - "gasUsed": 17072 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "push to field (2 slots, 10 uint32 items)", - "functionCall": "StoreCore.pushToField(table, key, 2, thirdDataToPush)", - "gasUsed": 39780 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "StoreCore: register schema", - "functionCall": "StoreCore.registerSchema(table, schema, keySchema)", - "gasUsed": 54877 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "StoreCore: get schema (warm)", - "functionCall": "StoreCore.getSchema(table)", - "gasUsed": 1136 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "StoreCore: get key schema (warm)", - "functionCall": "StoreCore.getKeySchema(table)", - "gasUsed": 2330 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set complex record with dynamic data (4 slots)", - "functionCall": "StoreCore.setRecord(table, key, data)", - "gasUsed": 107714 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get complex record with dynamic data (4 slots)", - "functionCall": "StoreCore.getRecord(table, key)", - "gasUsed": 6443 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "compare: Set complex record with dynamic data using native solidity", - "functionCall": "testStruct = _testStruct", - "gasUsed": 116839 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "compare: Set complex record with dynamic data using abi.encode", - "functionCall": "testMapping[1234] = abi.encode(_testStruct)", - "gasUsed": 267377 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set dynamic length of dynamic index 0", - "functionCall": "StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)", - "gasUsed": 23600 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set dynamic length of dynamic index 1", - "functionCall": "StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)", - "gasUsed": 1719 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "reduce dynamic length of dynamic index 0", - "functionCall": "StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)", - "gasUsed": 1707 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set static field (1 slot)", - "functionCall": "StoreCore.setField(table, key, 0, firstDataPacked)", - "gasUsed": 37980 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get static field (1 slot)", - "functionCall": "StoreCore.getField(table, key, 0)", - "gasUsed": 2980 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set static field (overlap 2 slot)", - "functionCall": "StoreCore.setField(table, key, 1, secondDataPacked)", - "gasUsed": 35009 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get static field (overlap 2 slot)", - "functionCall": "StoreCore.getField(table, key, 1)", - "gasUsed": 3877 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set dynamic field (1 slot, first dynamic field)", - "functionCall": "StoreCore.setField(table, key, 2, thirdDataBytes)", - "gasUsed": 57377 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get dynamic field (1 slot, first dynamic field)", - "functionCall": "StoreCore.getField(table, key, 2)", - "gasUsed": 3812 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set dynamic field (1 slot, second dynamic field)", - "functionCall": "StoreCore.setField(table, key, 3, fourthDataBytes)", - "gasUsed": 35503 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get dynamic field (1 slot, second dynamic field)", - "functionCall": "StoreCore.getField(table, key, 3)", - "gasUsed": 3825 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set static record (1 slot)", - "functionCall": "StoreCore.setRecord(table, key, data)", - "gasUsed": 37369 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get static record (1 slot)", - "functionCall": "StoreCore.getRecord(table, key, schema)", - "gasUsed": 1329 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "set static record (2 slots)", - "functionCall": "StoreCore.setRecord(table, key, data)", - "gasUsed": 59940 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "get static record (2 slots)", - "functionCall": "StoreCore.getRecord(table, key, schema)", - "gasUsed": 1578 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "StoreCore: set table metadata", - "functionCall": "StoreCore.setMetadata(table, tableName, fieldNames)", - "gasUsed": 251850 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "update in field (1 slot, 1 uint32 item)", - "functionCall": "StoreCore.updateInField(table, key, 1, 4 * 1, secondDataForUpdate)", - "gasUsed": 16604 - }, - { - "source": "test/StoreCoreGas.t.sol", - "name": "push to field (2 slots, 6 uint64 items)", - "functionCall": "StoreCore.updateInField(table, key, 2, 8 * 1, thirdDataForUpdate)", - "gasUsed": 17699 - }, - { - "source": "test/StoreMetadata.t.sol", - "name": "set record in StoreMetadataTable", - "functionCall": "StoreMetadata.set({ tableId: tableId, tableName: tableName, abiEncodedFieldNames: abi.encode(fieldNames) })", - "gasUsed": 250196 - }, - { - "source": "test/StoreMetadata.t.sol", - "name": "get record from StoreMetadataTable", - "functionCall": "StoreMetadataData memory metadata = StoreMetadata.get(tableId)", - "gasUsed": 12117 - }, - { - "source": "test/StoreSwitch.t.sol", - "name": "check if delegatecall", - "functionCall": "isDelegate = StoreSwitch.isDelegateCall()", - "gasUsed": 694 - }, - { - "source": "test/StoreSwitch.t.sol", - "name": "check if delegatecall", - "functionCall": "isDelegate = StoreSwitch.isDelegateCall()", - "gasUsed": 627 - }, - { - "source": "test/tables/Callbacks.t.sol", - "name": "set field in Callbacks", - "functionCall": "Callbacks.set(key, callbacks)", - "gasUsed": 63195 - }, - { - "source": "test/tables/Callbacks.t.sol", - "name": "get field from Callbacks (warm)", - "functionCall": "bytes24[] memory returnedCallbacks = Callbacks.get(key)", - "gasUsed": 5791 - }, - { - "source": "test/tables/Callbacks.t.sol", - "name": "push field to Callbacks", - "functionCall": "Callbacks.push(key, callbacks[0])", - "gasUsed": 40884 - }, - { - "source": "test/tables/Hooks.t.sol", - "name": "set field in Hooks", - "functionCall": "Hooks.set(key, addresses)", - "gasUsed": 63364 - }, - { - "source": "test/tables/Hooks.t.sol", - "name": "get field from Hooks (warm)", - "functionCall": "address[] memory returnedAddresses = Hooks.get(key)", - "gasUsed": 5940 - }, - { - "source": "test/tables/Hooks.t.sol", - "name": "push field to Hooks", - "functionCall": "Hooks.push(key, addresses[0])", - "gasUsed": 40876 - }, - { - "source": "test/tightcoder/DecodeSlice.t.sol", - "name": "decode packed uint32[]", - "functionCall": "uint32[] memory arr = SliceLib.fromBytes(input).decodeArray_uint32()", - "gasUsed": 794 - }, - { - "source": "test/tightcoder/DecodeSlice.t.sol", - "name": "decode packed bytes32[]", - "functionCall": "bytes32[] memory output = SliceLib.fromBytes(input).decodeArray_bytes32()", - "gasUsed": 623 - }, - { - "source": "test/tightcoder/EncodeArray.t.sol", - "name": "encode packed bytes[]", - "functionCall": "bytes memory output = EncodeArray.encode(input)", - "gasUsed": 1365 - }, - { - "source": "test/tightcoder/EncodeArray.t.sol", - "name": "encode packed uint16[]", - "functionCall": "bytes memory output = EncodeArray.encode(input)", - "gasUsed": 1155 - }, - { - "source": "test/tightcoder/EncodeArray.t.sol", - "name": "encode packed uint32[]", - "functionCall": "bytes memory output = EncodeArray.encode(input)", - "gasUsed": 1061 - }, - { - "source": "test/tightcoder/EncodeArray.t.sol", - "name": "encode packed uint8[]", - "functionCall": "bytes memory output = EncodeArray.encode(input)", - "gasUsed": 1050 - }, - { - "source": "test/tightcoder/TightCoder.t.sol", - "name": "decode packed uint32[]", - "functionCall": "uint32[] memory output = SliceLib.fromBytes(packed).decodeArray_uint32()", - "gasUsed": 797 - }, - { - "source": "test/tightcoder/TightCoder.t.sol", - "name": "encode packed bytes24[]", - "functionCall": "bytes memory packed = EncodeArray.encode(input)", - "gasUsed": 898 - }, - { - "source": "test/tightcoder/TightCoder.t.sol", - "name": "decode packed uint32[]", - "functionCall": "bytes24[] memory output = SliceLib.fromBytes(packed).decodeArray_bytes24()", - "gasUsed": 634 - }, - { - "source": "test/Vector2.t.sol", - "name": "register Vector2 schema", - "functionCall": "Vector2.registerSchema()", - "gasUsed": 58004 - }, - { - "source": "test/Vector2.t.sol", - "name": "set Vector2 record", - "functionCall": "Vector2.set({ key: key, x: 1, y: 2 })", - "gasUsed": 38643 - }, - { - "source": "test/Vector2.t.sol", - "name": "get Vector2 record", - "functionCall": "Vector2Data memory vector = Vector2.get(key)", - "gasUsed": 5094 + "gasUsed": 1375 } ] diff --git a/packages/store/package.json b/packages/store/package.json index a05e328c5b..bab6b295ee 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -46,7 +46,7 @@ "clean:typechain": "rimraf types", "dev": "tsup --watch", "docs": "rimraf API && hardhat docgen", - "gas-report": "../cli/dist/mud.js gas-report --path test/* --path test/**/* --save gas-report.json", + "gas-report": "../cli/dist/mud.js gas-report --save gas-report.json", "generate-tightcoder": "tsx ./scripts/generate-tightcoder.ts && prettier --write '**/tightcoder/*.sol'", "lint": "solhint --config ./.solhint.json 'src/**/*.sol'", "test": "vitest typecheck --run && vitest --run --passWithNoTests && forge test" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b57a697d59..c2f4721cd6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ importers: path: specifier: ^0.12.7 version: 0.12.7 + strip-ansi: + specifier: ^7.1.0 + version: 7.1.0 table: specifier: ^6.8.1 version: 6.8.1 @@ -4324,6 +4327,11 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: false + /ansi-styles@2.2.1: resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} engines: {node: '>=0.10.0'} @@ -13331,6 +13339,13 @@ packages: dependencies: ansi-regex: 5.0.1 + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: false + /strip-bom-stream@1.0.0: resolution: {integrity: sha512-7jfJB9YpI2Z0aH3wu10ZqitvYJaE0s5IzFuWE+0pbb4Q/armTloEUShymkDO47YSLnjAW52mlXT//hs9wXNNJQ==} engines: {node: '>=0.10.0'} From 0244cae919a21f34f8a47360ebdcf66ce72ba1a7 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 12:50:22 +0100 Subject: [PATCH 07/11] update comment --- packages/cli/src/commands/gas-report.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/gas-report.ts b/packages/cli/src/commands/gas-report.ts index 737b546a0c..0631c3e1d4 100644 --- a/packages/cli/src/commands/gas-report.ts +++ b/packages/cli/src/commands/gas-report.ts @@ -7,18 +7,21 @@ import stripAnsi from "strip-ansi"; /** * Print the gas report to the console, save it to a file and compare it to a previous gas report if provided. - * Requires forge to be installed, and gas test files including `// !gasreport` comments, like this: + * + * Requires foundry to be installed. Inherit from GasReporter and use startGasReport/endGasReport to measure gas used. * * ```solidity - * contract GasTest is DSTestPlus { + * contract MyContractTest is Test, GasReporter { * function testBuffer() public pure { - * // !gasreport allocate a buffer + * startGasReport("allocate a buffer"); * Buffer buffer = Buffer_.allocate(32); + * endGasReport(); * * bytes32 value = keccak256("some data"); * - * // !gasreport append 32 bytes to a buffer + * startGasReport("append 32 bytes to a buffer"); * buffer.append(value); + * endGasReport(); * } * } * ``` From 884afc0614e2d57e6c4250c31f93ece83a102be2 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 12:54:40 +0100 Subject: [PATCH 08/11] move tests to new gas report --- packages/store/gas-report.json | 708 ++++++++++++++++++ packages/store/test/Bytes.t.sol | 35 +- packages/store/test/KeyEncoding.t.sol | 8 +- packages/store/test/Mixed.t.sol | 17 +- packages/store/test/PackedCounter.t.sol | 17 +- packages/store/test/Schema.t.sol | 28 +- packages/store/test/Slice.t.sol | 41 +- packages/store/test/Storage.t.sol | 14 +- packages/store/test/StoreCoreDynamic.t.sol | 39 +- packages/store/test/StoreCoreGas.t.sol | 134 ++-- packages/store/test/StoreMetadata.t.sol | 11 +- packages/store/test/StoreSwitch.t.sol | 12 +- packages/store/test/Vector2.t.sol | 14 +- packages/store/test/tables/Callbacks.t.sol | 14 +- packages/store/test/tables/Hooks.t.sol | 14 +- .../store/test/tightcoder/DecodeSlice.t.sol | 11 +- .../store/test/tightcoder/EncodeArray.t.sol | 17 +- .../store/test/tightcoder/TightCoder.t.sol | 14 +- packages/world/gas-report.json | 316 ++++---- packages/world/package.json | 3 +- packages/world/test/KeysInTableModule.t.sol | 41 +- packages/world/test/KeysWithValueModule.t.sol | 29 +- packages/world/test/SnapSyncModule.t.sol | 23 +- packages/world/test/UniqueEntityModule.t.sol | 17 +- packages/world/test/World.t.sol | 40 +- packages/world/test/WorldDynamicUpdate.t.sol | 22 +- packages/world/test/query.t.sol | 38 +- pnpm-lock.yaml | 3 + 28 files changed, 1300 insertions(+), 380 deletions(-) diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 8e77fb77b1..2c4286c194 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -1,4 +1,64 @@ [ + { + "file": "test/Bytes.t.sol", + "test": "testEquals", + "name": "compare equal bytes", + "gasUsed": 195 + }, + { + "file": "test/Bytes.t.sol", + "test": "testEqualsFalse", + "name": "compare unequal bytes", + "gasUsed": 195 + }, + { + "file": "test/Bytes.t.sol", + "test": "testSetBytes1", + "name": "set bytes1 in bytes32", + "gasUsed": 0 + }, + { + "file": "test/Bytes.t.sol", + "test": "testSetBytes2", + "name": "set bytes2 in bytes32", + "gasUsed": 0 + }, + { + "file": "test/Bytes.t.sol", + "test": "testSetBytes4", + "name": "set bytes4 in bytes32", + "gasUsed": 0 + }, + { + "file": "test/Bytes.t.sol", + "test": "testSetBytes4Memory", + "name": "set bytes4 in bytes memory", + "gasUsed": 36 + }, + { + "file": "test/Bytes.t.sol", + "test": "testSlice3", + "name": "slice bytes3 with offset 1", + "gasUsed": 67 + }, + { + "file": "test/Bytes.t.sol", + "test": "testSlice32", + "name": "slice bytes32 with offset 10", + "gasUsed": 67 + }, + { + "file": "test/Bytes.t.sol", + "test": "testToBytes32", + "name": "create bytes32 from bytes memory with offset 0", + "gasUsed": 24 + }, + { + "file": "test/Bytes.t.sol", + "test": "testToBytes32CrossWord", + "name": "create bytes32 from bytes memory with offset 16", + "gasUsed": 35 + }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", @@ -34,5 +94,653 @@ "test": "testCompareAbiEncodeVsCustom", "name": "pass custom encoded bytes to external contract", "gasUsed": 1375 + }, + { + "file": "test/KeyEncoding.t.sol", + "test": "testRegisterAndGetSchema", + "name": "register KeyEncoding schema", + "gasUsed": 64685 + }, + { + "file": "test/Mixed.t.sol", + "test": "testCompareSolidity", + "name": "store Mixed struct in storage (native solidity)", + "gasUsed": 92037 + }, + { + "file": "test/Mixed.t.sol", + "test": "testRegisterAndGetSchema", + "name": "register Mixed schema", + "gasUsed": 61216 + }, + { + "file": "test/Mixed.t.sol", + "test": "testSetAndGet", + "name": "set record in Mixed", + "gasUsed": 112167 + }, + { + "file": "test/Mixed.t.sol", + "test": "testSetAndGet", + "name": "get record from Mixed", + "gasUsed": 13453 + }, + { + "file": "test/PackedCounter.t.sol", + "test": "testAtIndex", + "name": "get value at index of PackedCounter", + "gasUsed": 254 + }, + { + "file": "test/PackedCounter.t.sol", + "test": "testSetAtIndex", + "name": "set value at index of PackedCounter", + "gasUsed": 792 + }, + { + "file": "test/PackedCounter.t.sol", + "test": "testTotal", + "name": "pack uint40 array into PackedCounter", + "gasUsed": 2145 + }, + { + "file": "test/PackedCounter.t.sol", + "test": "testTotal", + "name": "get total of PackedCounter", + "gasUsed": 26 + }, + { + "file": "test/Schema.t.sol", + "test": "testEncodeDecodeSchema", + "name": "get schema type at index", + "gasUsed": 184 + }, + { + "file": "test/Schema.t.sol", + "test": "testGetNumDynamicFields", + "name": "get number of dynamic fields from schema", + "gasUsed": 73 + }, + { + "file": "test/Schema.t.sol", + "test": "testGetNumStaticFields", + "name": "get number of static fields from schema", + "gasUsed": 84 + }, + { + "file": "test/Schema.t.sol", + "test": "testGetStaticSchemaLength", + "name": "get static data length from schema", + "gasUsed": 32 + }, + { + "file": "test/Schema.t.sol", + "test": "testIsEmptyFalse", + "name": "check if schema is empty (non-empty schema)", + "gasUsed": 6 + }, + { + "file": "test/Schema.t.sol", + "test": "testIsEmptyTrue", + "name": "check if schema is empty (empty schema)", + "gasUsed": 6 + }, + { + "file": "test/Schema.t.sol", + "test": "testValidate", + "name": "validate schema", + "gasUsed": 18265 + }, + { + "file": "test/Slice.t.sol", + "test": "testFromBytes", + "name": "make Slice from bytes", + "gasUsed": 30 + }, + { + "file": "test/Slice.t.sol", + "test": "testFromBytes", + "name": "get Slice length", + "gasUsed": 0 + }, + { + "file": "test/Slice.t.sol", + "test": "testFromBytes", + "name": "get Slice pointer", + "gasUsed": 26 + }, + { + "file": "test/Slice.t.sol", + "test": "testSubslice", + "name": "subslice bytes (no copy) [1:4]", + "gasUsed": 310 + }, + { + "file": "test/Slice.t.sol", + "test": "testSubslice", + "name": "subslice bytes (no copy) [4:37]", + "gasUsed": 310 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes", + "name": "Slice (0 bytes) to bytes memory", + "gasUsed": 472 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes", + "name": "Slice (2 bytes) to bytes memory", + "gasUsed": 507 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes", + "name": "Slice (32 bytes) to bytes memory", + "gasUsed": 507 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes", + "name": "Slice (34 bytes) to bytes memory", + "gasUsed": 584 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes", + "name": "Slice (1024 bytes) to bytes memory", + "gasUsed": 4847 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes", + "name": "Slice (1024x1024 bytes) to bytes memory", + "gasUsed": 6614841 + }, + { + "file": "test/Slice.t.sol", + "test": "testToBytes32", + "name": "Slice to bytes32", + "gasUsed": 80 + }, + { + "file": "test/Storage.t.sol", + "test": "testStoreLoad", + "name": "store 1 storage slot", + "gasUsed": 22502 + }, + { + "file": "test/Storage.t.sol", + "test": "testStoreLoad", + "name": "store 34 bytes over 3 storage slots (with offset and safeTrail))", + "gasUsed": 23157 + }, + { + "file": "test/Storage.t.sol", + "test": "testStoreLoad", + "name": "load 34 bytes over 3 storage slots (with offset and safeTrail))", + "gasUsed": 1098 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetFieldSlice", + "name": "get field slice (cold, 1 slot)", + "gasUsed": 8151 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetFieldSlice", + "name": "get field slice (warm, 1 slot)", + "gasUsed": 2179 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetFieldSlice", + "name": "get field slice (semi-cold, 1 slot)", + "gasUsed": 4183 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetFieldSlice", + "name": "get field slice (warm, 2 slots)", + "gasUsed": 4470 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetSecondFieldLength", + "name": "get field length (cold, 1 slot)", + "gasUsed": 7958 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetSecondFieldLength", + "name": "get field length (warm, 1 slot)", + "gasUsed": 1955 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetThirdFieldLength", + "name": "get field length (warm due to , 2 slots)", + "gasUsed": 7958 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testGetThirdFieldLength", + "name": "get field length (warm, 2 slots)", + "gasUsed": 1954 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testPopFromSecondField", + "name": "pop from field (cold, 1 slot, 1 uint32 item)", + "gasUsed": 29290 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testPopFromSecondField", + "name": "pop from field (warm, 1 slot, 1 uint32 item)", + "gasUsed": 19343 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testPopFromThirdField", + "name": "pop from field (cold, 2 slots, 10 uint32 items)", + "gasUsed": 31173 + }, + { + "file": "test/StoreCoreDynamic.t.sol", + "test": "testPopFromThirdField", + "name": "pop from field (warm, 2 slots, 10 uint32 items)", + "gasUsed": 19226 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testAccessEmptyData", + "name": "access non-existing record", + "gasUsed": 7320 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testAccessEmptyData", + "name": "access static field of non-existing record", + "gasUsed": 2972 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testAccessEmptyData", + "name": "access dynamic field of non-existing record", + "gasUsed": 3585 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testAccessEmptyData", + "name": "access length of dynamic field of non-existing record", + "gasUsed": 1326 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testAccessEmptyData", + "name": "access slice of dynamic field of non-existing record", + "gasUsed": 1322 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testDeleteData", + "name": "delete record (complex data, 3 slots)", + "gasUsed": 10993 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHasSchema", + "name": "Check for existence of table (existent)", + "gasUsed": 1127 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHasSchema", + "name": "check for existence of table (non-existent)", + "gasUsed": 3128 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooks", + "name": "register subscriber", + "gasUsed": 65559 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooks", + "name": "set record on table with subscriber", + "gasUsed": 73960 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooks", + "name": "set static field on table with subscriber", + "gasUsed": 32025 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooks", + "name": "delete record on table with subscriber", + "gasUsed": 24583 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooksDynamicData", + "name": "register subscriber", + "gasUsed": 65559 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooksDynamicData", + "name": "set (dynamic) record on table with subscriber", + "gasUsed": 167397 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooksDynamicData", + "name": "set (dynamic) field on table with subscriber", + "gasUsed": 35058 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testHooksDynamicData", + "name": "delete (dynamic) record on table with subscriber", + "gasUsed": 26054 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testPushToField", + "name": "push to field (1 slot, 1 uint32 item)", + "gasUsed": 17063 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testPushToField", + "name": "push to field (2 slots, 10 uint32 items)", + "gasUsed": 39779 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testRegisterAndGetSchema", + "name": "StoreCore: register schema", + "gasUsed": 54869 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testRegisterAndGetSchema", + "name": "StoreCore: get schema (warm)", + "gasUsed": 1127 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testRegisterAndGetSchema", + "name": "StoreCore: get key schema (warm)", + "gasUsed": 2322 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicData", + "name": "set complex record with dynamic data (4 slots)", + "gasUsed": 107706 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicData", + "name": "get complex record with dynamic data (4 slots)", + "gasUsed": 6440 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicData", + "name": "compare: Set complex record with dynamic data using native solidity", + "gasUsed": 116841 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicData", + "name": "compare: Set complex record with dynamic data using abi.encode", + "gasUsed": 267368 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicDataLength", + "name": "set dynamic length of dynamic index 0", + "gasUsed": 23609 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicDataLength", + "name": "set dynamic length of dynamic index 1", + "gasUsed": 1710 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetDynamicDataLength", + "name": "reduce dynamic length of dynamic index 0", + "gasUsed": 1698 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "set static field (1 slot)", + "gasUsed": 37984 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "get static field (1 slot)", + "gasUsed": 2975 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "set static field (overlap 2 slot)", + "gasUsed": 35005 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "get static field (overlap 2 slot)", + "gasUsed": 3874 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "set dynamic field (1 slot, first dynamic field)", + "gasUsed": 57383 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "get dynamic field (1 slot, first dynamic field)", + "gasUsed": 3808 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "set dynamic field (1 slot, second dynamic field)", + "gasUsed": 35513 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetField", + "name": "get dynamic field (1 slot, second dynamic field)", + "gasUsed": 3823 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetStaticData", + "name": "set static record (1 slot)", + "gasUsed": 37373 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetStaticData", + "name": "get static record (1 slot)", + "gasUsed": 1319 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetStaticDataSpanningWords", + "name": "set static record (2 slots)", + "gasUsed": 59940 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetAndGetStaticDataSpanningWords", + "name": "get static record (2 slots)", + "gasUsed": 1568 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetMetadata", + "name": "StoreCore: set table metadata", + "gasUsed": 251865 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testUpdateInField", + "name": "update in field (1 slot, 1 uint32 item)", + "gasUsed": 16600 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testUpdateInField", + "name": "push to field (2 slots, 6 uint64 items)", + "gasUsed": 17698 + }, + { + "file": "test/StoreMetadata.t.sol", + "test": "testSetAndGet", + "name": "set record in StoreMetadataTable", + "gasUsed": 250187 + }, + { + "file": "test/StoreMetadata.t.sol", + "test": "testSetAndGet", + "name": "get record from StoreMetadataTable", + "gasUsed": 12114 + }, + { + "file": "test/StoreSwitch.t.sol", + "test": "testIsDelegatecall", + "name": "check if delegatecall", + "gasUsed": 682 + }, + { + "file": "test/StoreSwitch.t.sol", + "test": "testIsNoDelegatecall", + "name": "check if delegatecall", + "gasUsed": 702 + }, + { + "file": "test/tables/Callbacks.t.sol", + "test": "testSetAndGet", + "name": "set field in Callbacks", + "gasUsed": 63202 + }, + { + "file": "test/tables/Callbacks.t.sol", + "test": "testSetAndGet", + "name": "get field from Callbacks (warm)", + "gasUsed": 5783 + }, + { + "file": "test/tables/Callbacks.t.sol", + "test": "testSetAndGet", + "name": "push field to Callbacks", + "gasUsed": 40883 + }, + { + "file": "test/tables/Hooks.t.sol", + "test": "testSetAndGet", + "name": "set field in Hooks", + "gasUsed": 63358 + }, + { + "file": "test/tables/Hooks.t.sol", + "test": "testSetAndGet", + "name": "get field from Hooks (warm)", + "gasUsed": 5934 + }, + { + "file": "test/tables/Hooks.t.sol", + "test": "testSetAndGet", + "name": "push field to Hooks", + "gasUsed": 40877 + }, + { + "file": "test/tightcoder/DecodeSlice.t.sol", + "test": "testToArrayUint32", + "name": "decode packed uint32[]", + "gasUsed": 784 + }, + { + "file": "test/tightcoder/DecodeSlice.t.sol", + "test": "testToBytes32Array", + "name": "decode packed bytes32[]", + "gasUsed": 610 + }, + { + "file": "test/tightcoder/EncodeArray.t.sol", + "test": "testEncodeBytesArray", + "name": "encode packed bytes[]", + "gasUsed": 1356 + }, + { + "file": "test/tightcoder/EncodeArray.t.sol", + "test": "testEncodeUint16Array", + "name": "encode packed uint16[]", + "gasUsed": 1145 + }, + { + "file": "test/tightcoder/EncodeArray.t.sol", + "test": "testEncodeUint32Array", + "name": "encode packed uint32[]", + "gasUsed": 1051 + }, + { + "file": "test/tightcoder/EncodeArray.t.sol", + "test": "testEncodeUint8Array", + "name": "encode packed uint8[]", + "gasUsed": 1037 + }, + { + "file": "test/tightcoder/TightCoder.t.sol", + "test": "testFromAndToUint32Array", + "name": "decode packed uint32[]", + "gasUsed": 787 + }, + { + "file": "test/tightcoder/TightCoder.t.sol", + "test": "testToAndFromBytes24Array", + "name": "encode packed bytes24[]", + "gasUsed": 888 + }, + { + "file": "test/tightcoder/TightCoder.t.sol", + "test": "testToAndFromBytes24Array", + "name": "decode packed uint32[]", + "gasUsed": 621 + }, + { + "file": "test/Vector2.t.sol", + "test": "testRegisterAndGetSchema", + "name": "register Vector2 schema", + "gasUsed": 57994 + }, + { + "file": "test/Vector2.t.sol", + "test": "testSetAndGet", + "name": "set Vector2 record", + "gasUsed": 38662 + }, + { + "file": "test/Vector2.t.sol", + "test": "testSetAndGet", + "name": "get Vector2 record", + "gasUsed": 5089 } ] diff --git a/packages/store/test/Bytes.t.sol b/packages/store/test/Bytes.t.sol index 8f3000abd0..ca81dbb337 100644 --- a/packages/store/test/Bytes.t.sol +++ b/packages/store/test/Bytes.t.sol @@ -1,17 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Bytes } from "../src/Bytes.sol"; -contract BytesTest is Test { +contract BytesTest is Test, GasReporter { function testToBytes32() public { bytes memory input = new bytes(32); input[0] = 0x01; input[31] = 0x02; - // !gasreport create bytes32 from bytes memory with offset 0 + startGasReport("create bytes32 from bytes memory with offset 0"); bytes32 output = Bytes.toBytes32(input, 0); + endGasReport(); assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); } @@ -21,8 +23,9 @@ contract BytesTest is Test { input[0 + 16] = 0x01; input[31 + 16] = 0x02; - // !gasreport create bytes32 from bytes memory with offset 16 + startGasReport("create bytes32 from bytes memory with offset 16"); bytes32 output = Bytes.toBytes32(input, 16); + endGasReport(); assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); } @@ -31,8 +34,9 @@ contract BytesTest is Test { bytes memory a = bytes("a"); bytes memory b = bytes("a"); - // !gasreport compare equal bytes + startGasReport("compare equal bytes"); bool equals = Bytes.equals(a, b); + endGasReport(); assertTrue(equals); } @@ -41,8 +45,9 @@ contract BytesTest is Test { bytes memory a = bytes("a"); bytes memory b = bytes("b"); - // !gasreport compare unequal bytes + startGasReport("compare unequal bytes"); bool equals = Bytes.equals(a, b); + endGasReport(); assertFalse(equals); } @@ -62,8 +67,9 @@ contract BytesTest is Test { a[3] = 0x04; a[4] = 0x05; - // !gasreport slice bytes3 with offset 1 + startGasReport("slice bytes3 with offset 1"); bytes3 b = Bytes.slice3(a, 1); + endGasReport(); assertEq(b.length, 3); assertEq(uint256(uint8(b[0])), 0x02); @@ -75,8 +81,9 @@ contract BytesTest is Test { bytes32 original = keccak256("some data"); bytes memory input = abi.encodePacked(bytes10(keccak256("irrelevant data")), original); - // !gasreport slice bytes32 with offset 10 + startGasReport("slice bytes32 with offset 10"); bytes32 output = Bytes.slice32(input, 10); + endGasReport(); assertEq(output, original); } @@ -84,8 +91,9 @@ contract BytesTest is Test { function testSetBytes1() public { bytes32 input = bytes32(0); - // !gasreport set bytes1 in bytes32 + startGasReport("set bytes1 in bytes32"); Bytes.setBytes1(input, 8, 0xff); + endGasReport(); assertEq(Bytes.setBytes1(input, 0, 0x01), bytes32(bytes1(0x01))); assertEq(Bytes.setBytes1(input, 31, 0x01), bytes32(uint256(0x01))); @@ -94,8 +102,9 @@ contract BytesTest is Test { function testSetBytes2() public { bytes32 input = bytes32(0); - // !gasreport set bytes2 in bytes32 + startGasReport("set bytes2 in bytes32"); Bytes.setBytes2(input, 8, 0xffff); + endGasReport(); assertEq(Bytes.setBytes2(input, 0, 0xffff), bytes32(bytes2(0xffff))); assertEq(Bytes.setBytes2(input, 30, 0xffff), bytes32(uint256(0xffff))); @@ -104,8 +113,9 @@ contract BytesTest is Test { function testSetBytes4() public { bytes32 input = bytes32(0); - // !gasreport set bytes4 in bytes32 + startGasReport("set bytes4 in bytes32"); Bytes.setBytes4(input, 8, 0xffffffff); + endGasReport(); assertEq(Bytes.setBytes4(input, 0, 0xffffffff), bytes32(bytes4(0xffffffff))); assertEq(Bytes.setBytes4(input, 30, 0xffffffff), bytes32(uint256(0xffff))); @@ -127,8 +137,9 @@ contract BytesTest is Test { bytes memory input = abi.encodePacked(first, remainder); - // !gasreport set bytes4 in bytes memory + startGasReport("set bytes4 in bytes memory"); bytes memory result = Bytes.setBytes4(input, 0, overwrite); + endGasReport(); // First 4 bytes should be overwritten assertEq(bytes4(result), overwrite); diff --git a/packages/store/test/KeyEncoding.t.sol b/packages/store/test/KeyEncoding.t.sol index 52e6f67b7b..9f6c5f8c03 100644 --- a/packages/store/test/KeyEncoding.t.sol +++ b/packages/store/test/KeyEncoding.t.sol @@ -1,17 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { KeyEncoding, KeyEncodingTableId } from "../src/codegen/Tables.sol"; import { ExampleEnum } from "../src/codegen/Types.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; import { Schema } from "../src/Schema.sol"; -contract KeyEncodingTest is Test, StoreReadWithStubs { +contract KeyEncodingTest is Test, GasReporter, StoreReadWithStubs { function testRegisterAndGetSchema() public { - // !gasreport register KeyEncoding schema + startGasReport("register KeyEncoding schema"); KeyEncoding.registerSchema(); + endGasReport(); Schema registeredSchema = StoreCore.getSchema(KeyEncodingTableId); Schema declaredSchema = KeyEncoding.getSchema(); diff --git a/packages/store/test/Mixed.t.sol b/packages/store/test/Mixed.t.sol index eb9c4ec2d3..93589810f3 100644 --- a/packages/store/test/Mixed.t.sol +++ b/packages/store/test/Mixed.t.sol @@ -1,19 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { Mixed, MixedData, MixedTableId } from "../src/codegen/Tables.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; import { Schema } from "../src/Schema.sol"; -contract MixedTest is Test, StoreReadWithStubs { +contract MixedTest is Test, GasReporter, StoreReadWithStubs { MixedData private testMixed; function testRegisterAndGetSchema() public { - // !gasreport register Mixed schema + startGasReport("register Mixed schema"); Mixed.registerSchema(); + endGasReport(); Schema registeredSchema = StoreCore.getSchema(MixedTableId); Schema declaredSchema = Mixed.getSchema(); @@ -30,11 +32,13 @@ contract MixedTest is Test, StoreReadWithStubs { a32[1] = 4; string memory s = "some string"; - // !gasreport set record in Mixed + startGasReport("set record in Mixed"); Mixed.set({ key: key, u32: 1, u128: 2, a32: a32, s: s }); + endGasReport(); - // !gasreport get record from Mixed + startGasReport("get record from Mixed"); MixedData memory mixed = Mixed.get(key); + endGasReport(); assertEq(mixed.u32, 1); assertEq(mixed.u128, 2); @@ -48,7 +52,8 @@ contract MixedTest is Test, StoreReadWithStubs { mixed.a32[0] = 3; mixed.a32[1] = 4; - // !gasreport store Mixed struct in storage (native solidity) + startGasReport("store Mixed struct in storage (native solidity)"); testMixed = mixed; + endGasReport(); } } diff --git a/packages/store/test/PackedCounter.t.sol b/packages/store/test/PackedCounter.t.sol index 32d6b3afce..542cc55e01 100644 --- a/packages/store/test/PackedCounter.t.sol +++ b/packages/store/test/PackedCounter.t.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { PackedCounter, PackedCounterLib } from "../src/PackedCounter.sol"; -contract PackedCounterTest is Test { +contract PackedCounterTest is Test, GasReporter { function testTotal() public { uint40[] memory counters = new uint40[](4); counters[0] = 1; @@ -12,11 +13,13 @@ contract PackedCounterTest is Test { counters[2] = 3; counters[3] = 4; - // !gasreport pack uint40 array into PackedCounter + startGasReport("pack uint40 array into PackedCounter"); PackedCounter packedCounter = PackedCounterLib.pack(counters); + endGasReport(); - // !gasreport get total of PackedCounter + startGasReport("get total of PackedCounter"); packedCounter.total(); + endGasReport(); assertEq(packedCounter.total(), 10); } @@ -30,8 +33,9 @@ contract PackedCounterTest is Test { PackedCounter packedCounter = PackedCounterLib.pack(counters); - // !gasreport get value at index of PackedCounter + startGasReport("get value at index of PackedCounter"); packedCounter.atIndex(3); + endGasReport(); assertEq(packedCounter.atIndex(0), 1); assertEq(packedCounter.atIndex(1), 2); @@ -48,8 +52,9 @@ contract PackedCounterTest is Test { PackedCounter packedCounter = PackedCounterLib.pack(counters); - // !gasreport set value at index of PackedCounter + startGasReport("set value at index of PackedCounter"); packedCounter = packedCounter.setAtIndex(2, 5); + endGasReport(); assertEq(packedCounter.atIndex(0), 1); assertEq(packedCounter.atIndex(1), 2); diff --git a/packages/store/test/Schema.t.sol b/packages/store/test/Schema.t.sol index ec39e94e21..d197f60116 100644 --- a/packages/store/test/Schema.t.sol +++ b/packages/store/test/Schema.t.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test, console } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { Schema, SchemaLib } from "../src/Schema.sol"; // TODO add tests for all schema types -contract SchemaTest is Test { +contract SchemaTest is Test, GasReporter { function testEncodeDecodeSchema() public { uint256 gas = gasleft(); Schema schema = SchemaLib.encode( @@ -20,8 +21,9 @@ contract SchemaTest is Test { gas = gas - gasleft(); console.log("GAS REPORT: encode schema with 6 entries [SchemaLib.encode]: %s", gas); - // !gasreport get schema type at index + startGasReport("get schema type at index"); SchemaType schemaType1 = schema.atIndex(0); + endGasReport(); assertEq(uint8(schemaType1), uint8(SchemaType.UINT8)); assertEq(uint8(schema.atIndex(1)), uint8(SchemaType.UINT16)); @@ -137,8 +139,9 @@ contract SchemaTest is Test { SchemaType.UINT32_ARRAY // 0 bytes (because it's dynamic) ); - // !gasreport get static data length from schema + startGasReport("get static data length from schema"); uint256 length = schema.staticDataLength(); + endGasReport(); assertEq(length, 55); } @@ -153,8 +156,9 @@ contract SchemaTest is Test { SchemaType.UINT32_ARRAY // 0 bytes (because it's dynamic) ); - // !gasreport get number of static fields from schema + startGasReport("get number of static fields from schema"); uint256 num = schema.numStaticFields(); + endGasReport(); assertEq(num, 5); } @@ -169,13 +173,14 @@ contract SchemaTest is Test { SchemaType.UINT32_ARRAY // 0 bytes (because it's dynamic) ); - // !gasreport get number of dynamic fields from schema + startGasReport("get number of dynamic fields from schema"); uint256 num = schema.numDynamicFields(); + endGasReport(); assertEq(num, 1); } - function testValidate() public pure { + function testValidate() public { SchemaType[] memory schema = new SchemaType[](28); schema[0] = SchemaType.UINT256; schema[1] = SchemaType.UINT256; @@ -207,8 +212,9 @@ contract SchemaTest is Test { schema[27] = SchemaType.UINT32_ARRAY; Schema encodedSchema = SchemaLib.encode(schema); - // !gasreport validate schema + startGasReport("validate schema"); encodedSchema.validate({ allowEmpty: false }); + endGasReport(); } function testFailValidate() public pure { @@ -219,8 +225,9 @@ contract SchemaTest is Test { SchemaType[] memory schema = new SchemaType[](0); Schema encodedSchema = SchemaLib.encode(schema); - // !gasreport check if schema is empty (empty schema) + startGasReport("check if schema is empty (empty schema)"); bool empty = encodedSchema.isEmpty(); + endGasReport(); assertTrue(empty); } @@ -228,8 +235,9 @@ contract SchemaTest is Test { function testIsEmptyFalse() public { Schema encodedSchema = SchemaLib.encode(SchemaType.UINT256); - // !gasreport check if schema is empty (non-empty schema) + startGasReport("check if schema is empty (non-empty schema)"); bool empty = encodedSchema.isEmpty(); + endGasReport(); assertFalse(empty); } diff --git a/packages/store/test/Slice.t.sol b/packages/store/test/Slice.t.sol index 3535699dfc..e7cd28c856 100644 --- a/packages/store/test/Slice.t.sol +++ b/packages/store/test/Slice.t.sol @@ -1,22 +1,26 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Memory } from "../src/Memory.sol"; import { Slice, SliceLib } from "../src/Slice.sol"; -contract SliceTest is Test { +contract SliceTest is Test, GasReporter { function testFromBytes() public { bytes memory data = abi.encodePacked(bytes8(0x0102030405060708)); - // !gasreport make Slice from bytes + startGasReport("make Slice from bytes"); Slice slice = SliceLib.fromBytes(data); + endGasReport(); - // !gasreport get Slice length + startGasReport("get Slice length"); slice.length(); + endGasReport(); - // !gasreport get Slice pointer + startGasReport("get Slice pointer"); slice.pointer(); + endGasReport(); assertEq(slice.length(), 8); assertEq(slice.pointer(), Memory.dataPointer(data)); @@ -36,8 +40,9 @@ contract SliceTest is Test { input[31] = 0x02; Slice slice = SliceLib.fromBytes(input); - // !gasreport Slice to bytes32 + startGasReport("Slice to bytes32"); bytes32 output = slice.toBytes32(); + endGasReport(); assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); } @@ -52,28 +57,34 @@ contract SliceTest is Test { bytes memory data1024x1024 = new bytes(1024 * 1024); slice = SliceLib.fromBytes(data0); - // !gasreport Slice (0 bytes) to bytes memory + startGasReport("Slice (0 bytes) to bytes memory"); bytes memory sliceData0 = slice.toBytes(); + endGasReport(); slice = SliceLib.fromBytes(data2); - // !gasreport Slice (2 bytes) to bytes memory + startGasReport("Slice (2 bytes) to bytes memory"); bytes memory sliceData2 = slice.toBytes(); + endGasReport(); slice = SliceLib.fromBytes(data32); - // !gasreport Slice (32 bytes) to bytes memory + startGasReport("Slice (32 bytes) to bytes memory"); bytes memory sliceData32 = slice.toBytes(); + endGasReport(); slice = SliceLib.fromBytes(data34); - // !gasreport Slice (34 bytes) to bytes memory + startGasReport("Slice (34 bytes) to bytes memory"); bytes memory sliceData34 = slice.toBytes(); + endGasReport(); slice = SliceLib.fromBytes(data1024); - // !gasreport Slice (1024 bytes) to bytes memory + startGasReport("Slice (1024 bytes) to bytes memory"); bytes memory sliceData1024 = slice.toBytes(); + endGasReport(); slice = SliceLib.fromBytes(data1024x1024); - // !gasreport Slice (1024x1024 bytes) to bytes memory + startGasReport("Slice (1024x1024 bytes) to bytes memory"); bytes memory sliceData1024x1024 = slice.toBytes(); + endGasReport(); assertEq(sliceData0, data0); assertEq(sliceData2, data2); @@ -88,11 +99,13 @@ contract SliceTest is Test { bytes memory data2 = hex"0405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324"; bytes memory data = abi.encodePacked(hex"00", data1, data2); - // !gasreport subslice bytes (no copy) [1:4] + startGasReport("subslice bytes (no copy) [1:4]"); Slice slice1 = SliceLib.getSubslice(data, 1, 1 + 3); + endGasReport(); - // !gasreport subslice bytes (no copy) [4:37] + startGasReport("subslice bytes (no copy) [4:37]"); Slice slice2 = SliceLib.getSubslice(data, 4, 4 + 33); + endGasReport(); assertEq(slice1.length(), 3); assertEq(slice2.length(), 33); diff --git a/packages/store/test/Storage.t.sol b/packages/store/test/Storage.t.sol index 6d98535b20..c6bbcd2908 100644 --- a/packages/store/test/Storage.t.sol +++ b/packages/store/test/Storage.t.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Storage } from "../src/Storage.sol"; import { Utils } from "../src/Utils.sol"; import { Bytes } from "../src/Bytes.sol"; -contract StorageTest is Test { +contract StorageTest is Test, GasReporter { function testStoreLoad() public { bytes memory data1 = abi.encodePacked( bytes1(0x01), @@ -26,15 +27,17 @@ contract StorageTest is Test { // First store some data to storage at the target slot and two slots after the target slot - // !gasreport store 1 storage slot + startGasReport("store 1 storage slot"); Storage.store({ storagePointer: storagePointer, data: originalDataFirstSlot }); + endGasReport(); Storage.store({ storagePointer: storagePointerTwoSlotsAfter, data: originalDataLastSlot }); // Then set the target slot, partially overwriting the first and third slot, but using safeTrail and offset - // !gasreport store 34 bytes over 3 storage slots (with offset and safeTrail)) + startGasReport("store 34 bytes over 3 storage slots (with offset and safeTrail))"); Storage.store({ storagePointer: storagePointer, offset: 31, data: data1 }); + endGasReport(); // Assert the first slot has the correct value assertEq( @@ -56,8 +59,9 @@ contract StorageTest is Test { // Assert we can load the correct partial value from storage - // !gasreport load 34 bytes over 3 storage slots (with offset and safeTrail)) + startGasReport("load 34 bytes over 3 storage slots (with offset and safeTrail))"); bytes memory data = Storage.load({ storagePointer: storagePointer, length: 34, offset: 31 }); + endGasReport(); assertEq(Bytes.slice1(data, 0), bytes1(0x01)); assertEq(Bytes.slice32(data, 1), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003)); diff --git a/packages/store/test/StoreCoreDynamic.t.sol b/packages/store/test/StoreCoreDynamic.t.sol index 8dd996187e..b651059ff3 100644 --- a/packages/store/test/StoreCoreDynamic.t.sol +++ b/packages/store/test/StoreCoreDynamic.t.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { Test, console } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { SliceLib } from "../src/Slice.sol"; @@ -9,7 +10,7 @@ import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; import { Schema, SchemaLib } from "../src/Schema.sol"; import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; -contract StoreCoreDynamicTest is Test, StoreReadWithStubs { +contract StoreCoreDynamicTest is Test, GasReporter, StoreReadWithStubs { Schema internal defaultKeySchema = SchemaLib.encode(SchemaType.BYTES32); bytes32[] internal _key; @@ -84,8 +85,9 @@ contract StoreCoreDynamicTest is Test, StoreReadWithStubs { emit StoreSetField(_table, _key, 1, newDataBytes); // Pop from second field - // !gasreport pop from field (cold, 1 slot, 1 uint32 item) + startGasReport("pop from field (cold, 1 slot, 1 uint32 item)"); StoreCore.popFromField(_table, _key, 1, byteLengthToPop); + endGasReport(); // Get second field bytes memory loadedData = StoreCore.getField(_table, _key, 1); // Verify loaded data is correct @@ -93,8 +95,9 @@ contract StoreCoreDynamicTest is Test, StoreReadWithStubs { // Reset the second field and pop again (but warm this time) StoreCore.setField(_table, _key, 1, dataBytes); - // !gasreport pop from field (warm, 1 slot, 1 uint32 item) + startGasReport("pop from field (warm, 1 slot, 1 uint32 item)"); StoreCore.popFromField(_table, _key, 1, byteLengthToPop); + endGasReport(); // Get second field loadedData = StoreCore.getField(_table, _key, 1); // Verify loaded data is correct @@ -120,16 +123,18 @@ contract StoreCoreDynamicTest is Test, StoreReadWithStubs { emit StoreSetField(_table, _key, 2, dataBytes); // Pop from the field - // !gasreport pop from field (cold, 2 slots, 10 uint32 items) + startGasReport("pop from field (cold, 2 slots, 10 uint32 items)"); StoreCore.popFromField(_table, _key, 2, byteLengthToPop); + endGasReport(); // Load and verify the field bytes memory loadedData = StoreCore.getField(_table, _key, 2); assertEq(loadedData, newDataBytes); // Reset the field and pop again (but warm this time) StoreCore.setField(_table, _key, 2, dataBytes); - // !gasreport pop from field (warm, 2 slots, 10 uint32 items) + startGasReport("pop from field (warm, 2 slots, 10 uint32 items)"); StoreCore.popFromField(_table, _key, 2, byteLengthToPop); + endGasReport(); // Load and verify the field loadedData = StoreCore.getField(_table, _key, 2); assertEq(loadedData, newDataBytes); @@ -142,40 +147,48 @@ contract StoreCoreDynamicTest is Test, StoreReadWithStubs { function testGetSecondFieldLength() public { Schema schema = StoreCore.getSchema(_table); - // !gasreport get field length (cold, 1 slot) + startGasReport("get field length (cold, 1 slot)"); uint256 length = StoreCore.getFieldLength(_table, _key, 1, schema); + endGasReport(); assertEq(length, secondDataBytes.length); - // !gasreport get field length (warm, 1 slot) + startGasReport("get field length (warm, 1 slot)"); length = StoreCore.getFieldLength(_table, _key, 1, schema); + endGasReport(); assertEq(length, secondDataBytes.length); } function testGetThirdFieldLength() public { Schema schema = StoreCore.getSchema(_table); - // !gasreport get field length (warm due to , 2 slots) + startGasReport("get field length (warm due to , 2 slots)"); uint256 length = StoreCore.getFieldLength(_table, _key, 2, schema); + endGasReport(); assertEq(length, thirdDataBytes.length); - // !gasreport get field length (warm, 2 slots) + startGasReport("get field length (warm, 2 slots)"); length = StoreCore.getFieldLength(_table, _key, 2, schema); + endGasReport(); assertEq(length, thirdDataBytes.length); } function testGetFieldSlice() public { Schema schema = StoreCore.getSchema(_table); - // !gasreport get field slice (cold, 1 slot) + startGasReport("get field slice (cold, 1 slot)"); bytes memory secondFieldSlice = StoreCore.getFieldSlice(_table, _key, 1, schema, 0, 4); + endGasReport(); assertEq(secondFieldSlice, SliceLib.getSubslice(secondDataBytes, 0, 4).toBytes()); - // !gasreport get field slice (warm, 1 slot) + startGasReport("get field slice (warm, 1 slot)"); secondFieldSlice = StoreCore.getFieldSlice(_table, _key, 1, schema, 4, 8); + endGasReport(); assertEq(secondFieldSlice, SliceLib.getSubslice(secondDataBytes, 4, 8).toBytes()); - // !gasreport get field slice (semi-cold, 1 slot) + startGasReport("get field slice (semi-cold, 1 slot)"); bytes memory thirdFieldSlice = StoreCore.getFieldSlice(_table, _key, 2, schema, 4, 32); + endGasReport(); assertEq(thirdFieldSlice, SliceLib.getSubslice(thirdDataBytes, 4, 32).toBytes()); - // !gasreport get field slice (warm, 2 slots) + startGasReport("get field slice (warm, 2 slots)"); thirdFieldSlice = StoreCore.getFieldSlice(_table, _key, 2, schema, 8, 40); + endGasReport(); assertEq(thirdFieldSlice, SliceLib.getSubslice(thirdDataBytes, 8, 40).toBytes()); } } diff --git a/packages/store/test/StoreCoreGas.t.sol b/packages/store/test/StoreCoreGas.t.sol index 2dbf3d09d4..a05d7257a9 100644 --- a/packages/store/test/StoreCoreGas.t.sol +++ b/packages/store/test/StoreCoreGas.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { StoreCore, StoreCoreInternal } from "../src/StoreCore.sol"; import { Utils } from "../src/Utils.sol"; @@ -23,7 +24,7 @@ struct TestStruct { uint32[] thirdData; } -contract StoreCoreGasTest is Test, StoreMock { +contract StoreCoreGasTest is Test, GasReporter, StoreMock { TestStruct private testStruct; mapping(uint256 => bytes) private testMapping; @@ -34,14 +35,17 @@ contract StoreCoreGasTest is Test, StoreMock { Schema keySchema = SchemaLib.encode(SchemaType.UINT8, SchemaType.UINT16); bytes32 table = keccak256("some.table"); - // !gasreport StoreCore: register schema + startGasReport("StoreCore: register schema"); StoreCore.registerSchema(table, schema, keySchema); + endGasReport(); - // !gasreport StoreCore: get schema (warm) + startGasReport("StoreCore: get schema (warm)"); StoreCore.getSchema(table); + endGasReport(); - // !gasreport StoreCore: get key schema (warm) + startGasReport("StoreCore: get key schema (warm)"); StoreCore.getKeySchema(table); + endGasReport(); } function testHasSchema() public { @@ -50,11 +54,13 @@ contract StoreCoreGasTest is Test, StoreMock { bytes32 table2 = keccak256("other.table"); StoreCore.registerSchema(table, schema, defaultKeySchema); - // !gasreport Check for existence of table (existent) + startGasReport("Check for existence of table (existent)"); StoreCore.hasTable(table); + endGasReport(); - // !gasreport check for existence of table (non-existent) + startGasReport("check for existence of table (non-existent)"); StoreCore.hasTable(table2); + endGasReport(); } function testSetMetadata() public { @@ -69,8 +75,9 @@ contract StoreCoreGasTest is Test, StoreMock { // Register table StoreCore.registerSchema(table, schema, keySchema); - // !gasreport StoreCore: set table metadata + startGasReport("StoreCore: set table metadata"); StoreCore.setMetadata(table, tableName, fieldNames); + endGasReport(); } function testSetAndGetDynamicDataLength() public { @@ -92,16 +99,19 @@ contract StoreCoreGasTest is Test, StoreMock { key[0] = bytes32("some key"); // Set dynamic data length of dynamic index 0 - // !gasreport set dynamic length of dynamic index 0 + startGasReport("set dynamic length of dynamic index 0"); StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10); + endGasReport(); // Set dynamic data length of dynamic index 1 - // !gasreport set dynamic length of dynamic index 1 + startGasReport("set dynamic length of dynamic index 1"); StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99); + endGasReport(); // Reduce dynamic data length of dynamic index 0 again - // !gasreport reduce dynamic length of dynamic index 0 + startGasReport("reduce dynamic length of dynamic index 0"); StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5); + endGasReport(); } function testSetAndGetStaticData() public { @@ -115,12 +125,14 @@ contract StoreCoreGasTest is Test, StoreMock { bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); - // !gasreport set static record (1 slot) + startGasReport("set static record (1 slot)"); StoreCore.setRecord(table, key, data); + endGasReport(); // Get data - // !gasreport get static record (1 slot) + startGasReport("get static record (1 slot)"); StoreCore.getRecord(table, key, schema); + endGasReport(); } function testSetAndGetStaticDataSpanningWords() public { @@ -138,12 +150,14 @@ contract StoreCoreGasTest is Test, StoreMock { bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); - // !gasreport set static record (2 slots) + startGasReport("set static record (2 slots)"); StoreCore.setRecord(table, key, data); + endGasReport(); // Get data - // !gasreport get static record (2 slots) + startGasReport("get static record (2 slots)"); StoreCore.getRecord(table, key, schema); + endGasReport(); } function testSetAndGetDynamicData() public { @@ -195,12 +209,14 @@ contract StoreCoreGasTest is Test, StoreMock { key[0] = bytes32("some.key"); // Set data - // !gasreport set complex record with dynamic data (4 slots) + startGasReport("set complex record with dynamic data (4 slots)"); StoreCore.setRecord(table, key, data); + endGasReport(); // Get data - // !gasreport get complex record with dynamic data (4 slots) + startGasReport("get complex record with dynamic data (4 slots)"); StoreCore.getRecord(table, key); + endGasReport(); // Compare gas - setting the data as raw struct TestStruct memory _testStruct = TestStruct(0, new uint32[](2), new uint32[](3)); @@ -211,11 +227,13 @@ contract StoreCoreGasTest is Test, StoreMock { _testStruct.thirdData[1] = 0x1d1e1f20; _testStruct.thirdData[2] = 0x21222324; - // !gasreport compare: Set complex record with dynamic data using native solidity + startGasReport("compare: Set complex record with dynamic data using native solidity"); testStruct = _testStruct; + endGasReport(); - // !gasreport compare: Set complex record with dynamic data using abi.encode + startGasReport("compare: Set complex record with dynamic data using abi.encode"); testMapping[1234] = abi.encode(_testStruct); + endGasReport(); } function testSetAndGetField() public { @@ -241,27 +259,31 @@ contract StoreCoreGasTest is Test, StoreMock { bytes memory firstDataPacked = abi.encodePacked(firstDataBytes); // Set first field - // !gasreport set static field (1 slot) + startGasReport("set static field (1 slot)"); StoreCore.setField(table, key, 0, firstDataPacked); + endGasReport(); //////////////// // Static data //////////////// // Get first field - // !gasreport get static field (1 slot) + startGasReport("get static field (1 slot)"); StoreCore.getField(table, key, 0); + endGasReport(); // Set second field bytes32 secondDataBytes = keccak256("some data"); bytes memory secondDataPacked = abi.encodePacked(secondDataBytes); - // !gasreport set static field (overlap 2 slot) + startGasReport("set static field (overlap 2 slot)"); StoreCore.setField(table, key, 1, secondDataPacked); + endGasReport(); // Get second field - // !gasreport get static field (overlap 2 slot) + startGasReport("get static field (overlap 2 slot)"); StoreCore.getField(table, key, 1); + endGasReport(); //////////////// // Dynamic data @@ -285,20 +307,24 @@ contract StoreCoreGasTest is Test, StoreMock { } // Set third field - // !gasreport set dynamic field (1 slot, first dynamic field) + startGasReport("set dynamic field (1 slot, first dynamic field)"); StoreCore.setField(table, key, 2, thirdDataBytes); + endGasReport(); // Get third field - // !gasreport get dynamic field (1 slot, first dynamic field) + startGasReport("get dynamic field (1 slot, first dynamic field)"); StoreCore.getField(table, key, 2); + endGasReport(); // Set fourth field - // !gasreport set dynamic field (1 slot, second dynamic field) + startGasReport("set dynamic field (1 slot, second dynamic field)"); StoreCore.setField(table, key, 3, fourthDataBytes); + endGasReport(); // Get fourth field - // !gasreport get dynamic field (1 slot, second dynamic field) + startGasReport("get dynamic field (1 slot, second dynamic field)"); StoreCore.getField(table, key, 3); + endGasReport(); } function testDeleteData() public { @@ -351,8 +377,9 @@ contract StoreCoreGasTest is Test, StoreMock { StoreCore.setRecord(table, key, data); // Delete data - // !gasreport delete record (complex data, 3 slots) + startGasReport("delete record (complex data, 3 slots)"); StoreCore.deleteRecord(table, key); + endGasReport(); } function testPushToField() public { @@ -402,8 +429,9 @@ contract StoreCoreGasTest is Test, StoreMock { bytes memory newSecondDataBytes = abi.encodePacked(secondDataBytes, secondDataToPush); // Push to second field - // !gasreport push to field (1 slot, 1 uint32 item) + startGasReport("push to field (1 slot, 1 uint32 item)"); StoreCore.pushToField(table, key, 1, secondDataToPush); + endGasReport(); // Create data to push bytes memory thirdDataToPush; @@ -423,8 +451,9 @@ contract StoreCoreGasTest is Test, StoreMock { } // Push to third field - // !gasreport push to field (2 slots, 10 uint32 items) + startGasReport("push to field (2 slots, 10 uint32 items)"); StoreCore.pushToField(table, key, 2, thirdDataToPush); + endGasReport(); } function testUpdateInField() public { @@ -473,8 +502,9 @@ contract StoreCoreGasTest is Test, StoreMock { } // Update index 1 in second field (4 = byte length of uint32) - // !gasreport update in field (1 slot, 1 uint32 item) + startGasReport("update in field (1 slot, 1 uint32 item)"); StoreCore.updateInField(table, key, 1, 4 * 1, secondDataForUpdate); + endGasReport(); // Create data for update bytes memory thirdDataForUpdate; @@ -498,8 +528,9 @@ contract StoreCoreGasTest is Test, StoreMock { } // Update indexes 1,2,3,4 in third field (8 = byte length of uint64) - // !gasreport push to field (2 slots, 6 uint64 items) + startGasReport("push to field (2 slots, 6 uint64 items)"); StoreCore.updateInField(table, key, 2, 8 * 1, thirdDataForUpdate); + endGasReport(); } function testAccessEmptyData() public { @@ -512,20 +543,25 @@ contract StoreCoreGasTest is Test, StoreMock { bytes32[] memory key = new bytes32[](1); key[0] = bytes32("some.key"); - // !gasreport access non-existing record + startGasReport("access non-existing record"); StoreCore.getRecord(table, key); + endGasReport(); - // !gasreport access static field of non-existing record + startGasReport("access static field of non-existing record"); StoreCore.getField(table, key, 0); + endGasReport(); - // !gasreport access dynamic field of non-existing record + startGasReport("access dynamic field of non-existing record"); StoreCore.getField(table, key, 1); + endGasReport(); - // !gasreport access length of dynamic field of non-existing record + startGasReport("access length of dynamic field of non-existing record"); StoreCore.getFieldLength(table, key, 1, schema); + endGasReport(); - // !gasreport access slice of dynamic field of non-existing record + startGasReport("access slice of dynamic field of non-existing record"); StoreCore.getFieldSlice(table, key, 1, schema, 0, 0); + endGasReport(); } function testHooks() public { @@ -540,21 +576,25 @@ contract StoreCoreGasTest is Test, StoreMock { // Create subscriber MirrorSubscriber subscriber = new MirrorSubscriber(table, schema, defaultKeySchema); - // !gasreport register subscriber + startGasReport("register subscriber"); StoreCore.registerStoreHook(table, subscriber); + endGasReport(); bytes memory data = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); - // !gasreport set record on table with subscriber + startGasReport("set record on table with subscriber"); StoreCore.setRecord(table, key, data); + endGasReport(); data = abi.encodePacked(bytes16(0x1112131415161718191a1b1c1d1e1f20)); - // !gasreport set static field on table with subscriber + startGasReport("set static field on table with subscriber"); StoreCore.setField(table, key, 0, data); + endGasReport(); - // !gasreport delete record on table with subscriber + startGasReport("delete record on table with subscriber"); StoreCore.deleteRecord(table, key); + endGasReport(); } function testHooksDynamicData() public { @@ -569,8 +609,9 @@ contract StoreCoreGasTest is Test, StoreMock { // Create subscriber MirrorSubscriber subscriber = new MirrorSubscriber(table, schema, defaultKeySchema); - // !gasreport register subscriber + startGasReport("register subscriber"); StoreCore.registerStoreHook(table, subscriber); + endGasReport(); uint32[] memory arrayData = new uint32[](1); arrayData[0] = 0x01020304; @@ -580,8 +621,9 @@ contract StoreCoreGasTest is Test, StoreMock { bytes memory staticData = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); bytes memory data = abi.encodePacked(staticData, dynamicData); - // !gasreport set (dynamic) record on table with subscriber + startGasReport("set (dynamic) record on table with subscriber"); StoreCore.setRecord(table, key, data); + endGasReport(); // Update dynamic data arrayData[0] = 0x11121314; @@ -589,11 +631,13 @@ contract StoreCoreGasTest is Test, StoreMock { dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); data = abi.encodePacked(staticData, dynamicData); - // !gasreport set (dynamic) field on table with subscriber + startGasReport("set (dynamic) field on table with subscriber"); StoreCore.setField(table, key, 1, arrayDataBytes); + endGasReport(); - // !gasreport delete (dynamic) record on table with subscriber + startGasReport("delete (dynamic) record on table with subscriber"); StoreCore.deleteRecord(table, key); + endGasReport(); } } diff --git a/packages/store/test/StoreMetadata.t.sol b/packages/store/test/StoreMetadata.t.sol index d3837cb8b0..4d98cc10f1 100644 --- a/packages/store/test/StoreMetadata.t.sol +++ b/packages/store/test/StoreMetadata.t.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { StoreMetadata, StoreMetadataData } from "../src/codegen/Tables.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; import { Schema } from "../src/Schema.sol"; -contract StoreMetadataTest is Test, StoreReadWithStubs { +contract StoreMetadataTest is Test, GasReporter, StoreReadWithStubs { function testSetAndGet() public { bytes32 tableId = "1"; string memory tableName = "firstTable"; @@ -15,11 +16,13 @@ contract StoreMetadataTest is Test, StoreReadWithStubs { fieldNames[0] = "firstField"; fieldNames[1] = "secondField"; - // !gasreport set record in StoreMetadataTable + startGasReport("set record in StoreMetadataTable"); StoreMetadata.set({ tableId: tableId, tableName: tableName, abiEncodedFieldNames: abi.encode(fieldNames) }); + endGasReport(); - // !gasreport get record from StoreMetadataTable + startGasReport("get record from StoreMetadataTable"); StoreMetadataData memory metadata = StoreMetadata.get(tableId); + endGasReport(); assertEq(metadata.tableName, tableName); assertEq(metadata.abiEncodedFieldNames, abi.encode(fieldNames)); diff --git a/packages/store/test/StoreSwitch.t.sol b/packages/store/test/StoreSwitch.t.sol index f64f299a57..1d186136e7 100644 --- a/packages/store/test/StoreSwitch.t.sol +++ b/packages/store/test/StoreSwitch.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; import { StoreSwitch } from "../src/StoreSwitch.sol"; @@ -24,14 +25,15 @@ contract StoreSwitchTestStore is StoreReadWithStubs { } // Mock system to wrap StoreSwitch.isDelegateCall() -contract MockSystem { - function isDelegateCall() public view returns (bool isDelegate) { - // !gasreport check if delegatecall +contract MockSystem is GasReporter { + function isDelegateCall() public returns (bool isDelegate) { + startGasReport("check if delegatecall"); isDelegate = StoreSwitch.isDelegateCall(); + endGasReport(); } } -contract StoreSwitchTest is Test { +contract StoreSwitchTest is Test, GasReporter { StoreSwitchTestStore store; function setUp() public { diff --git a/packages/store/test/Vector2.t.sol b/packages/store/test/Vector2.t.sol index e21889ee4a..16791ddd8c 100644 --- a/packages/store/test/Vector2.t.sol +++ b/packages/store/test/Vector2.t.sol @@ -1,16 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Vector2, Vector2Data, Vector2TableId } from "../src/codegen/Tables.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; import { Schema } from "../src/Schema.sol"; -contract Vector2Test is Test, StoreReadWithStubs { +contract Vector2Test is Test, GasReporter, StoreReadWithStubs { function testRegisterAndGetSchema() public { - // !gasreport register Vector2 schema + startGasReport("register Vector2 schema"); Vector2.registerSchema(); + endGasReport(); Schema registeredSchema = StoreCore.getSchema(Vector2TableId); Schema declaredSchema = Vector2.getSchema(); @@ -22,11 +24,13 @@ contract Vector2Test is Test, StoreReadWithStubs { Vector2.registerSchema(); bytes32 key = keccak256("somekey"); - // !gasreport set Vector2 record + startGasReport("set Vector2 record"); Vector2.set({ key: key, x: 1, y: 2 }); + endGasReport(); - // !gasreport get Vector2 record + startGasReport("get Vector2 record"); Vector2Data memory vector = Vector2.get(key); + endGasReport(); assertEq(vector.x, 1); assertEq(vector.y, 2); diff --git a/packages/store/test/tables/Callbacks.t.sol b/packages/store/test/tables/Callbacks.t.sol index a39ab97770..609ee6dedc 100644 --- a/packages/store/test/tables/Callbacks.t.sol +++ b/packages/store/test/tables/Callbacks.t.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { StoreReadWithStubs } from "../../src/StoreReadWithStubs.sol"; import { Callbacks } from "../../src/codegen/Tables.sol"; -contract CallbacksTest is Test, StoreReadWithStubs { +contract CallbacksTest is Test, GasReporter, StoreReadWithStubs { function testSetAndGet() public { Callbacks.registerSchema(); bytes32 key = keccak256("somekey"); @@ -13,17 +14,20 @@ contract CallbacksTest is Test, StoreReadWithStubs { bytes24[] memory callbacks = new bytes24[](1); callbacks[0] = bytes24(abi.encode(this.testSetAndGet)); - // !gasreport set field in Callbacks + startGasReport("set field in Callbacks"); Callbacks.set(key, callbacks); + endGasReport(); - // !gasreport get field from Callbacks (warm) + startGasReport("get field from Callbacks (warm)"); bytes24[] memory returnedCallbacks = Callbacks.get(key); + endGasReport(); assertEq(returnedCallbacks.length, callbacks.length); assertEq(returnedCallbacks[0], callbacks[0]); - // !gasreport push field to Callbacks + startGasReport("push field to Callbacks"); Callbacks.push(key, callbacks[0]); + endGasReport(); returnedCallbacks = Callbacks.get(key); diff --git a/packages/store/test/tables/Hooks.t.sol b/packages/store/test/tables/Hooks.t.sol index 833056b5d5..4f93ced1d0 100644 --- a/packages/store/test/tables/Hooks.t.sol +++ b/packages/store/test/tables/Hooks.t.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { StoreReadWithStubs } from "../../src/StoreReadWithStubs.sol"; import { Hooks } from "../../src/codegen/Tables.sol"; -contract HooksTest is Test, StoreReadWithStubs { +contract HooksTest is Test, GasReporter, StoreReadWithStubs { function testSetAndGet() public { // Hooks schema is already registered by StoreCore bytes32 key = keccak256("somekey"); @@ -13,17 +14,20 @@ contract HooksTest is Test, StoreReadWithStubs { address[] memory addresses = new address[](1); addresses[0] = address(this); - // !gasreport set field in Hooks + startGasReport("set field in Hooks"); Hooks.set(key, addresses); + endGasReport(); - // !gasreport get field from Hooks (warm) + startGasReport("get field from Hooks (warm)"); address[] memory returnedAddresses = Hooks.get(key); + endGasReport(); assertEq(returnedAddresses.length, addresses.length); assertEq(returnedAddresses[0], addresses[0]); - // !gasreport push field to Hooks + startGasReport("push field to Hooks"); Hooks.push(key, addresses[0]); + endGasReport(); returnedAddresses = Hooks.get(key); diff --git a/packages/store/test/tightcoder/DecodeSlice.t.sol b/packages/store/test/tightcoder/DecodeSlice.t.sol index 96e42fb387..7bd2f417b1 100644 --- a/packages/store/test/tightcoder/DecodeSlice.t.sol +++ b/packages/store/test/tightcoder/DecodeSlice.t.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { SliceLib } from "../../src/Slice.sol"; -contract DecodeSliceTest is Test { +contract DecodeSliceTest is Test, GasReporter { function testToBytes32Array() public { bytes memory input = new bytes(64); input[0] = 0x01; @@ -12,8 +13,9 @@ contract DecodeSliceTest is Test { input[32] = 0x03; input[63] = 0x04; - // !gasreport decode packed bytes32[] + startGasReport("decode packed bytes32[]"); bytes32[] memory output = SliceLib.fromBytes(input).decodeArray_bytes32(); + endGasReport(); assertEq(output.length, 2); assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); @@ -37,8 +39,9 @@ contract DecodeSliceTest is Test { uint32 num2 = 0x05060708; bytes memory input = abi.encodePacked(num1, num2); - // !gasreport decode packed uint32[] + startGasReport("decode packed uint32[]"); uint32[] memory arr = SliceLib.fromBytes(input).decodeArray_uint32(); + endGasReport(); assertEq(arr.length, 2); assertEq(arr[0], num1); diff --git a/packages/store/test/tightcoder/EncodeArray.t.sol b/packages/store/test/tightcoder/EncodeArray.t.sol index 7457a6c9a8..79a1fc4010 100644 --- a/packages/store/test/tightcoder/EncodeArray.t.sol +++ b/packages/store/test/tightcoder/EncodeArray.t.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Bytes } from "../../src/Bytes.sol"; import { EncodeArray } from "../../src/tightcoder/EncodeArray.sol"; -contract EncodeArrayTest is Test { +contract EncodeArrayTest is Test, GasReporter { function testEncodeBytesArray() public { bytes[] memory input = new bytes[](2); input[0] = new bytes(32); @@ -15,8 +16,9 @@ contract EncodeArrayTest is Test { input[1][0] = 0x03; input[1][31] = 0x04; - // !gasreport encode packed bytes[] + startGasReport("encode packed bytes[]"); bytes memory output = EncodeArray.encode(input); + endGasReport(); assertEq(output.length, 64); assertEq(uint256(Bytes.toBytes32(output, 0)), 0x0100000000000000000000000000000000000000000000000000000000000002); @@ -30,8 +32,9 @@ contract EncodeArrayTest is Test { input[0] = val0; input[1] = val1; - // !gasreport encode packed uint8[] + startGasReport("encode packed uint8[]"); bytes memory output = EncodeArray.encode(input); + endGasReport(); assertEq(output, abi.encodePacked(val0, val1)); } @@ -45,8 +48,9 @@ contract EncodeArrayTest is Test { input[1] = val1; input[2] = val2; - // !gasreport encode packed uint16[] + startGasReport("encode packed uint16[]"); bytes memory output = EncodeArray.encode(input); + endGasReport(); assertEq(output, abi.encodePacked(val0, val1, val2)); } @@ -58,8 +62,9 @@ contract EncodeArrayTest is Test { input[0] = val0; input[1] = val1; - // !gasreport encode packed uint32[] + startGasReport("encode packed uint32[]"); bytes memory output = EncodeArray.encode(input); + endGasReport(); assertEq(output, abi.encodePacked(val0, val1)); } diff --git a/packages/store/test/tightcoder/TightCoder.t.sol b/packages/store/test/tightcoder/TightCoder.t.sol index fea33f599e..a41dbe48c3 100644 --- a/packages/store/test/tightcoder/TightCoder.t.sol +++ b/packages/store/test/tightcoder/TightCoder.t.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { SliceLib } from "../../src/Slice.sol"; import { EncodeArray } from "../../src/tightcoder/EncodeArray.sol"; -contract TightCoderTest is Test { +contract TightCoderTest is Test, GasReporter { function testFromAndToUint32Array() public { uint32[] memory input = new uint32[](2); input[0] = 0x01020304; @@ -15,8 +16,9 @@ contract TightCoderTest is Test { bytes memory packed = EncodeArray.encode(input); assertEq(packed.length, 8); - // !gasreport decode packed uint32[] + startGasReport("decode packed uint32[]"); uint32[] memory output = SliceLib.fromBytes(packed).decodeArray_uint32(); + endGasReport(); assertEq(output.length, 2); assertEq(output[0], 0x01020304); @@ -28,13 +30,15 @@ contract TightCoderTest is Test { input[0] = bytes24(0x0102030405060708090a0b0c0d0e0f101112131415161718); input[1] = bytes24(0x19202122232425262728292a2b2c2d2e2f30313233343536); - // !gasreport encode packed bytes24[] + startGasReport("encode packed bytes24[]"); bytes memory packed = EncodeArray.encode(input); + endGasReport(); assertEq(packed.length, 48); - // !gasreport decode packed uint32[] + startGasReport("decode packed uint32[]"); bytes24[] memory output = SliceLib.fromBytes(packed).decodeArray_bytes24(); + endGasReport(); assertEq(output.length, 2); assertEq(output[0], input[0]); diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index f40c7f71d5..110a8602f0 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -1,320 +1,320 @@ [ { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testInstallComposite", "name": "install keys in table module", - "functionCall": "world.installRootModule(keysInTableModule, abi.encode(tableId))", - "gasUsed": 1286932 + "gasUsed": 1286949 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testInstallGas", "name": "install keys in table module", - "functionCall": "world.installRootModule(keysInTableModule, abi.encode(tableId))", - "gasUsed": 1286932 + "gasUsed": 1286949 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "functionCall": "world.setRecord(namespace, name, keyTuple1, abi.encodePacked(value))", - "gasUsed": 193894 + "gasUsed": 193904 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testInstallSingleton", "name": "install keys in table module", - "functionCall": "world.installRootModule(keysInTableModule, abi.encode(tableId))", - "gasUsed": 1286932 + "gasUsed": 1286949 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "functionCall": "world.installRootModule(keysInTableModule, abi.encode(tableId))", - "gasUsed": 1286932 + "gasUsed": 1286949 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "functionCall": "world.setRecord(namespace, compositeName, keyTupleA, abi.encodePacked(value2))", - "gasUsed": 30066 + "gasUsed": 30071 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "functionCall": "world.deleteRecord(namespace, compositeName, keyTupleA)", - "gasUsed": 292110 + "gasUsed": 292112 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "functionCall": "world.installRootModule(keysInTableModule, abi.encode(tableId))", - "gasUsed": 1286932 + "gasUsed": 1286949 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "functionCall": "world.setRecord(namespace, name, keyTuple1, abi.encodePacked(value2))", - "gasUsed": 28689 + "gasUsed": 28685 }, { - "source": "test/KeysInTableModule.t.sol", + "file": "test/KeysInTableModule.t.sol", + "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "functionCall": "world.deleteRecord(namespace, name, keyTuple1)", - "gasUsed": 153364 + "gasUsed": 153351 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testGetKeysWithValue", "name": "install keys with value module", - "functionCall": "world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))", - "gasUsed": 626500 + "gasUsed": 626517 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testGetKeysWithValue", "name": "Get list of keys with a given value", - "functionCall": "bytes32[] memory keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value1))", - "gasUsed": 7654 + "gasUsed": 7715 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testGetTargetTableSelector", "name": "compute the target table selector", - "functionCall": "bytes32 targetTableSelector = getTargetTableSelector(MODULE_NAMESPACE, sourceTableId)", - "gasUsed": 2240 + "gasUsed": 2233 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testInstall", "name": "install keys with value module", - "functionCall": "world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))", - "gasUsed": 626500 + "gasUsed": 626517 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "functionCall": "world.setRecord(namespace, sourceName, keyTuple1, abi.encodePacked(value))", - "gasUsed": 165712 + "gasUsed": 165706 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "functionCall": "world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))", - "gasUsed": 626500 + "gasUsed": 626517 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "functionCall": "world.setRecord(namespace, sourceName, keyTuple1, abi.encodePacked(value2))", - "gasUsed": 135364 + "gasUsed": 135358 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "functionCall": "world.deleteRecord(namespace, sourceName, keyTuple1)", - "gasUsed": 54309 + "gasUsed": 54296 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testSetField", "name": "install keys with value module", - "functionCall": "world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))", - "gasUsed": 626500 + "gasUsed": 626517 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "functionCall": "world.setField(namespace, sourceName, keyTuple1, 0, abi.encodePacked(value1))", - "gasUsed": 173726 + "gasUsed": 173711 }, { - "source": "test/KeysWithValueModule.t.sol", + "file": "test/KeysWithValueModule.t.sol", + "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "functionCall": "world.setField(namespace, sourceName, keyTuple1, 0, abi.encodePacked(value2))", - "gasUsed": 138084 + "gasUsed": 138069 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 168826 + "gasUsed": 169287 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 78844 + "gasUsed": 79069 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 232573 + "gasUsed": 233190 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 146143 + "gasUsed": 146483 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 144801 + "gasUsed": 145185 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testCombinedHasValueQuery", "name": "CombinedHasValueQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 19555 + "gasUsed": 19620 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testHasQuery", "name": "HasQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 33032 + "gasUsed": 33096 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 11272548 + "gasUsed": 11312083 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 1056092 + "gasUsed": 1060034 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testHasValueQuery", "name": "HasValueQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 9494 + "gasUsed": 9521 }, { - "source": "test/query.t.sol", + "file": "test/query.t.sol", + "test": "testNotValueQuery", "name": "NotValueQuery", - "functionCall": "bytes32[][] memory keyTuples = query(world, fragments)", - "gasUsed": 72026 + "gasUsed": 72250 }, { - "source": "test/SnapSyncModule.t.sol", + "file": "test/SnapSyncModule.t.sol", + "test": "testSnapSyncGas", "name": "Call snap sync on a table with 1 record", - "functionCall": "SyncRecord[] memory records = ISnapSyncSystem(address(world)).snapSync_system_getRecords(tableId, limit, 0)", - "gasUsed": 43813 + "gasUsed": 43864 }, { - "source": "test/SnapSyncModule.t.sol", + "file": "test/SnapSyncModule.t.sol", + "test": "testSnapSyncGas", "name": "Call snap sync on a table with 2 records", - "functionCall": "records = ISnapSyncSystem(address(world)).snapSync_system_getRecords(tableId, limit, 0)", - "gasUsed": 62449 + "gasUsed": 62552 }, { - "source": "test/UniqueEntityModule.t.sol", + "file": "test/UniqueEntityModule.t.sol", + "test": "testInstall", "name": "install unique entity module", - "functionCall": "world.installModule(uniqueEntityModule, new bytes(0))", - "gasUsed": 824012 + "gasUsed": 824002 }, { - "source": "test/UniqueEntityModule.t.sol", + "file": "test/UniqueEntityModule.t.sol", + "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "functionCall": "uint256 uniqueEntity = uint256(getUniqueEntity(world))", - "gasUsed": 73389 + "gasUsed": 73376 }, { - "source": "test/UniqueEntityModule.t.sol", + "file": "test/UniqueEntityModule.t.sol", + "test": "testInstallRoot", "name": "installRoot unique entity module", - "functionCall": "world.installRootModule(uniqueEntityModule, new bytes(0))", - "gasUsed": 794087 + "gasUsed": 794077 }, { - "source": "test/UniqueEntityModule.t.sol", + "file": "test/UniqueEntityModule.t.sol", + "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "functionCall": "uint256 uniqueEntity = uint256(getUniqueEntity(world))", - "gasUsed": 73389 + "gasUsed": 73376 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testDeleteRecord", "name": "Delete record", - "functionCall": "world.deleteRecord(namespace, name, singletonKey)", - "gasUsed": 16152 + "gasUsed": 16148 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testPushToField", "name": "Push data to the table", - "functionCall": "world.pushToField(namespace, name, keyTuple, 0, encodedData)", - "gasUsed": 96514 + "gasUsed": 96510 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "functionCall": "bytes4 funcSelector1 = world.registerFunctionSelector(namespace, name, \"\", \"\")", - "gasUsed": 87024 + "gasUsed": 87011 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "functionCall": "bytes4 funcSelector2 = world.registerRootFunctionSelector(namespace, name, worldFunc, 0)", - "gasUsed": 78208 + "gasUsed": 78195 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "functionCall": "bytes4 functionSelector = world.registerFunctionSelector(namespace, name, \"msgSender\", \"()\")", - "gasUsed": 107622 + "gasUsed": 107606 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testRegisterNamespace", "name": "Register a new namespace", - "functionCall": "world.registerNamespace(\"test\")", - "gasUsed": 161681 + "gasUsed": 161671 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "functionCall": "bytes4 functionSelector = world.registerRootFunctionSelector(namespace, name, worldFunc, sysFunc)", - "gasUsed": 94114 + "gasUsed": 94098 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testRegisterTable", "name": "Register a new table in the namespace", - "functionCall": "bytes32 tableSelector = world.registerTable(namespace, table, schema, defaultKeySchema)", - "gasUsed": 262408 + "gasUsed": 262386 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testSetField", "name": "Write data to a table field", - "functionCall": "world.setField(namespace, name, singletonKey, 0, abi.encodePacked(true))", - "gasUsed": 44803 + "gasUsed": 44812 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testSetMetadata", "name": "Set metadata", - "functionCall": "world.setMetadata(namespace, name, tableName, fieldNames)", - "gasUsed": 283358 + "gasUsed": 283379 }, { - "source": "test/World.t.sol", + "file": "test/World.t.sol", + "test": "testSetRecord", "name": "Write data to the table", - "functionCall": "Bool.set(world, tableId, true)", "gasUsed": 42692 }, { - "source": "test/WorldDynamicUpdate.t.sol", + "file": "test/WorldDynamicUpdate.t.sol", + "test": "testPopFromField", "name": "pop 1 address (cold)", - "functionCall": "world.popFromField(namespace, name, keyTuple, 0, byteLengthToPop)", - "gasUsed": 38042 + "gasUsed": 38032 }, { - "source": "test/WorldDynamicUpdate.t.sol", + "file": "test/WorldDynamicUpdate.t.sol", + "test": "testPopFromField", "name": "pop 1 address (warm)", - "functionCall": "world.popFromField(namespace, name, keyTuple, 0, byteLengthToPop)", - "gasUsed": 22836 + "gasUsed": 22829 }, { - "source": "test/WorldDynamicUpdate.t.sol", + "file": "test/WorldDynamicUpdate.t.sol", + "test": "testUpdateInField", "name": "updateInField 1 item (cold)", - "functionCall": "world.updateInField(namespace, name, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate))", - "gasUsed": 40376 + "gasUsed": 40361 }, { - "source": "test/WorldDynamicUpdate.t.sol", + "file": "test/WorldDynamicUpdate.t.sol", + "test": "testUpdateInField", "name": "updateInField 1 item (warm)", - "functionCall": "world.updateInField(namespace, name, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate))", - "gasUsed": 25574 + "gasUsed": 25565 } ] diff --git a/packages/world/package.json b/packages/world/package.json index 9a19c9c87c..e53e005648 100644 --- a/packages/world/package.json +++ b/packages/world/package.json @@ -47,7 +47,7 @@ "clean:typechain": "rimraf types", "dev": "tsup --watch", "docs": "rimraf API && hardhat docgen", - "gas-report": "../cli/dist/mud.js gas-report --path test/* --path test/**/* --save gas-report.json", + "gas-report": "../cli/dist/mud.js gas-report --save gas-report.json", "lint": "solhint --config ./.solhint.json 'src/**/*.sol'", "test": "tsc --noEmit && vitest --run && forge test" }, @@ -62,6 +62,7 @@ "zod": "^3.21.4" }, "devDependencies": { + "@latticexyz/std-contracts": "workspace:*", "@typechain/ethers-v5": "^10.2.0", "@types/ejs": "^3.1.1", "@types/glob": "^7.2.0", diff --git a/packages/world/test/KeysInTableModule.t.sol b/packages/world/test/KeysInTableModule.t.sol index 1ff6dada72..51a7ca8b0e 100644 --- a/packages/world/test/KeysInTableModule.t.sol +++ b/packages/world/test/KeysInTableModule.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; @@ -16,7 +17,7 @@ import { KeysInTableModule } from "../src/modules/keysintable/KeysInTableModule. import { getKeysInTable } from "../src/modules/keysintable/getKeysInTable.sol"; import { hasKey } from "../src/modules/keysintable/hasKey.sol"; -contract KeysInTableModuleTest is Test { +contract KeysInTableModuleTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; KeysInTableModule keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times @@ -70,8 +71,9 @@ contract KeysInTableModuleTest is Test { // Install the index module // TODO: add support for installing this via installModule // -> requires `callFrom` for the module to be able to register a hook in the name of the original caller - // !gasreport install keys in table module + startGasReport("install keys in table module"); world.installRootModule(keysInTableModule, abi.encode(tableId)); + endGasReport(); world.installRootModule(keysInTableModule, abi.encode(singletonTableId)); world.installRootModule(keysInTableModule, abi.encode(compositeTableId)); } @@ -117,14 +119,16 @@ contract KeysInTableModuleTest is Test { } function testInstallGas() public { + // call fuzzed test manually to get gas report testInstall(val1); } function testInstall(uint256 value) public { _installKeysInTableModule(); // Set a value in the source table - // !gasreport set a record on a table with keysInTableModule installed + startGasReport("set a record on a table with keysInTableModule installed"); world.setRecord(namespace, name, keyTuple1, abi.encodePacked(value)); + endGasReport(); // Get the list of keys in this target table bytes32[][] memory keysInTable = getKeysInTable(world, tableId); @@ -141,8 +145,9 @@ contract KeysInTableModuleTest is Test { keyTuple[0] = keyA; // Set a value in the source table - // !gasreport set a record on a table with keysInTableModule installed + startGasReport("set a record on a table with keysInTableModule installed (first)"); world.setRecord(namespace, name, keyTuple, abi.encodePacked(value1)); + endGasReport(); // Get the list of keys in the first target table bytes32[][] memory keysInTable = getKeysInTable(world, tableId); @@ -160,8 +165,9 @@ contract KeysInTableModuleTest is Test { keyTuple[0] = keyB; // Set a value in the source table - // !gasreport set a record on a table with keysInTableModule installed + startGasReport("set a record on a table with keysInTableModule installed (second)"); world.setRecord(namespace, sourceFile2, keyTuple, abi.encodePacked(value2)); + endGasReport(); // Get the list of keys in the second target table keysInTable = getKeysInTable(world, sourceTableId2); @@ -172,6 +178,7 @@ contract KeysInTableModuleTest is Test { } function testSetAndDeleteRecordHookGas() public { + // call fuzzed test manually to get gas report testSetAndDeleteRecordHook(val1, val2); } @@ -200,8 +207,9 @@ contract KeysInTableModuleTest is Test { assertEq(keysInTable[1][0], key2, "4"); // Change the value of the first key - // !gasreport change a record on a table with keysInTableModule installed + startGasReport("change a record on a table with keysInTableModule installed"); world.setRecord(namespace, name, keyTuple1, abi.encodePacked(value2)); + endGasReport(); // Get the list of keys in the target table keysInTable = getKeysInTable(world, tableId); @@ -212,8 +220,9 @@ contract KeysInTableModuleTest is Test { assertEq(keysInTable[1][0], key2, "7"); // Delete the first key - // !gasreport delete a record on a table with keysInTableModule installed + startGasReport("delete a record on a table with keysInTableModule installed"); world.deleteRecord(namespace, name, keyTuple1); + endGasReport(); // Get the list of keys in the target table keysInTable = getKeysInTable(world, tableId); @@ -224,6 +233,7 @@ contract KeysInTableModuleTest is Test { } function testSetAndDeleteRecordHookCompositeGas() public { + // call fuzzed test manually to get gas report testSetAndDeleteRecordHookComposite(val1, val2); } @@ -268,8 +278,9 @@ contract KeysInTableModuleTest is Test { } // Change the value of the first key - // !gasreport change a composite record on a table with keysInTableModule installed + startGasReport("change a composite record on a table with keysInTableModule installed"); world.setRecord(namespace, compositeName, keyTupleA, abi.encodePacked(value2)); + endGasReport(); // Get the list of keys in the target table keysInTable = getKeysInTable(world, compositeTableId); @@ -284,8 +295,9 @@ contract KeysInTableModuleTest is Test { } // Delete the first key - // !gasreport delete a composite record on a table with keysInTableModule installed + startGasReport("delete a composite record on a table with keysInTableModule installed"); world.deleteRecord(namespace, compositeName, keyTupleA); + endGasReport(); // Get the list of keys in the target table keysInTable = getKeysInTable(world, compositeTableId); @@ -301,8 +313,9 @@ contract KeysInTableModuleTest is Test { _installKeysInTableModule(); // Set a value in the source table - // !gasreport set a field on a table with keysInTableModule installed + startGasReport("set a field on a table with keysInTableModule installed"); world.setField(namespace, name, keyTuple1, 0, abi.encodePacked(value1)); + endGasReport(); // Get the list of keys in the target table bytes32[][] memory keysInTable = getKeysInTable(world, tableId); @@ -312,8 +325,9 @@ contract KeysInTableModuleTest is Test { assertEq(keysInTable[0][0], key1); // Change the value using setField - // !gasreport change a field on a table with keysInTableModule installed + startGasReport("change a field on a table with keysInTableModule installed"); world.setField(namespace, name, keyTuple1, 0, abi.encodePacked(value2)); + endGasReport(); // Get the list of keys in the target table keysInTable = getKeysInTable(world, tableId); @@ -329,8 +343,9 @@ contract KeysInTableModuleTest is Test { // Set a value in the source table world.setRecord(namespace, name, keyTuple1, abi.encodePacked(value1)); - // !gasreport Get list of keys in a given table + startGasReport("Get list of keys in a given table"); bytes32[][] memory keysInTable = getKeysInTable(world, tableId); + endGasReport(); // Assert that the list is correct assertEq(keysInTable.length, 1); diff --git a/packages/world/test/KeysWithValueModule.t.sol b/packages/world/test/KeysWithValueModule.t.sol index 907fba2b76..2a3c0cabf2 100644 --- a/packages/world/test/KeysWithValueModule.t.sol +++ b/packages/world/test/KeysWithValueModule.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; @@ -18,7 +19,7 @@ import { KeysWithValue } from "../src/modules/keyswithvalue/tables/KeysWithValue import { getKeysWithValue } from "../src/modules/keyswithvalue/getKeysWithValue.sol"; import { getTargetTableSelector } from "../src/modules/utils/getTargetTableSelector.sol"; -contract KeysWithValueModuleTest is Test { +contract KeysWithValueModuleTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; KeysWithValueModule keysWithValueModule = new KeysWithValueModule(); // Modules can be deployed once and installed multiple times @@ -55,8 +56,9 @@ contract KeysWithValueModuleTest is Test { // Install the index module // TODO: add support for installing this via installModule // -> requires `callFrom` for the module to be able to register a hook in the name of the original caller - // !gasreport install keys with value module + startGasReport("install keys with value module"); world.installRootModule(keysWithValueModule, abi.encode(sourceTableId)); + endGasReport(); } function testInstall() public { @@ -64,8 +66,9 @@ contract KeysWithValueModuleTest is Test { // Set a value in the source table uint256 value = 1; - // !gasreport set a record on a table with KeysWithValueModule installed + startGasReport("set a record on a table with KeysWithValueModule installed"); world.setRecord(namespace, sourceName, keyTuple1, abi.encodePacked(value)); + endGasReport(); // Get the list of entities with this value from the target table bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value))); @@ -104,8 +107,9 @@ contract KeysWithValueModuleTest is Test { // Change the value of the first key uint256 value2 = 2; - // !gasreport change a record on a table with KeysWithValueModule installed + startGasReport("change a record on a table with KeysWithValueModule installed"); world.setRecord(namespace, sourceName, keyTuple1, abi.encodePacked(value2)); + endGasReport(); // Get the list of entities with value1 from the target table keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); @@ -122,8 +126,9 @@ contract KeysWithValueModuleTest is Test { assertEq(keysWithValue[0], key1, "8"); // Delete the first key - // !gasreport delete a record on a table with KeysWithValueModule installed + startGasReport("delete a record on a table with KeysWithValueModule installed"); world.deleteRecord(namespace, sourceName, keyTuple1); + endGasReport(); // Get the list of entities with value2 from the target table keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value2))); @@ -138,8 +143,9 @@ contract KeysWithValueModuleTest is Test { // Set a value in the source table uint256 value1 = 1; - // !gasreport set a field on a table with KeysWithValueModule installed + startGasReport("set a field on a table with KeysWithValueModule installed"); world.setField(namespace, sourceName, keyTuple1, 0, abi.encodePacked(value1)); + endGasReport(); // Get the list of entities with value1 from the target table bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); @@ -151,8 +157,9 @@ contract KeysWithValueModuleTest is Test { uint256 value2 = 2; // Change the value using setField - // !gasreport change a field on a table with KeysWithValueModule installed + startGasReport("change a field on a table with KeysWithValueModule installed"); world.setField(namespace, sourceName, keyTuple1, 0, abi.encodePacked(value2)); + endGasReport(); // Get the list of entities with value1 from the target table keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); @@ -169,8 +176,9 @@ contract KeysWithValueModuleTest is Test { } function testGetTargetTableSelector() public { - // !gasreport compute the target table selector + startGasReport("compute the target table selector"); bytes32 targetTableSelector = getTargetTableSelector(MODULE_NAMESPACE, sourceTableId); + endGasReport(); // The first 8 bytes are the module namespace assertEq(bytes8(targetTableSelector), MODULE_NAMESPACE); @@ -190,8 +198,9 @@ contract KeysWithValueModuleTest is Test { world.setRecord(namespace, sourceName, keyTuple1, abi.encodePacked(value1)); - // !gasreport Get list of keys with a given value + startGasReport("Get list of keys with a given value"); bytes32[] memory keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value1)); + endGasReport(); // Assert that the list is correct assertEq(keysWithValue.length, 1); diff --git a/packages/world/test/SnapSyncModule.t.sol b/packages/world/test/SnapSyncModule.t.sol index bfc09a04bd..4209deb0d9 100644 --- a/packages/world/test/SnapSyncModule.t.sol +++ b/packages/world/test/SnapSyncModule.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; @@ -28,7 +29,7 @@ interface ISnapSyncSystem { function snapSync_system_getNumKeysInTable(bytes32 tableId) external view returns (uint256); } -contract SnapSyncModuleTest is Test { +contract SnapSyncModuleTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; KeysInTableModule keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times @@ -90,8 +91,9 @@ contract SnapSyncModuleTest is Test { uint256 limit = ISnapSyncSystem(address(world)).snapSync_system_getNumKeysInTable(tableId); - // !gasreport Call snap sync on a table with 1 record + startGasReport("Call snap sync on a table with 1 record"); SyncRecord[] memory records = ISnapSyncSystem(address(world)).snapSync_system_getRecords(tableId, limit, 0); + endGasReport(); // Assert that the list is correct assertEq(records.length, 1); @@ -103,8 +105,9 @@ contract SnapSyncModuleTest is Test { limit = ISnapSyncSystem(address(world)).snapSync_system_getNumKeysInTable(tableId); - // !gasreport Call snap sync on a table with 2 records + startGasReport("Call snap sync on a table with 2 records"); records = ISnapSyncSystem(address(world)).snapSync_system_getRecords(tableId, limit, 0); + endGasReport(); // Assert that the list is correct assertEq(records.length, 2); @@ -137,8 +140,9 @@ contract SnapSyncModuleTest is Test { uint256 limit = syncSystem.snapSync_system_getNumKeysInTable(compositeTableId); - // !gasreport Call snap sync on a table with 1 record + startGasReport("Call snap sync on a table with 1 record"); SyncRecord[] memory records = syncSystem.snapSync_system_getRecords(compositeTableId, limit, 0); + endGasReport(); // Assert that the list is correct assertEq(records.length, 1); @@ -152,8 +156,9 @@ contract SnapSyncModuleTest is Test { limit = syncSystem.snapSync_system_getNumKeysInTable(compositeTableId); - // !gasreport Call snap sync on a table with 2 records + startGasReport("Call snap sync on a table with 2 records"); records = syncSystem.snapSync_system_getRecords(compositeTableId, limit, 0); + endGasReport(); // Assert that the list is correct assertEq(records.length, 2); @@ -184,8 +189,9 @@ contract SnapSyncModuleTest is Test { uint256 limit = syncSystem.snapSync_system_getNumKeysInTable(compositeTableId); - // !gasreport Call snap sync on a table with 1 record + startGasReport("Call snap sync on a table with 1 record"); SyncRecord[] memory records = syncSystem.snapSync_system_getRecords(compositeTableId, limit, 0); + endGasReport(); // Assert that the list is correct assertEq(records.length, 1); @@ -199,8 +205,9 @@ contract SnapSyncModuleTest is Test { limit = syncSystem.snapSync_system_getNumKeysInTable(compositeTableId); - // !gasreport Call snap sync on a table with 2 records + startGasReport("Call snap sync on a table with 2 records"); records = syncSystem.snapSync_system_getRecords(compositeTableId, limit, 0); + endGasReport(); // Assert that the list is correct assertEq(records.length, 2); diff --git a/packages/world/test/UniqueEntityModule.t.sol b/packages/world/test/UniqueEntityModule.t.sol index 031a5af96b..7b8a5dfa73 100644 --- a/packages/world/test/UniqueEntityModule.t.sol +++ b/packages/world/test/UniqueEntityModule.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { World } from "../src/World.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; @@ -15,7 +16,7 @@ import { getUniqueEntity } from "../src/modules/uniqueentity/getUniqueEntity.sol import { NAMESPACE, TABLE_NAME } from "../src/modules/uniqueentity/constants.sol"; import { ResourceSelector } from "../src/ResourceSelector.sol"; -contract UniqueEntityModuleTest is Test { +contract UniqueEntityModuleTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; @@ -28,11 +29,13 @@ contract UniqueEntityModuleTest is Test { } function testInstall() public { - // !gasreport install unique entity module + startGasReport("install unique entity module"); world.installModule(uniqueEntityModule, new bytes(0)); + endGasReport(); - // !gasreport get a unique entity nonce (non-root module) + startGasReport("get a unique entity nonce (non-root module)"); uint256 uniqueEntity = uint256(getUniqueEntity(world)); + endGasReport(); // Table must have the same entity set assertEq(UniqueEntity.get(world, tableId), uniqueEntity); @@ -42,11 +45,13 @@ contract UniqueEntityModuleTest is Test { } function testInstallRoot() public { - // !gasreport installRoot unique entity module + startGasReport("installRoot unique entity module"); world.installRootModule(uniqueEntityModule, new bytes(0)); + endGasReport(); - // !gasreport get a unique entity nonce (root module) + startGasReport("get a unique entity nonce (root module)"); uint256 uniqueEntity = uint256(getUniqueEntity(world)); + endGasReport(); // Table must have the same entity set assertEq(UniqueEntity.get(world, tableId), uniqueEntity); diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 69934c922b..be1e3c3349 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; + import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { IStoreHook } from "@latticexyz/store/src/IStore.sol"; @@ -129,7 +131,7 @@ contract WorldTestSystemHook is ISystemHook { } } -contract WorldTest is Test { +contract WorldTest is Test, GasReporter { using ResourceSelector for bytes32; event HelloWorld(); @@ -194,8 +196,9 @@ contract WorldTest is Test { } function testRegisterNamespace() public { - // !gasreport Register a new namespace + startGasReport("Register a new namespace"); world.registerNamespace("test"); + endGasReport(); // Expect the caller to be the namespace owner assertEq(NamespaceOwner.get(world, "test"), address(this), "caller should be namespace owner"); @@ -215,8 +218,9 @@ contract WorldTest is Test { bytes16 namespace = "testNamespace"; bytes16 table = "testTable"; - // !gasreport Register a new table in the namespace + startGasReport("Register a new table in the namespace"); bytes32 tableSelector = world.registerTable(namespace, table, schema, defaultKeySchema); + endGasReport(); // Expect the namespace to be created and owned by the caller assertEq(NamespaceOwner.get(world, namespace), address(this)); @@ -256,8 +260,9 @@ contract WorldTest is Test { // Register a table world.registerTable(namespace, name, schema, defaultKeySchema); - // !gasreport Set metadata + startGasReport("Set metadata"); world.setMetadata(namespace, name, tableName, fieldNames); + endGasReport(); // Expect the metadata to be set StoreMetadataData memory metadata = StoreMetadata.get(world, tableId); @@ -364,8 +369,9 @@ contract WorldTest is Test { // Register a new table bytes32 tableId = world.registerTable("testSetRecord", "testTable", Bool.getSchema(), defaultKeySchema); - // !gasreport Write data to the table + startGasReport("Write data to the table"); Bool.set(world, tableId, true); + endGasReport(); // Expect the data to be written assertTrue(Bool.get(world, tableId)); @@ -386,8 +392,9 @@ contract WorldTest is Test { // Register a new table bytes32 tableId = world.registerTable(namespace, name, Bool.getSchema(), defaultKeySchema); - // !gasreport Write data to a table field + startGasReport("Write data to a table field"); world.setField(namespace, name, singletonKey, 0, abi.encodePacked(true)); + endGasReport(); // Expect the data to be written assertTrue(Bool.get(world, tableId)); @@ -425,8 +432,9 @@ contract WorldTest is Test { dataToPush[2] = address(bytes20(keccak256("another address"))); bytes memory encodedData = EncodeArray.encode(dataToPush); - // !gasreport Push data to the table + startGasReport("Push data to the table"); world.pushToField(namespace, name, keyTuple, 0, encodedData); + endGasReport(); // Expect the data to be written assertEq(AddressArray.get(world, tableId, key), dataToPush); @@ -464,8 +472,9 @@ contract WorldTest is Test { world.setRecord(namespace, name, singletonKey, abi.encodePacked(true)); assertTrue(Bool.get(world, tableId)); - // !gasreport Delete record + startGasReport("Delete record"); world.deleteRecord(namespace, name, singletonKey); + endGasReport(); // expect it to be deleted assertFalse(Bool.get(world, tableId)); @@ -679,8 +688,9 @@ contract WorldTest is Test { WorldTestSystem system = new WorldTestSystem(); world.registerSystem(namespace, name, system, true); - // !gasreport Register a function selector + startGasReport("Register a function selector"); bytes4 functionSelector = world.registerFunctionSelector(namespace, name, "msgSender", "()"); + endGasReport(); string memory expectedWorldFunctionSignature = "testNamespace_testSystem_msgSender()"; bytes4 expectedWorldFunctionSelector = bytes4(keccak256(abi.encodePacked(expectedWorldFunctionSignature))); @@ -719,8 +729,9 @@ contract WorldTest is Test { vm.prank(address(world)); world.registerRootFunctionSelector(namespace, name, "smth", "smth"); - // !gasreport Register a root function selector + startGasReport("Register a root function selector"); bytes4 functionSelector = world.registerRootFunctionSelector(namespace, name, worldFunc, sysFunc); + endGasReport(); assertEq(functionSelector, worldFunc, "wrong function selector returned"); @@ -751,8 +762,9 @@ contract WorldTest is Test { WorldTestSystem system = new WorldTestSystem(); world.registerSystem(namespace, name, system, true); - // !gasreport Register a fallback system + startGasReport("Register a fallback system"); bytes4 funcSelector1 = world.registerFunctionSelector(namespace, name, "", ""); + endGasReport(); // Call the system's fallback function vm.expectEmit(true, true, true, true); @@ -762,8 +774,10 @@ contract WorldTest is Test { bytes4 worldFunc = bytes4(abi.encodeWithSignature("testSelector()")); - // !gasreport Register a root fallback system + startGasReport("Register a root fallback system"); bytes4 funcSelector2 = world.registerRootFunctionSelector(namespace, name, worldFunc, 0); + endGasReport(); + assertEq(funcSelector2, worldFunc, "wrong function selector returned"); // Call the system's fallback function diff --git a/packages/world/test/WorldDynamicUpdate.t.sol b/packages/world/test/WorldDynamicUpdate.t.sol index 49acc675d1..4e8acb9462 100644 --- a/packages/world/test/WorldDynamicUpdate.t.sol +++ b/packages/world/test/WorldDynamicUpdate.t.sol @@ -2,6 +2,8 @@ pragma solidity >=0.8.0; import { Test, console } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; + import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { IStoreHook } from "@latticexyz/store/src/IStore.sol"; @@ -22,7 +24,7 @@ import { CoreModule } from "../src/modules/core/CoreModule.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; -contract UpdateInFieldTest is Test { +contract UpdateInFieldTest is Test, GasReporter { using ResourceSelector for bytes32; event HookCalled(bytes data); @@ -87,8 +89,11 @@ contract UpdateInFieldTest is Test { // Pop 1 item uint256 byteLengthToPop = 20; - // !gasreport pop 1 address (cold) + + startGasReport("pop 1 address (cold)"); world.popFromField(namespace, name, keyTuple, 0, byteLengthToPop); + endGasReport(); + // Expect the data to be updated address[] memory loadedData = AddressArray.get(world, tableId, key); assertEq(loadedData.length, initData.length - 1); @@ -98,8 +103,11 @@ contract UpdateInFieldTest is Test { // Pop 1 more item byteLengthToPop = 20; - // !gasreport pop 1 address (warm) + + startGasReport("pop 1 address (warm)"); world.popFromField(namespace, name, keyTuple, 0, byteLengthToPop); + endGasReport(); + // Expect the data to be updated loadedData = AddressArray.get(world, tableId, key); assertEq(loadedData.length, initData.length - 2); @@ -142,10 +150,14 @@ contract UpdateInFieldTest is Test { // Update index 0 address[] memory dataForUpdate = new address[](1); dataForUpdate[0] = address(bytes20(keccak256("address for update"))); - // !gasreport updateInField 1 item (cold) + + startGasReport("updateInField 1 item (cold)"); world.updateInField(namespace, name, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate)); - // !gasreport updateInField 1 item (warm) + endGasReport(); + + startGasReport("updateInField 1 item (warm)"); world.updateInField(namespace, name, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate)); + endGasReport(); // Expect the data to be updated initData[0] = dataForUpdate[0]; diff --git a/packages/world/test/query.t.sol b/packages/world/test/query.t.sol index 1846228270..45c8e79c1e 100644 --- a/packages/world/test/query.t.sol +++ b/packages/world/test/query.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import "forge-std/Test.sol"; +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/std-contracts/src/test/GasReporter.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; @@ -16,7 +17,7 @@ import { KeysInTableModule } from "../src/modules/keysintable/KeysInTableModule. import { KeysWithValueModule } from "../src/modules/keyswithvalue/KeysWithValueModule.sol"; import { query, QueryFragment, QueryType } from "../src/modules/keysintable/query.sol"; -contract QueryTest is Test { +contract QueryTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; KeysInTableModule keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times @@ -92,8 +93,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](1); // The value argument is ignored in for Has query fragments fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); - // !gasreport HasQuery + startGasReport("HasQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 2); assertTrue(keyTuples[0][0] == key1[0]); @@ -110,8 +112,9 @@ contract QueryTest is Test { // Query should return all keys in table1 with value 1 QueryFragment[] memory fragments = new QueryFragment[](1); fragments[0] = QueryFragment(QueryType.HasValue, table1, abi.encode(1)); - // !gasreport HasValueQuery + startGasReport("HasValueQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 2); assertTrue(keyTuples[0][0] == key2[0]); @@ -132,8 +135,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](2); fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); fragments[1] = QueryFragment(QueryType.Has, table2, new bytes(0)); - // !gasreport CombinedHasQuery + startGasReport("CombinedHasQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 2); assertTrue(keyTuples[0][0] == key2[0]); @@ -155,8 +159,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](2); fragments[0] = QueryFragment(QueryType.HasValue, table1, abi.encode(1)); fragments[1] = QueryFragment(QueryType.HasValue, table2, abi.encode(1)); - // !gasreport CombinedHasValueQuery + startGasReport("CombinedHasValueQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 1); assertTrue(keyTuples[0][0] == key3[0]); @@ -178,8 +183,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](2); fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); fragments[1] = QueryFragment(QueryType.HasValue, table2, abi.encode(2)); - // !gasreport CombinedHasHasValueQuery + startGasReport("CombinedHasHasValueQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 2); assertTrue(keyTuples[0][0] == key2[0]); @@ -201,8 +207,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](2); fragments[0] = QueryFragment(QueryType.Has, table2, new bytes(0)); fragments[1] = QueryFragment(QueryType.Not, table1, new bytes(0)); - // !gasreport CombinedHasNotQuery + startGasReport("CombinedHasNotQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 1); assertTrue(keyTuples[0][0] == key4[0]); @@ -224,8 +231,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](2); fragments[0] = QueryFragment(QueryType.HasValue, table2, abi.encode(1)); fragments[1] = QueryFragment(QueryType.Not, table1, new bytes(0)); - // !gasreport CombinedHasValueNotQuery + startGasReport("CombinedHasValueNotQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 1); assertTrue(keyTuples[0][0] == key4[0]); @@ -251,8 +259,9 @@ contract QueryTest is Test { fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); fragments[1] = QueryFragment(QueryType.HasValue, table2, abi.encode(1)); fragments[2] = QueryFragment(QueryType.Not, table3, new bytes(0)); - // !gasreport CombinedHasHasValueNotQuery + startGasReport("CombinedHasHasValueNotQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 1); assertTrue(keyTuples[0][0] == key1[0]); @@ -270,8 +279,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](2); fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); fragments[1] = QueryFragment(QueryType.NotValue, table1, abi.encode(6)); - // !gasreport NotValueQuery + startGasReport("NotValueQuery"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 2); assertTrue(keyTuples[0][0] == key1[0]); @@ -292,8 +302,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](1); // The value argument is ignored in for Has query fragments fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); - // !gasreport HasQuery with 100 keys + startGasReport("HasQuery with 100 keys"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 100); } @@ -312,8 +323,9 @@ contract QueryTest is Test { QueryFragment[] memory fragments = new QueryFragment[](1); // The value argument is ignored in for Has query fragments fragments[0] = QueryFragment(QueryType.Has, table1, new bytes(0)); - // !gasreport HasQuery with 1000 keys + startGasReport("HasQuery with 1000 keys"); bytes32[][] memory keyTuples = query(world, fragments); + endGasReport(); assertTrue(keyTuples.length == 1000); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2f4721cd6..15b0a1b6a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1085,6 +1085,9 @@ importers: specifier: ^3.21.4 version: 3.21.4 devDependencies: + '@latticexyz/std-contracts': + specifier: workspace:* + version: link:../std-contracts '@typechain/ethers-v5': specifier: ^10.2.0 version: 10.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.1.1)(typescript@4.9.5) From 3439b53a3801bdc4e42d56900d745f5630d4e948 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 14:39:06 +0100 Subject: [PATCH 09/11] move CI gas report to tests, since it'll be cached --- .github/workflows/build.yml | 6 ------ .github/workflows/release.yml | 12 ------------ .github/workflows/test.yml | 6 ++++++ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e71a70e90..a7c94549f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,9 +29,3 @@ jobs: - name: Outdated files, run `pnpm build` and commit them uses: ./.github/actions/require-empty-diff - - - name: Generate gas reports - run: pnpm gas-report - - - name: Outdated files, run `pnpm gas-report` and commit them - uses: ./.github/actions/require-empty-diff diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3bb8374986..c32f25707f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,12 +30,6 @@ jobs: - name: Build uses: ./.github/actions/build - - name: Generate gas reports - run: pnpm gas-report - - - name: Outdated files, run `pnpm gas-report` and commit them - uses: ./.github/actions/require-empty-diff - - name: Set deployment token run: npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" env: @@ -64,12 +58,6 @@ jobs: - name: Build uses: ./.github/actions/build - - name: Generate gas reports - run: pnpm gas-report - - - name: Outdated files, run `pnpm gas-report` and commit them - uses: ./.github/actions/require-empty-diff - - name: Set deployment token run: npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 052f0768b0..939a09e004 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,6 +23,12 @@ jobs: - name: Run tests run: pnpm test + - name: Generate gas reports + run: pnpm gas-report + + - name: Outdated files, run `pnpm gas-report` and commit them + uses: ./.github/actions/require-empty-diff + - name: Lint run: pnpm lint From 3289c2ba554b7064017e9ade25caec42a666e62d Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 14:58:50 +0100 Subject: [PATCH 10/11] make gas report default off --- packages/cli/src/commands/gas-report.ts | 1 + .../std-contracts/src/test/GasReporter.sol | 12 +- packages/std-contracts/test/GasReporter.t.sol | 5 +- packages/store/gas-report.json | 240 +++++++++--------- 4 files changed, 132 insertions(+), 126 deletions(-) diff --git a/packages/cli/src/commands/gas-report.ts b/packages/cli/src/commands/gas-report.ts index 0631c3e1d4..9fea453b2d 100644 --- a/packages/cli/src/commands/gas-report.ts +++ b/packages/cli/src/commands/gas-report.ts @@ -102,6 +102,7 @@ async function runGasReport(): Promise { // Run the generated file using forge const child = execa("forge", ["test", "-vvv"], { stdio: ["inherit", "pipe", "inherit"], + env: { GAS_REPORTER_ENABLED: "true" }, }); stdout = (await child).stdout; } catch (error: any) { diff --git a/packages/std-contracts/src/test/GasReporter.sol b/packages/std-contracts/src/test/GasReporter.sol index ee668a7b68..60b1784697 100644 --- a/packages/std-contracts/src/test/GasReporter.sol +++ b/packages/std-contracts/src/test/GasReporter.sol @@ -10,6 +10,7 @@ contract GasReporter is Test { string[] private __gasReportNames; function startGasReport(string memory name) internal { + if (!vm.envOr("GAS_REPORTER_ENABLED", false)) return; require( bytes(__currentGasReportName).length == 0, string.concat( @@ -18,7 +19,7 @@ contract GasReporter is Test { '" is already running and only one report can be run at a time' ) ); - require(__gasReports[name] == 0, string.concat('gas report name "', name, '" already used')); + require(__gasReports[name] == 0, string.concat('gas report "', name, '" already used for this test')); __currentGasReportName = name; vm.pauseGasMetering(); __currentGasReportValue = gasleft(); @@ -27,9 +28,11 @@ contract GasReporter is Test { function endGasReport() internal { uint256 gas = gasleft(); - // 160 is the gas used by the GasReporter itself - uint256 gasUsed = __currentGasReportValue - gas - 160; - require(gasUsed >= 0, "negative gas used, did you forget to call startGasReport?"); + if (!vm.envOr("GAS_REPORTER_ENABLED", false)) return; + // subtract 160 gas used by the GasReporter itself + // add 1 gas so we can later check that this is set + uint256 gasUsed = __currentGasReportValue - gas - 160 + 1; + require(gasUsed > 0, "gas report didn't use gas"); __gasReports[__currentGasReportName] = gasUsed; __gasReportNames.push(__currentGasReportName); printGasReport(__currentGasReportName); @@ -43,6 +46,7 @@ contract GasReporter is Test { } function getGasUsed(string memory name) internal view returns (uint256) { + require(__gasReports[name] > 0, string.concat('gas report "', name, '" not found')); return __gasReports[name]; } diff --git a/packages/std-contracts/test/GasReporter.t.sol b/packages/std-contracts/test/GasReporter.t.sol index 1d0eb14c16..993dd63536 100644 --- a/packages/std-contracts/test/GasReporter.t.sol +++ b/packages/std-contracts/test/GasReporter.t.sol @@ -6,9 +6,10 @@ import { GasReporter } from "../src/test/GasReporter.sol"; contract GasReporterTest is Test, GasReporter { function testGasReporterGas() public { + vm.setEnv("GAS_REPORTER_ENABLED", "true"); startGasReport("empty gas report"); endGasReport(); - // An empty gas report should always result in zero gas. If not, update GasReporter's gasUsed calculation to make this zero. - assertEq(getGasUsed("empty gas report"), 0); + // An "empty" gas report should be 1 gas. If not, update GasReporter's gasUsed calculation. + assertEq(getGasUsed("empty gas report"), 1); } } diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 2c4286c194..7df41d1e35 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -3,235 +3,235 @@ "file": "test/Bytes.t.sol", "test": "testEquals", "name": "compare equal bytes", - "gasUsed": 195 + "gasUsed": 196 }, { "file": "test/Bytes.t.sol", "test": "testEqualsFalse", "name": "compare unequal bytes", - "gasUsed": 195 + "gasUsed": 196 }, { "file": "test/Bytes.t.sol", "test": "testSetBytes1", "name": "set bytes1 in bytes32", - "gasUsed": 0 + "gasUsed": 1 }, { "file": "test/Bytes.t.sol", "test": "testSetBytes2", "name": "set bytes2 in bytes32", - "gasUsed": 0 + "gasUsed": 1 }, { "file": "test/Bytes.t.sol", "test": "testSetBytes4", "name": "set bytes4 in bytes32", - "gasUsed": 0 + "gasUsed": 1 }, { "file": "test/Bytes.t.sol", "test": "testSetBytes4Memory", "name": "set bytes4 in bytes memory", - "gasUsed": 36 + "gasUsed": 37 }, { "file": "test/Bytes.t.sol", "test": "testSlice3", "name": "slice bytes3 with offset 1", - "gasUsed": 67 + "gasUsed": 68 }, { "file": "test/Bytes.t.sol", "test": "testSlice32", "name": "slice bytes32 with offset 10", - "gasUsed": 67 + "gasUsed": 68 }, { "file": "test/Bytes.t.sol", "test": "testToBytes32", "name": "create bytes32 from bytes memory with offset 0", - "gasUsed": 24 + "gasUsed": 25 }, { "file": "test/Bytes.t.sol", "test": "testToBytes32CrossWord", "name": "create bytes32 from bytes memory with offset 16", - "gasUsed": 35 + "gasUsed": 36 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "abi encode", - "gasUsed": 956 + "gasUsed": 957 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "abi decode", - "gasUsed": 1745 + "gasUsed": 1746 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "custom encode", - "gasUsed": 1811 + "gasUsed": 1812 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "custom decode", - "gasUsed": 2123 + "gasUsed": 2124 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "pass abi encoded bytes to external contract", - "gasUsed": 6549 + "gasUsed": 6551 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "pass custom encoded bytes to external contract", - "gasUsed": 1375 + "gasUsed": 1376 }, { "file": "test/KeyEncoding.t.sol", "test": "testRegisterAndGetSchema", "name": "register KeyEncoding schema", - "gasUsed": 64685 + "gasUsed": 64686 }, { "file": "test/Mixed.t.sol", "test": "testCompareSolidity", "name": "store Mixed struct in storage (native solidity)", - "gasUsed": 92037 + "gasUsed": 92038 }, { "file": "test/Mixed.t.sol", "test": "testRegisterAndGetSchema", "name": "register Mixed schema", - "gasUsed": 61216 + "gasUsed": 61218 }, { "file": "test/Mixed.t.sol", "test": "testSetAndGet", "name": "set record in Mixed", - "gasUsed": 112167 + "gasUsed": 112166 }, { "file": "test/Mixed.t.sol", "test": "testSetAndGet", "name": "get record from Mixed", - "gasUsed": 13453 + "gasUsed": 13455 }, { "file": "test/PackedCounter.t.sol", "test": "testAtIndex", "name": "get value at index of PackedCounter", - "gasUsed": 254 + "gasUsed": 255 }, { "file": "test/PackedCounter.t.sol", "test": "testSetAtIndex", "name": "set value at index of PackedCounter", - "gasUsed": 792 + "gasUsed": 793 }, { "file": "test/PackedCounter.t.sol", "test": "testTotal", "name": "pack uint40 array into PackedCounter", - "gasUsed": 2145 + "gasUsed": 2146 }, { "file": "test/PackedCounter.t.sol", "test": "testTotal", "name": "get total of PackedCounter", - "gasUsed": 26 + "gasUsed": 27 }, { "file": "test/Schema.t.sol", "test": "testEncodeDecodeSchema", "name": "get schema type at index", - "gasUsed": 184 + "gasUsed": 185 }, { "file": "test/Schema.t.sol", "test": "testGetNumDynamicFields", "name": "get number of dynamic fields from schema", - "gasUsed": 73 + "gasUsed": 74 }, { "file": "test/Schema.t.sol", "test": "testGetNumStaticFields", "name": "get number of static fields from schema", - "gasUsed": 84 + "gasUsed": 85 }, { "file": "test/Schema.t.sol", "test": "testGetStaticSchemaLength", "name": "get static data length from schema", - "gasUsed": 32 + "gasUsed": 33 }, { "file": "test/Schema.t.sol", "test": "testIsEmptyFalse", "name": "check if schema is empty (non-empty schema)", - "gasUsed": 6 + "gasUsed": 7 }, { "file": "test/Schema.t.sol", "test": "testIsEmptyTrue", "name": "check if schema is empty (empty schema)", - "gasUsed": 6 + "gasUsed": 7 }, { "file": "test/Schema.t.sol", "test": "testValidate", "name": "validate schema", - "gasUsed": 18265 + "gasUsed": 18266 }, { "file": "test/Slice.t.sol", "test": "testFromBytes", "name": "make Slice from bytes", - "gasUsed": 30 + "gasUsed": 31 }, { "file": "test/Slice.t.sol", "test": "testFromBytes", "name": "get Slice length", - "gasUsed": 0 + "gasUsed": 1 }, { "file": "test/Slice.t.sol", "test": "testFromBytes", "name": "get Slice pointer", - "gasUsed": 26 + "gasUsed": 27 }, { "file": "test/Slice.t.sol", "test": "testSubslice", "name": "subslice bytes (no copy) [1:4]", - "gasUsed": 310 + "gasUsed": 311 }, { "file": "test/Slice.t.sol", "test": "testSubslice", "name": "subslice bytes (no copy) [4:37]", - "gasUsed": 310 + "gasUsed": 311 }, { "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (0 bytes) to bytes memory", - "gasUsed": 472 + "gasUsed": 473 }, { "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (2 bytes) to bytes memory", - "gasUsed": 507 + "gasUsed": 508 }, { "file": "test/Slice.t.sol", @@ -243,151 +243,151 @@ "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (34 bytes) to bytes memory", - "gasUsed": 584 + "gasUsed": 585 }, { "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (1024 bytes) to bytes memory", - "gasUsed": 4847 + "gasUsed": 4849 }, { "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (1024x1024 bytes) to bytes memory", - "gasUsed": 6614841 + "gasUsed": 6616506 }, { "file": "test/Slice.t.sol", "test": "testToBytes32", "name": "Slice to bytes32", - "gasUsed": 80 + "gasUsed": 81 }, { "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "store 1 storage slot", - "gasUsed": 22502 + "gasUsed": 22503 }, { "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "store 34 bytes over 3 storage slots (with offset and safeTrail))", - "gasUsed": 23157 + "gasUsed": 23158 }, { "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "load 34 bytes over 3 storage slots (with offset and safeTrail))", - "gasUsed": 1098 + "gasUsed": 1099 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (cold, 1 slot)", - "gasUsed": 8151 + "gasUsed": 8152 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (warm, 1 slot)", - "gasUsed": 2179 + "gasUsed": 2180 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (semi-cold, 1 slot)", - "gasUsed": 4183 + "gasUsed": 4185 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (warm, 2 slots)", - "gasUsed": 4470 + "gasUsed": 4471 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetSecondFieldLength", "name": "get field length (cold, 1 slot)", - "gasUsed": 7958 + "gasUsed": 7959 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetSecondFieldLength", "name": "get field length (warm, 1 slot)", - "gasUsed": 1955 + "gasUsed": 1956 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetThirdFieldLength", "name": "get field length (warm due to , 2 slots)", - "gasUsed": 7958 + "gasUsed": 7959 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetThirdFieldLength", "name": "get field length (warm, 2 slots)", - "gasUsed": 1954 + "gasUsed": 1955 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (cold, 1 slot, 1 uint32 item)", - "gasUsed": 29290 + "gasUsed": 29291 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (warm, 1 slot, 1 uint32 item)", - "gasUsed": 19343 + "gasUsed": 19346 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (cold, 2 slots, 10 uint32 items)", - "gasUsed": 31173 + "gasUsed": 31174 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (warm, 2 slots, 10 uint32 items)", - "gasUsed": 19226 + "gasUsed": 19229 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access non-existing record", - "gasUsed": 7320 + "gasUsed": 7321 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access static field of non-existing record", - "gasUsed": 2972 + "gasUsed": 2974 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access dynamic field of non-existing record", - "gasUsed": 3585 + "gasUsed": 3587 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access length of dynamic field of non-existing record", - "gasUsed": 1326 + "gasUsed": 1328 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access slice of dynamic field of non-existing record", - "gasUsed": 1322 + "gasUsed": 1324 }, { "file": "test/StoreCoreGas.t.sol", "test": "testDeleteData", "name": "delete record (complex data, 3 slots)", - "gasUsed": 10993 + "gasUsed": 10994 }, { "file": "test/StoreCoreGas.t.sol", @@ -399,67 +399,67 @@ "file": "test/StoreCoreGas.t.sol", "test": "testHasSchema", "name": "check for existence of table (non-existent)", - "gasUsed": 3128 + "gasUsed": 3129 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "register subscriber", - "gasUsed": 65559 + "gasUsed": 65560 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set record on table with subscriber", - "gasUsed": 73960 + "gasUsed": 73961 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set static field on table with subscriber", - "gasUsed": 32025 + "gasUsed": 32051 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "delete record on table with subscriber", - "gasUsed": 24583 + "gasUsed": 24585 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "register subscriber", - "gasUsed": 65559 + "gasUsed": 65560 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) record on table with subscriber", - "gasUsed": 167397 + "gasUsed": 167398 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) field on table with subscriber", - "gasUsed": 35058 + "gasUsed": 35081 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "delete (dynamic) record on table with subscriber", - "gasUsed": 26054 + "gasUsed": 26057 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToField", "name": "push to field (1 slot, 1 uint32 item)", - "gasUsed": 17063 + "gasUsed": 17065 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToField", "name": "push to field (2 slots, 10 uint32 items)", - "gasUsed": 39779 + "gasUsed": 39781 }, { "file": "test/StoreCoreGas.t.sol", @@ -471,61 +471,61 @@ "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetSchema", "name": "StoreCore: get schema (warm)", - "gasUsed": 1127 + "gasUsed": 1128 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetSchema", "name": "StoreCore: get key schema (warm)", - "gasUsed": 2322 + "gasUsed": 2324 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "set complex record with dynamic data (4 slots)", - "gasUsed": 107706 + "gasUsed": 107707 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "get complex record with dynamic data (4 slots)", - "gasUsed": 6440 + "gasUsed": 6442 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "compare: Set complex record with dynamic data using native solidity", - "gasUsed": 116841 + "gasUsed": 116842 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "compare: Set complex record with dynamic data using abi.encode", - "gasUsed": 267368 + "gasUsed": 267369 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicDataLength", "name": "set dynamic length of dynamic index 0", - "gasUsed": 23609 + "gasUsed": 23610 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicDataLength", "name": "set dynamic length of dynamic index 1", - "gasUsed": 1710 + "gasUsed": 1711 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicDataLength", "name": "reduce dynamic length of dynamic index 0", - "gasUsed": 1698 + "gasUsed": 1699 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (1 slot)", - "gasUsed": 37984 + "gasUsed": 37985 }, { "file": "test/StoreCoreGas.t.sol", @@ -537,210 +537,210 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (overlap 2 slot)", - "gasUsed": 35005 + "gasUsed": 35007 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get static field (overlap 2 slot)", - "gasUsed": 3874 + "gasUsed": 3875 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, first dynamic field)", - "gasUsed": 57383 + "gasUsed": 57386 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get dynamic field (1 slot, first dynamic field)", - "gasUsed": 3808 + "gasUsed": 3811 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, second dynamic field)", - "gasUsed": 35513 + "gasUsed": 35518 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get dynamic field (1 slot, second dynamic field)", - "gasUsed": 3823 + "gasUsed": 3826 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "set static record (1 slot)", - "gasUsed": 37373 + "gasUsed": 37374 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "get static record (1 slot)", - "gasUsed": 1319 + "gasUsed": 1320 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "set static record (2 slots)", - "gasUsed": 59940 + "gasUsed": 59942 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "get static record (2 slots)", - "gasUsed": 1568 + "gasUsed": 1569 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetMetadata", "name": "StoreCore: set table metadata", - "gasUsed": 251865 + "gasUsed": 251866 }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInField", "name": "update in field (1 slot, 1 uint32 item)", - "gasUsed": 16600 + "gasUsed": 16605 }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInField", "name": "push to field (2 slots, 6 uint64 items)", - "gasUsed": 17698 + "gasUsed": 17696 }, { "file": "test/StoreMetadata.t.sol", "test": "testSetAndGet", "name": "set record in StoreMetadataTable", - "gasUsed": 250187 + "gasUsed": 250181 }, { "file": "test/StoreMetadata.t.sol", "test": "testSetAndGet", "name": "get record from StoreMetadataTable", - "gasUsed": 12114 + "gasUsed": 12116 }, { "file": "test/StoreSwitch.t.sol", "test": "testIsDelegatecall", "name": "check if delegatecall", - "gasUsed": 682 + "gasUsed": 683 }, { "file": "test/StoreSwitch.t.sol", "test": "testIsNoDelegatecall", "name": "check if delegatecall", - "gasUsed": 702 + "gasUsed": 703 }, { "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "set field in Callbacks", - "gasUsed": 63202 + "gasUsed": 63203 }, { "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "get field from Callbacks (warm)", - "gasUsed": 5783 + "gasUsed": 5785 }, { "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "push field to Callbacks", - "gasUsed": 40883 + "gasUsed": 40890 }, { "file": "test/tables/Hooks.t.sol", "test": "testSetAndGet", "name": "set field in Hooks", - "gasUsed": 63358 + "gasUsed": 63360 }, { "file": "test/tables/Hooks.t.sol", "test": "testSetAndGet", "name": "get field from Hooks (warm)", - "gasUsed": 5934 + "gasUsed": 5935 }, { "file": "test/tables/Hooks.t.sol", "test": "testSetAndGet", "name": "push field to Hooks", - "gasUsed": 40877 + "gasUsed": 40881 }, { "file": "test/tightcoder/DecodeSlice.t.sol", "test": "testToArrayUint32", "name": "decode packed uint32[]", - "gasUsed": 784 + "gasUsed": 785 }, { "file": "test/tightcoder/DecodeSlice.t.sol", "test": "testToBytes32Array", "name": "decode packed bytes32[]", - "gasUsed": 610 + "gasUsed": 611 }, { "file": "test/tightcoder/EncodeArray.t.sol", "test": "testEncodeBytesArray", "name": "encode packed bytes[]", - "gasUsed": 1356 + "gasUsed": 1357 }, { "file": "test/tightcoder/EncodeArray.t.sol", "test": "testEncodeUint16Array", "name": "encode packed uint16[]", - "gasUsed": 1145 + "gasUsed": 1143 }, { "file": "test/tightcoder/EncodeArray.t.sol", "test": "testEncodeUint32Array", "name": "encode packed uint32[]", - "gasUsed": 1051 + "gasUsed": 1049 }, { "file": "test/tightcoder/EncodeArray.t.sol", "test": "testEncodeUint8Array", "name": "encode packed uint8[]", - "gasUsed": 1037 + "gasUsed": 1038 }, { "file": "test/tightcoder/TightCoder.t.sol", "test": "testFromAndToUint32Array", "name": "decode packed uint32[]", - "gasUsed": 787 + "gasUsed": 788 }, { "file": "test/tightcoder/TightCoder.t.sol", "test": "testToAndFromBytes24Array", "name": "encode packed bytes24[]", - "gasUsed": 888 + "gasUsed": 886 }, { "file": "test/tightcoder/TightCoder.t.sol", "test": "testToAndFromBytes24Array", "name": "decode packed uint32[]", - "gasUsed": 621 + "gasUsed": 622 }, { "file": "test/Vector2.t.sol", "test": "testRegisterAndGetSchema", "name": "register Vector2 schema", - "gasUsed": 57994 + "gasUsed": 57995 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "set Vector2 record", - "gasUsed": 38662 + "gasUsed": 38660 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "get Vector2 record", - "gasUsed": 5089 + "gasUsed": 5090 } ] From 20b2285afc66ab2ff5cc68dd06d3b9c85d346e52 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 19 Jun 2023 15:07:53 +0100 Subject: [PATCH 11/11] update gas report --- packages/world/gas-report.json | 104 ++++++++++++++++----------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 110a8602f0..d3037819bb 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -3,55 +3,55 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1286949 + "gasUsed": 1286951 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1286949 + "gasUsed": 1286951 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 193904 + "gasUsed": 193905 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1286949 + "gasUsed": 1286951 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1286949 + "gasUsed": 1286951 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 30071 + "gasUsed": 30072 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 292112 + "gasUsed": 292113 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1286949 + "gasUsed": 1286951 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 28685 + "gasUsed": 28686 }, { "file": "test/KeysInTableModule.t.sol", @@ -63,258 +63,258 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValue", "name": "install keys with value module", - "gasUsed": 626517 + "gasUsed": 626518 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValue", "name": "Get list of keys with a given value", - "gasUsed": 7715 + "gasUsed": 7716 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetTargetTableSelector", "name": "compute the target table selector", - "gasUsed": 2233 + "gasUsed": 2234 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 626517 + "gasUsed": 626518 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 165706 + "gasUsed": 165703 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 626517 + "gasUsed": 626518 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 135358 + "gasUsed": 135356 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 54296 + "gasUsed": 54297 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 626517 + "gasUsed": 626518 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 173711 + "gasUsed": 173712 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 138069 + "gasUsed": 138070 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "gasUsed": 169287 + "gasUsed": 169289 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "gasUsed": 79069 + "gasUsed": 79072 }, { "file": "test/query.t.sol", "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "gasUsed": 233190 + "gasUsed": 233194 }, { "file": "test/query.t.sol", "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "gasUsed": 146483 + "gasUsed": 146486 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 145185 + "gasUsed": 145184 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueQuery", "name": "CombinedHasValueQuery", - "gasUsed": 19620 + "gasUsed": 19621 }, { "file": "test/query.t.sol", "test": "testHasQuery", "name": "HasQuery", - "gasUsed": 33096 + "gasUsed": 33097 }, { "file": "test/query.t.sol", "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "gasUsed": 11312083 + "gasUsed": 11312135 }, { "file": "test/query.t.sol", "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "gasUsed": 1060034 + "gasUsed": 1060038 }, { "file": "test/query.t.sol", "test": "testHasValueQuery", "name": "HasValueQuery", - "gasUsed": 9521 + "gasUsed": 9522 }, { "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 72250 + "gasUsed": 72252 }, { "file": "test/SnapSyncModule.t.sol", "test": "testSnapSyncGas", "name": "Call snap sync on a table with 1 record", - "gasUsed": 43864 + "gasUsed": 43866 }, { "file": "test/SnapSyncModule.t.sol", "test": "testSnapSyncGas", "name": "Call snap sync on a table with 2 records", - "gasUsed": 62552 + "gasUsed": 62553 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 824002 + "gasUsed": 824003 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 73376 + "gasUsed": 73377 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 794077 + "gasUsed": 794078 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 73376 + "gasUsed": 73377 }, { "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 16148 + "gasUsed": 16152 }, { "file": "test/World.t.sol", "test": "testPushToField", "name": "Push data to the table", - "gasUsed": 96510 + "gasUsed": 96511 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "gasUsed": 87011 + "gasUsed": 87012 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "gasUsed": 78195 + "gasUsed": 78196 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 107606 + "gasUsed": 107607 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 161671 + "gasUsed": 161669 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 94098 + "gasUsed": 94099 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 262386 + "gasUsed": 262388 }, { "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 44812 + "gasUsed": 44813 }, { "file": "test/World.t.sol", "test": "testSetMetadata", "name": "Set metadata", - "gasUsed": 283379 + "gasUsed": 283384 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 42692 + "gasUsed": 42690 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (cold)", - "gasUsed": 38032 + "gasUsed": 38033 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (warm)", - "gasUsed": 22829 + "gasUsed": 22830 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testUpdateInField", "name": "updateInField 1 item (cold)", - "gasUsed": 40361 + "gasUsed": 40364 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testUpdateInField", "name": "updateInField 1 item (warm)", - "gasUsed": 25565 + "gasUsed": 25566 } ]