Reflex is an open-source framework for building and maintaining upgradeable Solidity applications. It consists of a few abstract smart contracts designed to be inherited and extended in a Solidity codebase. In this post, we discuss why we built Reflex and what it accomplishes.
The code is available on Github under the GPL-V3.0 license.
Historically, Solidity building blocks have taken the form of libraries like Solmate, OpenZeppelin, and Solady or in EIPs like EIP-2535 (The Diamond Standard). Libraries, by their nature, are collections of code snippets that optimize for expressiveness rather than a holistic abstraction. EIPs require too much consensus at the outset of an idea and leave little room for experimentation. We propose that the natural evolution of Solidity development tools are barebone, comprehensive, and opinionated frameworks.
At Chroma we are always looking to contribute to the frontier.
As we began to architect and build a new lending protocol we ran into several questions and challenges:
How do we build a legible system that exceeds the smart contract size limit?
What is the best way for the proper authority to maintain upgradeability controls?
How can we better combat the common attack vector of cross-contract reentrancy?
How do we tackle the issue of function selector clashing?
We dove into all fronts of research on existing solutions and concluded the architecture for Reflex was the best solution. Realizing the benefits this architecture could have for not only our application but also other builders we split the contracts into two pieces:
The upgradeability logic and modular architecture; Reflex.
The business logic; Coming Soon.
As the name suggests, Reflex is a nod to React, the popular JavaScript framework for the web. Like React, Reflex is built on the abstraction of modules. This software development paradigm has proven to be successful in other programming languages and ecosystems. Reflex’s modular architecture allows for better flexibility, scalability, and maintainability of Solidity applications. Each module can be updated, replaced, or removed without affecting others.
In React, developers need not concern themselves with the internals of React Fiber’s reconciler to use React in their application: it just works. Similarly, Reflex alleviates developers from the implementation details of building upgradeable smart contracts.
Reflex consists of:
A built-in installer allows implementers to add, upgrade, and deprecate modules throughout the lifespan of the application.
Three core module types, each specialized for different use cases. These modules enable Reflex to support multiple standard compliant ERC20s, ERC721s, ERC4626, or other token types inside of the protocol, side by side.
Periphery modules. The ReflexBatch module, for example, executes and simulates performing batch calls across any external methods in any module.
A single global reentrancy lock covers every storage modifying method in the application.
Function hooks enable implementers to add conditional checks or extend behavior inside their application.
To get started with Reflex we created Reflex Template, a minimal template repository demonstrating a simple counter application built with Reflex.
We built Reflex for the community and its developers. Reflex is still in an experimental phase leading up to its first audit. We would love to hear your feedback on how we can make it better. We encourage the community to optimize for gas efficiency, add periphery modules, expand the documentation, and try to break it. Contributions are welcome by anyone.
If you are interested in using Reflex, we would love to hear from you and are happy to help.
Parts of the architecture are directly inspired by Euler's Proxy Protocol. We are thankful for their extensive documentation and novel approach to modularization.
We are also thankful for the Diamond Standard, the Default Framework, and numerous other contributions to the upgradeable contract design space.