Lattice is a Solidity library of modular contract modules built on top of the
diamond-lib EIP-2535 Diamond Standard
framework. Most modules ship as a stateless facet + a library with ERC-7201 namespaced
storage, so they can be cut into a single Diamond proxy without storage collisions.
The core is an OpenZeppelin Contracts v5-style module set ported into the Diamond facet pattern. The current tree also includes smart-account, cross-chain, DeFi adapter, oracle, ENS, marketplace, privacy/ZK, and upgrade-governance modules. It is a Foundry project consumed as a Forge dependency; there is no application or canonical deployment of its own.
Unaudited. Use at your own risk. Lattice re-implements OpenZeppelin and adapts external protocol interfaces, account standards, bridge/oracle integrations, and ZK/privacy primitives. It has not been audited and carries no warranty. It has not received the review that the upstream libraries it mirrors or composes with have. Do not deploy it to mainnet with funds at risk without your own independent audit, especially modules that custody assets, verify proofs, bridge messages, or authorize upgrades. Licensed under MIT.
| Area | Modules |
|---|---|
access/ |
AccessControl, AccessControlEnumerable, AccessControlTimed, AccessManager (+ AccessManaged, AccessManagerStandalone), Ownable |
accounts/ |
Diamond smart-account building blocks — two modular-account flavors (see below), each in its own subfolder. accounts/erc7579/: AccountDiamond, Account7702Diamond, AccountFactory, AccountInit, AccountSigner, ERC7821Executor, ERC7579ModuleConfig. accounts/erc6900/: ModularAccount6900, AccountFactory6900, AccountInit6900, ERC6900ModuleManager, ERC6900Executor, ERC6900Validation, ERC6900Signature, ERC6900AccountView (+ reference modules modules/SingleSignerValidation, modules/SpendingLimit). Shared base + standalone account types (accounts/): ERC4337Validation, ERC1271Signature (the ERC-4337/1271 base both flavors build on), plus the single-facet standalone types ERC6551Account (token-bound) and SessionKey |
amm/ |
ConstantProduct |
crosschain/ |
Message gateways: CCIPGatewayAdapter, AxelarGatewayAdapter, WormholeGatewayAdapter, LayerZeroGatewayAdapter, HyperlaneGatewayAdapter, ZetaChainGatewayAdapter (hub-routed), HyperbridgeGatewayAdapter (proof-verified), L2ToL2/L1ToL2CrossDomainMessengerGatewayAdapter (OP). Token rails: CCTPBridgeAdapter (burn/mint), AcrossBridgeAdapter (intent), StargateBridgeAdapter (pooled), BridgeERC20, BridgeERC7802, SuperchainETHBridgeAdapter. Non-EVM: StarknetGatewayAdapter (felt252, L1↔L2). Composition: ERC7786OpenBridge (M-of-N), CrosschainLink, ChainRegistry (one-action fan-out), CrosschainTimelockHandler. See CROSSCHAIN.md for the adapter-shape reference + off-chain dependency matrix |
defi/ |
AaveV3Adapter, CompoundV3Adapter, CurveStableSwapAdapter, ERC4626Adapter, LidoAdapter, StrategyManager, UniswapV3Adapter, VaultCore |
ens/ |
ENSResolver, ENSReverseClaimer, ENSSubnameIssuer |
governance/ |
Governor (+ GovernorStandalone), TimelockController (+ TimelockControllerStandalone), Votes, GovernedDiamondCut, SafeDiamondCut, GovernedSafeDiamondCut, SafeHarborAdopter |
oracles/ |
API3Adapter, API3QRNGAdapter, BandAdapter, ChainlinkAdapter, ChainlinkAutomationAdapter, ChainlinkCREAdapter, ChainlinkVRF, ChronicleAdapter, DIAAdapter, GelatoAutomateAdapter, GelatoVRFAdapter, PythAdapter, PythEntropyAdapter, RedStoneAdapter, TWAPOracle, TellorAdapter |
privacy/ |
CommitReveal, ERC5564Announcer, ERC6538Registry, Groth16Verifier, PlonkVerifier, PrivateVoting, Semaphore, ShieldedPool |
security/ |
Pausable, ReentrancyGuard, RateLimiter, CircuitBreaker, EmergencyStop, InvariantChecker |
tokens/ |
One subfolder per standard (base + extensions flat inside, <std>/libraries/ for logic). tokens/ERC20/: ERC20 (+ Burnable, Capped, Crosschain, Permit, Votes). tokens/ERC721/: ERC721 (+ URIStorage). tokens/ERC1155/, tokens/ERC2981/, tokens/ERC4626/, tokens/ERC7802/. MarketplaceZone sits at the tokens/ root (a Seaport zone enforcing the issuer's own token policy, not a token standard) |
utils/ |
EIP712, Multicall, Nonces, VestingWallet (+ VestingWalletStandalone) |
Utility libraries (src/utils/libraries/) — pure logic with no own storage, facet,
or interface: Base64, Bytes, Calldata, Checkpoints, ECDSA, EnumerableSet,
InterestRate, InteroperableAddress, P256, Panic, ShortStrings,
SignatureChecker, Strings, TimelockLib, UniswapV3FullRangeMath, WebAuthn, plus
module helpers such as EIP712Lib, MulticallLib, NoncesLib, and VestingWalletLib.
src/interfaces/external/ vendors minimal third-party ABIs used by adapters and standards
integrations. The canonical storage/interface registry is
STORAGE_REGISTRY.md, and
test/unit/StorageSlotVerificationTest.t.sol re-derives every registered slot and checks
global uniqueness.
Lattice ships two modular smart-account flavors, both built on the shared Diamond core
(DiamondCut / Loupe / ERC-165 / AccessControl). They are separate blueprints — you pick one
per account; the two facet sets are never cut into the same Diamond. Both target ERC-4337 and
ERC-1271, and both are written fresh in the three-layer facet pattern (the ERC-6900 reference
implementation is GPL and is not a dependency — only minimal interfaces are vendored into
src/interfaces/external/).
- ERC-7579 (
AccountDiamondproxy) — the four fixed module types (validator 1, executor 2, fallback 3, hook 4). A per-op validator is selected by the top 20 bytes ofuserOp.nonce; one global hook wraps execution; a fallback registry is layered under the facet map. - ERC-6900 (
ModularAccount6900proxy) — inspired by EIP-2535 itself, so it maps onto the Diamond most naturally. Validation is a richerModuleEntity(address ‖ uint32 entityId) with per-validation pre-validation hooks and per-selector pre/post execution hooks — the standardized form of a session-key permission. Execution modules are dispatched by CALL (so they run in their own storage), layered under the facet map.
| Dimension | ERC-7579 | ERC-6900 |
|---|---|---|
| Proxy | AccountDiamond |
ModularAccount6900 |
| Factory / init | AccountFactory / AccountInit |
AccountFactory6900 / AccountInit6900 |
| Module identity | module address (per type) | ModuleEntity = address ‖ uint32 entityId |
| Module kinds | 4 fixed types | validation / validation-hook / execution / execution-hook modules |
| userOp validator selection | top 20 bytes of userOp.nonce |
first 24 bytes of userOp.signature |
| Validation scope | by module type | global (per-selector opt-in) or a per-selector allowlist |
| Hooks | one global pre/post hook | per-validation pre-validation hooks + per-selector pre/post exec hooks |
| Execution extension | fallback handlers (CALL or DELEGATECALL) | execution-function registry, dispatched by CALL (own storage) |
| Session-key permissions | SessionKey library (ad hoc) |
a validation + attached hooks (standardized) |
| Introspection | DiamondLoupe |
IERC6900AccountView (getExecutionData / getValidationData) |
| Config authority | account-self or admin | account-self or admin (config is admin-gated, not validation-gated) |
The ERC-6900 facets: ERC6900ModuleManager (install/uninstall validations + executions),
ERC6900Executor (execute / executeBatch / executeWithRuntimeValidation plus the proxy's
execution-module dispatch), ERC6900Validation (ERC-4337 validateUserOp), ERC6900Signature
(ERC-1271, ERC-7739-bound to the account's domain so signatures can't be replayed across
accounts), and ERC6900AccountView (the loupe). Reference modules SingleSignerValidation and
SpendingLimit demonstrate the validation and execution-hook module shapes.
Diamond facets must be stateless — proxy state lives in the Diamond, not the facet — so facet modules are split into three files with strict responsibilities:
src/interfaces/<area>/IFoo.sol # ABI, custom errors, events. Wide pragma (>=0.8.4)
▲ # interfaces mirror the module's <area> folder
src/<area>/libraries/FooLib.sol # ALL logic + ERC-7201 storage + __Foo_init(...)
▲ # storage read via a single FooStorage() -> hardcoded slot
src/<area>/Foo.sol # stateless facet: virtual fns that forward to FooLib
// src/<area>/Foo.sol — facet: no state, no logic, just delegation
contract Foo is IFoo {
function bar(uint256 x) external virtual returns (uint256) {
return FooLib.bar(x);
}
}A handful of utility libraries (see above) skip this split: they are pure logic with
no own ERC-7201 slot, no interface file, and no facet — the consuming module owns any
storage struct they operate on. A few contracts are standalone rather than facets where the
standard or deployment model requires it, for example AccountFactory, GovernorStandalone,
TimelockControllerStandalone, AccessManagerStandalone, and VestingWalletStandalone.
Install as a Forge dependency:
forge install dadadave80/lattice
git submodule update --init --recursive # diamond-lib + forge-std submodulesAdd the remappings (mirror of this repo's remappings.txt / foundry.toml):
@lattice/=lib/lattice/src/
@diamond/=lib/diamond-lib/src/
forge-std/=lib/forge-std/src/
Because facets have no constructors, proxy state is set up through diamond-lib's
InitializableLib with a three-call dance the consumer performs once per module:
import {AccessControlLib} from "@lattice/access/libraries/AccessControlLib.sol";
import {InitializableLib} from "@diamond/.../InitializableLib.sol";
function initialize(address _admin) external {
bytes32 s = InitializableLib.initializableSlot();
InitializableLib.preInitializer(s); // set initializing flag, check version
AccessControlLib.__AccessControl_init(_admin); // module init (gated by checkInitializing)
InitializableLib.postInitializer(s); // clear flag, emit Initialized
}When adding new modules, be deliberate about caller semantics. Some existing modules use
msg.sender directly because they authenticate protocol callbacks, Safe calls, EntryPoint
calls, or Diamond self-dispatch. If a module is intended to support forwarded calls, use the
project's established caller-resolution pattern consistently through the library layer.
A reference deployment of the self-governed, ENS-named ERC-4626 vault recipe
(DeployGovernedVaultENS) — one diamond hosting the
share token, vault, vote checkpoints, Governor, TimelockController, EmergencyStop, and a governed
upgrade path, with no external admin: the diamond administers itself, so a passed, timelock-executed
shareholder proposal is the only way to upgrade or reconfigure it.
| Diamond (vault) | 0x7a498c34A8Dc3B6502889C21218Da0F8696b7bb6 |
| Primary ENS name | milestone1vault.lattice.studio.eth (forward + reverse) |
| Underlying asset | TestnetAsset (open faucet: mint(address,uint256)) |
All 14 facets are source-verified on Etherscan: ERC165Facet · AccessControl · TimelockController · ERC20 · ERC4626 · VaultCore · Votes · ERC20Votes · Governor · GovernedVault · DiamondLoupeFacet · EmergencyStop · GovernedDiamondCut · ENSReverseClaimer · (init: GovernedVaultENSInit)
Reproduce against any fresh testnet in one command (asset 0x0 auto-deploys a faucet asset; the diamond
claims its ENS reverse record at init):
forge script script/base/defi/DeployGovernedVaultENS.s.sol --tc DeployGovernedVaultENS \
--rpc-url sepolia --account <keystore> --broadcast \
--sig "run(((address,string,string,uint8,uint256,uint48,uint32,uint256,uint256),address,string))" \
"((0x0000000000000000000000000000000000000000,\"Governed Vault Share\",\"gVLT\",0,300,60,600,0,4),<ReverseRegistrar>,\"<name>\")" \
--verify --etherscan-api-key "$ETHERSCAN_API_KEY"The broadcast run log for this deployment is committed at
broadcast/DeployGovernedVaultENS.s.sol/11155111/run-latest.json.
After deployment the diamond governed itself on-chain: a shareholder proposal — deposit → propose →
vote → timelock queue → execute — froze the loupe/cut selector set and reasserted the ENS name through the
diamond's own Governor + TimelockController, with every step cranked by
script/config/governance-demo-loop.sh. Execution proof:
ProposalExecuted tx
(run log: governanceDemo-latest.json).
forge build # compile
forge test # run all tests
forge test --match-contract AccessControl -vvv # verbose, by contract
forge test --match-contract StorageSlotVerificationTest # verify ERC-7201/ERC-165 slots
forge fmt # format (CI runs `forge fmt --check`)
forge snapshot # gas snapshotsThe suite currently includes unit, integration, fork, fuzz, invariant, and gas tests. CI
runs with FOUNDRY_PROFILE=ci (optimizer_runs = 1_000_000, via_ir = false). A green
local forge test does not guarantee CI passes if optimizer behavior diverges — run
FOUNDRY_PROFILE=ci forge build --sizes before pushing if you touch hot paths.
src/
├── access/ # AccessControl(+Enumerable,+Timed), AccessManager(+Managed,+Standalone), Ownable
├── accounts/ # Diamond smart accounts — erc7579/ & erc6900/ flavor subfolders + shared (ERC-4337/1271/6551, session keys)
├── amm/ # ConstantProduct
├── crosschain/ # CCIP, Axelar, Wormhole, ERC-7786, bridge tokens, cross-chain timelock
├── defi/ # Aave, Compound, Curve, Lido, Uniswap V3, ERC4626 adapters, vault/strategy modules
├── ens/ # ENS resolver, reverse claimer, subname issuer
├── governance/ # Governor, timelock, governed/Safe diamond cuts, Safe Harbor adoption
├── oracles/ # Chainlink, Pyth, RedStone, Chronicle, DIA, API3, Band, Tellor, Gelato, TWAP
├── privacy/ # Commit-reveal, stealth address standards, Groth16/PLONK, Semaphore, shielded pool
├── security/ # Pausable, ReentrancyGuard, RateLimiter, CircuitBreaker, EmergencyStop, InvariantChecker
├── tokens/ # per-standard subfolders ERC20/ ERC721/ ERC1155/ ERC2981/ ERC4626/ ERC7802/ (base+extensions); MarketplaceZone at root
├── utils/ # EIP712, Multicall, Nonces, VestingWallet(+Standalone)
│ └── libraries/ # Crypto, encoding, strings, checkpoints, math, multicall/nonces/vesting helpers
└── interfaces/ # I<Module>.sol mirrored into per-<area> subfolders; external/ for third-party ABIs
Each <area>/ also contains a libraries/ subfolder holding the <Module>Lib.sol
logic libraries for that area.
MIT