A structured, OOP-style framework for testing Diamond (EIP-2535) contracts.
Designed for modularity, clarity, full test coverage across facets.
DTO is a small framework/pattern for testing Diamond contracts.
It uses isolated runners for complex integration logic by combining:
- Scoped
{}blocks to keep each run separate try/catchto trap expected failurestPrototype/tFacetcontracts as modular test units
The goal is to provide an elegant way to run large, cross-module test logic inside a single test case — without crashing the whole suite.
When I first researched EIP-2535 (Diamond Standard), the most frustrating thing was how people (and even AI tools like GPT or Gemini) kept calling it too complicated.
OpenZeppelin openly refuses to support it. The meme is always the same: “just use another proxy.”
But I see Diamond as a game-changer for blockchain — essentially a microservice system on-chain.
Naturally, a complex system needs integration tests across multiple modules, which normal frameworks don’t handle gracefully.
With current methods, there’s no clean way to run massive, cross-facet integration tests.
This project shows how an OOP-style test harness can make Diamond testing simpler and more modular.
Yes, this pattern adds some gas overhead because each {} block and try/catch introduces extra execution cost.
But since this is test logic only, not production flow, the gas cost doesn’t matter.
👉 The focus here is clarity and isolation, not efficiency.
A Diamond is infinitely modular — each Facet lives as its own isolated logic block.
So a monolithic test style (like Foundry’s default Test.sol approach) doesn’t scale well here.
The idea is simple:
👉 if the system is modular, the test should mirror that modularity.
- Each Facet defines a specific behavior in the Diamond.
- Each Interface describes that behavior’s surface.
- Each tFacet sets up selectors and builds the cut based on its Interface.
- tPrototype represents many tFacet just like the Diamond represents many Facets
- Abstract base contract for test units.
- Holds
selectors[]and definessetUp(),buildCut(),baseCut().
- Inherits from
tPrototype. - Declares the real Facet, builds selectors, and implements setup logic.
- Gets cut into the Diamond, then disposed to free stack space.
- Each
{}represents an isolated sub-test. - Keeps state and revert scope clean.
- Prevents one failed sub-test from stopping the entire run.
- Each scenario can run independently and log its own results.
- All
tFacetcontracts were generated from Interfaces using Gemini-CLI.
- /test/TestAddFacet.t is a simple unit test for diamond
- /test/TestMassiveIntegration.t.sol shows how to use scoped
{}blocks andtry/catchto handle large initialization and test flows. - /test/TestSetupDiamond.t.sol adds an abstraction layer for modular DiamondCuts using the
tPrototype/tFacetpattern. - Since this is a
test framework, you should checktestdirectory for example
- Foundry
- Solidity
- foundry.toml with
rpc_endpoints
- Clone repo
- Run
forge install(if needed) - Add
rpc_endpoints - forge build
- forge test TestSetupDiamond.t -vvv
abstract contract tPrototype is Test {
bytes4[] public selectors;
// @notice Build facet cut struct based on selectors
// @param facet The facet contract address
// @return cut as An array containing one FacetCut with all selectors
function setUp() public virtual;
function buildCut() external view virtual returns (IDiamondCut.FacetCut[] memory);
// basically same in all sub contract
function baseCut(address facet) internal view returns (IDiamondCut.FacetCut[] memory)
{
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
cut[0] = IDiamondCut.FacetCut({
facetAddress: facet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: selectors
});
return cut;
}
}
contract tAdd1Facet is tPrototype {
// for main call
Add1Facet public facet;
// set up here
function setUp() public override {
// Generate based on Facet
facet = new Add1Facet();
// Generate based on Interface
selectors = new bytes4[](3);
selectors[0] = IAdd1Facet.whoami1.selector;
selectors[1] = IAdd1Facet.add1.selector;
selectors[2] = IAdd1Facet.add1error.selector;
}
// basically same in all sub contract
function buildCut() external override view returns (IDiamondCut.FacetCut[] memory) {
return baseCut(address(facet));
}
}// Because all tFacet (tAdd1Facet, tAdd2Facet...) inherit tPrototype we can setup it like this
function cutHelper(tPrototype temp, bytes memory data) internal
{
IDiamondCut.FacetCut[] memory cut;
temp.setUp();
cut= temp.buildCut();
IDiamondCut(address(diamond)).diamondCut(cut, address(diamond), data);
}
function setupDiamond() internal
{
vm.startPrank(vm.addr(key_owner));
// Deploy DiamondCutFacet and the main Diamond contract
{
DiamondCutFacet cutFacet = new DiamondCutFacet();
diamond = new Diamond(address(vm.addr(key_owner)), address(cutFacet));
}
// start setup here
{
// Base utility facets
cutHelper(new tDiamondLoupe(), "");
// Functional modules
cutHelper(new tAdd1Facet(), "");
// Initialization facet (with parameters)
cutHelper(new tAddFacet(), abi.encodeWithSelector(IAddFacet.init.selector, 500));
}
vm.stopPrank();
}
// test_massive_integration
// use scope {} and try/catch pattern
{
try IAdd17Facet(address(diamond)).add17(summer) returns (uint256 newSummer) {
summer = newSummer;
console.log(summer);
} catch {
console.log("Error calling add17");
}
}
// handle Failpath
console.log(" use Try / Catch pattern - Failpath ");
console.log(" ================================== ");
{
try IAdd1Facet(address(diamond)).add1error() {
console.log("add1error did not revert");
} catch {
console.log("add1error reverted as expected");
}
}