Let’s say you’re a hardcore Solidity nerd. You’re probably going to want to stick around for want follows. If not, suffice it to say that in the process of demonstrating a somewhat esoteric concept, an incredibly useful protocol emerged.
If you’re still here, put on your splash goggles, button up your lab coat and let’s go…
Most extensible among Solidity contract proxy patterns is the EIP-2535 Diamond Multi-Facet Proxy specification. It allows a proxy to have more than one upgradeable implementation (logic) contract. The Diamond architecture is extremely useful for almost any non-trivial contract suite, lending a modular building approach that can place any amount of logic behind a single Ethereum address. Having built upon the Diamond architecture multiple times, I highly recommend it.
However, one criticism auditors and implementers have often raised is the level of complexity involved in managing the “Facets” (how the Diamond spec refers to implementation contracts) and their function selectors. That complexity is, for the most part, tucked away out of sight and works flawlessly. But it does beg the question:
What other forms might the multiple-implementation proxy pattern take?
Fismo began as an experiment in what I will call Deterministic Selector Proxy design. The idea was that the function selectors would be determined not by calling complicated maintenance functions to associate a given function selector with its implementation, but rather by generating the function selector on the fly somehow, based on the execution context.
Nifty idea, but without a problem domain, this hypothetical deterministic selector proxy would have no context within which to formulate function selectors to be proxied.
FSMs are a perfect match for this experiment. As problem domains go, it’s relatively simple, but still non-trivial. You can describe them in a lightweight way, validate the descriptions easily, consume them in Solidity, and place them into contract storage. It is straightforward to enforce that transitions between states follow the edges of their directed graph. Fismo does that automatically.
But there is one place where you need to add custom code: guarding state transitions.
In the Lockable Door example, going from the Locked state to the Unlocked state should require that the user have a key. That could be an NFT, or just some state in the contract.
This is where you need to write some code and deploy a guard logic implementation contract. It is also where we get a chance to test the Deterministic Selector Proxy hypothesis at the heart of the Fismo experiment.
In the machine definition for the Locked state, the
exitGuarded flag should be set to
true and the
guardLogic property set to this guard contract’s address. When Fismo tries to transition between the Locked state and the Open state, it will attempt to execute the following function by combining machine name, state name, and guard direction.
LockableDoor_Locked_Exit(address user, string calldata _action, string calldata _nextStateName)
This requires a developer to write function signatures in a very specific way, but it nicely demonstrates the Deterministic Selector Proxy concept with no chance of function signature collision, since:
- Each machine name must be unique
- Within a machine, each state name must be unique
- There are only two valid guard directions
- The Deterministic Selector Proxy concept is fully demonstrated. Implementations for other problem domains wherein the expected function selector can be determined from execution context alone could follow this pattern for implementation. For instance, a geo-tagging system could have selectors based on a global coordinate scheme rather than state machines.
As a result of the experiment, we ended up with a protocol that can be used to simulate, oh, I don’t know…
… just to name a few. What will you build?
- The Fismo contract is prohibitively expensive to deploy if you just want to create interesting machines for people to interact with.
- At the time of this writing, it approaches $2000 USD to deploy Fismo to Ethereum mainnet.
- By adding support for ERC-1167 Minimal Proxy cloning, Fismo allows anyone to deploy their own fully functional clone.
- At the time of this writing, it costs about $40 USD to clone Fismo on Ethereum mainnet.
- And finally yes, you could try to make a clone of a clone. And it would work… ok.
- Unfortunately, like Micheal Keaton in Multiplicity, you would realize a sort of fidelity loss with each successive clone in the chain.
- Although the logic would operate the same and the clone would store the data, each clone would be delegating the call to the clone it came from, increasing the transaction cost with each delegation.
- To avoid this hidden expense for the unwary, the
cloneFismomethod reverts if attempting to clone a clone.