Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/diamond/DiamondInspectFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30;

contract DiamondInspectFacet {
bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("compose.diamond");

/**
* @notice Data stored for each function selector.
* @dev Facet address of function selector.
* Position of selector in the 'bytes4[] selectors' array.
*/
struct FacetAndPosition {
address facet;
uint32 position;
}

/**
* @custom:storage-location erc8042:compose.diamond
*/
struct DiamondStorage {
mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition;
/**
* Array of all function selectors that can be called in the diamond.
*/
bytes4[] selectors;
}

/**
* @notice Retrieves the diamond's storage struct from its fixed position.
* @dev Uses inline assembly to access the storage slot directly.
* @return s The `DiamondStorage` struct stored at `DIAMOND_STORAGE_POSITION`.
*/
function getStorage() internal pure returns (DiamondStorage storage s) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
s.slot := position
}
}

struct FunctionFacetPair {
bytes4 selector;
address facet;
}

/**
* @notice Returns an array of all function selectors and their corresponding facet addresses.
* @dev Iterates through the diamond's stored selectors and pairs each with its facet.
* @return pairs An array of `FunctionFacetPair` structs, each containing a selector and its facet address.
*/
function functionFacetPairs() external view returns (FunctionFacetPair[] memory pairs) {
DiamondStorage storage s = getStorage();
uint256 length = s.selectors.length;
pairs = new FunctionFacetPair[](length);
for (uint256 i; i < length; i++) {
bytes4 selector = s.selectors[i];
pairs[i] = FunctionFacetPair(selector, s.facetAndPosition[selector].facet);
}
}
}
150 changes: 150 additions & 0 deletions test/benchmark/DiamondInspectGas.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30;

import {Test} from "forge-std/Test.sol";
import "forge-std/console.sol";
import "../../src/diamond/DiamondInspectFacet.sol";

contract DiamondInspectGasTest is Test {
DiamondInspectFacet diamondInspect;

bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("compose.diamond");

struct FacetAndPosition {
address facet;
uint32 position;
}

struct DiamondStorage {
mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition;
bytes4[] selectors;
}

function setUp() public {
diamondInspect = new DiamondInspectFacet();
}

// Helper to populate storage directly
function _populateSelectors(uint256 count) internal {
bytes32 position = DIAMOND_STORAGE_POSITION;
DiamondStorage storage s;
assembly {
s.slot := position
}

// Dummy address
address facet = address(0x1234567890123456789012345678901234567890);

for (uint256 i = 0; i < count; i++) {
// Generate pseudo-random selector
bytes4 selector = bytes4(keccak256(abi.encode(i)));

s.selectors.push(selector);
s.facetAndPosition[selector] = FacetAndPosition({facet: facet, position: uint32(i)});
}
}

// Generic benchmark function
function _runBenchmark(uint256 count) internal {
_populateSelectors(count);

uint256 startGas = gasleft();
(bool success, bytes memory data) = address(diamondInspect)
.delegatecall(abi.encodeWithSelector(DiamondInspectFacet.functionFacetPairs.selector));
uint256 gasUsed = startGas - gasleft();

require(success, "Delegatecall failed");
DiamondInspectFacet.FunctionFacetPair[] memory pairs =
abi.decode(data, (DiamondInspectFacet.FunctionFacetPair[]));

console.log("Count:", count);
console.log("Gas Used:", gasUsed);

assertEq(pairs.length, count);
}

function testBenchmark_000100() public {
_runBenchmark(100);
}

function testBenchmark_001000() public {
_runBenchmark(1000);
}

function testBenchmark_005000() public {
_runBenchmark(5000);
}

function testBenchmark_005600() public {
_runBenchmark(5600);
}

//10 mil gas
function testBenchmark_005665() public {
_runBenchmark(5665);
}

function testBenchmark_006000() public {
_runBenchmark(6000);
}

function testBenchmark_010000() public {
_runBenchmark(10000);
}

function testBenchmark_015200() public {
_runBenchmark(15200);
}

//50 mil gas
function testBenchmark_015902() public {
_runBenchmark(15902);
}

function testBenchmark_020000() public {
_runBenchmark(20000);
}

//100 mil gas
function testBenchmark_022000() public {
_runBenchmark(23778);
}

function testBenchmark_024300() public {
_runBenchmark(24300);
}

function testBenchmark_035000() public {
_runBenchmark(35000);
}

function testBenchmark_040000() public {
_runBenchmark(40000);
}

//300 mil gas
function testBenchmark_044000() public {
_runBenchmark(43612);
}

function testBenchmark_045000() public {
_runBenchmark(45000);
}

function testBenchmark_050000() public {
_runBenchmark(50000);
}

function testBenchmark_052000() public {
_runBenchmark(52000);
}

// 500 mil gas
function testBenchmark_057315() public {
_runBenchmark(57315);
}

function testBenchmark_060000() public {
_runBenchmark(60000);
}
}
Loading