A Vision of FISSION
Application-level messages for Ethereum and beyond
This is part of a series of posts discussing FISSION (ERC1066 and associated infrastructure) in varying levels of detail. FISSION codes are a similar-but-different to solutions on other systems like HTTP, tagged tuples, and other signals. These similarities make it easy to jump to incorrect assumptions about the scope and intent of this project.
It’s early days for Ethereum. The first release of the platform was in 2015. The community has been making steady progress since then, but it’s not surprising that there’s a lot of basic infrastructure that doesn’t yet exist, tools that can be improved, and fundamental shifts on the horizon. Low hanging fruit abounds on several layers, and we can get a huge amount of value from a small amount of design thinking.
FISSION is one such project that aims to fill a fundamental need in the ecosystem. We want to build a future that’s more inclusive, easy, clear, and automated. It’s something that seems so obvious when you stop to think about it. By far the most common reaction that we have is “how does this not exist?”
Oh, The Pain!
If you’ve ever done serious work with Ethereum, you know that there’s a lot left to be desired as both a developer and end user. Transactions succeed and fail for unclear reasons, the feedback that does exist is aimed at a narrow audience, and it’s very difficult for smart contracts to interact without a lot of very bespoke work.
No system has ever won by forcing you to solve the same problems over and over again, with granular and bespoke work. The very nature of a shared system (the EVM) implies an interdependent network of nodes, but we’re leaving our smart contracts in silos.
FISSION helps us understand and build interoperable smart contract communication. It establishes a common vocabulary for smart contracts to interact with a rich set of messages to tag data and common situations. It has applications in state transfer, development tooling, instrumentation, and user messaging.
Broadly speaking, the FISSION strategy falls into three parts:
- Improve feedback for humans
- Help developers understand their code at runtime
- Enhance contract interoperability and autonomy
User Experience (UX)
Ethereum runs critical applications with real world consequences. Users must be able to understand what’s happening in a clear way, in the language and phrasing that best serves them.
The state of the art today is limited. We sometimes get textual feedback when things go wrong (ie:
revert-with-reason). We never get feedback when things go right (ex. “is the token purchased or in escrow?”). Furthermore, feedback is always hard coded. Not only is this inflexible, but not everyone speaks English! A truly global system would give feedback in a language that the user speaks well.
Developer Experience (DX)
At time of writing, the developer experience on Ethereum… “could be better.” Given the context of other modern ecosystems, EVM code is very difficult to debug. The existing tools are very low level, and developers have to wade through core dumps and cryptic error messages. We are starting to see improvements with features like
revert -with-reason, and better support in tools like the Truffle 5 beta 🍙
While libraries such as OpenZeppelin exist (and are widely used), we’re mostly relying on complex inheritance graphs to stand up contracts. Inheritance introduces a lot of indirection, hides implementation details, and can be confusing to trace which method variant ended up in the final contract.
Smart Contract Autonomy
Smart contracts are not reaching their full potential. The trend today is to snap together one or two smart contracts from [off-chain] libraries, overwrite a few methods, point them at each other, and let them live out their days tightly coupled. Others that want to talk to smart contract need to deeply understand the internal logic, coupled to your custom
enums (if any!), make guesses about the state, and almost always
revert if things don’t go precisely as expected (there simply isn’t enough information being passed around to do otherwise).
This leads to ecosystem duplication and segmentation for both functionality and data. We can do better; we can save time and energy, have fewer bugs, data collection, and use business models not widely seen on-chain today, all with less manual human intervention.
At it’s core, the underlying properties of the platform break neatly into two halves — one of which has been partially solved for already, but is deeply underused:
- Decentralized behaviour / shared and collaborative functionality
- Decentralized state / closed data ownership
The future of smart contracts is interop and state transfer. Information wants to flow.
Collaborative Control Flow
We have been promised a platform where smart contracts behave more like bots: able to make decisions, make requests to each other, without an owner beyond “the community”. We are not living this glorious future just yet.
The current state of the art is to use concrete interfaces and keep as much functionality contained in our single contract as possible. Overriding inherited contract functions may lead to unexpected behaviour to an external caller, not to mention that long inheritance chains being one of the most confusing forms of indirection.
On-chain libraries partially solve a portion of this issue, but from reviewing the code of a few dozen companies, it doesn’t appear to be a widely used strategy. This is partly due to tools not providing an easy way to discover and plug-in a deployed library, especially when developing locally. It’s also conceptual: if a library is “part” of your contract, of course it would live at the same address, right?
Today contract collaboration is extremely limited outside of tightly coupled bespoke systems. If our protocols are defined exclusively by their disparate function signatures, the best that you can hope for without tremendous effort is a simple call-and-response pattern. We are missing out on much more powerful flows: many-to-one collaboration, handshakes, reusable microservices, pluggable data stores, truly autonomous validators, flows that have an off-chain component (ex. government ID check), multi-transaction sessions, on-chain smart-contracts-as-a-service (SCaaS), and much more.
Shared multi-user systems like Ethereum lend themselves to easily sharing data. Data is decentralized in the sense that we don’t have one giant database with open access. Unlike how we can use on-chain libraries to share functionality (rather than redeploying the same classes over and over), the true value of most applications is the data that they contain.
The very essence and power of having shared platform is in how it facilitates the flow of information […] This is a major untapped advantage of this technology!
Today’s architectures silo data behind access control and programmatic interfaces with only brittle and limited options for requesting this data. We need a way to help data flow between these silos.
The very essence and power of having a shared platform is in how it facilitates the flow of information. We pay for this abstraction with a speed penalty, but at the application layer there is no need to worry about the common issues in distributed systems: availability, stale state, locked data, and so on. This is an oft-overlooked advantage of this technology, so let’s make the most of it!
The FISSION Suite
FISSION is the overarching brand for a suite of standards and tools that help improve the ecosystem with a low overhead, opt-in, no hard fork, easy-to-implement messaging layer for smart contracts. (Phew, that’s a mouthful!)
FISSION: Fluid Interface for Scalable Smart contract InterOperable Networks
Why a suite? While we have grand ambitions, and each piece of the project sits together into a cohesive whole, we want to keep slice focused and as general as possible. Ideally the scope of a particular spec or library can find life beyond one team’s imagination! In the spirit of decentralization, we would love to see new use cases, remixes, and combinations as the ecosystem evolves 💪
Ethereum Improvement Proposals
A well-defined spec is more important than a reference implementation. The community can get involved at the conceptual level, agree on a design that works for everyone, and importantly isn’t confined to any particular language.
At time of writing, FISSION has two EIPs:
The core of the FISSION Suite is ERC1066: Status Codes. These provide the common interface to build protocols from. This strategy isn’t limited projects under the FISSION umbrella; we encourage everyone to build robust protocols from these building blocks!
The core idea is very simple: 256 codes organized as a 16x16 table. The columns are categories (ex. general, permissions, identity, off-chain), and the rows are reasons (ex. failure, success, informational, required action). They are passed around as the first value in a multi-value return, or as an argument to a function.
There is a helper library to make it easy to construct codes, inspect them for conditions, automatically
revert on failures, retrieve human-readable localizations, and so on.
The status code spec alludes to being able to translate status codes for better user experience. We realized that this was not limited to status codes, so we factored out ERC1444: Localized Messaging. Status codes and localizations still have great synergy, and our team is actively pursuing translating status codes.
The preference contract is very simple: users can register their preferred localization, and the singleton forwards the request on to the correct localization. There is no single list of localizations, and anyone can create and register their own.
Localization is very commonly overlooked. Our aim is to make this as automatic and seamless as possible: developers will only ever need to use the FISSION library and not some special hand-rolled translations code. Users get simplified English by default, and can choose a different language once and all contracts are translated. Outside of deploying your own
Localization, this functionality should “just work” with zero effort from application developers.
Nodes and Edges: Better Together!
Not to be confused with a fluent interface, FISSION Codes can be seen as a fluid interface — an common interface flowing between nodes. This stands in contrast to the concrete interfaces that we commonly see today on Ethereum: method signatures. These are not mutually exclusive concepts, but rather two halves of a very powerful whole!
In the above diagram, the dotted arrows are requests (or calls). The rest are responses (or returns) that contain FISSION codes; green arrows are success responses, and orange arrows are neither successes nor errors. By convention, the code is the first value in a request or multiple return.
The Thin Red Line
The red lines show irrecoverable exceptions and
reverting — FISSION fully supports
revert-with-reason, and the helper library has several functions that make it easy to combine the two.
FISSION fully supports revert and revert-with-message […] Of course we should immediately revert if there’s an overflow, or find ourselves with nonsensical state
We now have a much richer language than
revert, so we can let our smart contracts interpret if a state is safe or not. Of course we should immediately revert if there’s an overflow, or find ourselves with nonsensical state. These errors are not recoverable, and that’s exactly what
assert are there for.
In other cases, a collaborator contract may not have all the context that it needs to know if a flow needs to explode. As the old programming saying goes: “if in doubt, ask your parents” (ie: delegate to caller). The caller still has the option of
reverting if it makes sense, and we haven’t robbed it of the ability to make that determination.
The caller has a few options when receiving a message:
- Interpret the message as acceptable and continue execution
- Forward the unchanged message to another contract (ex. its own caller, or a collaborator), which then has these same options
- Recontextualize the code (ie: replace it with one clearer for this situation), and forward to another contract, which then has these same options
It’s All Semantics
Since there is a consistent interface of arrows flowing between nodes, we have a common vocabulary of meanings being passed between contracts. We’re no longer fulfilling the bare mechanical requirements of a function call: our smart contracts are having a conversation. They have enough information to act more autonomously, listen for certain scenarios and ignore others, switch behaviours, and explicitly ask for human intervention when it’s needed.
This is complementary to concrete interfaces […] method interfaces are primarily mechanical (the “how”), data and status codes are primarily semantic (the “what”)
This is complementary to concrete interfaces such as ERC20’s function signatures. We absolutely still need to know what a function is expecting as input (for mechanical reasons), and what it promises as output. Once we have that, we can look at the status code and have a great deal of understanding about what happened elsewhere in the call stack, where we are in a state machine (such as several parties agreeing to some terms), or metadata about the rest of the response.
“How” vs “What”
Higher-level approaches to programming (languages and architectures) tend towards imbuing code with rich context. Method signatures and interfaces are primarily mechanical (the “how”), data and status codes are primarily semantic (the “what”). Of course it’s a spectrum, and there is overlap: “how” and “what” depend on each other to make sense at all. They compliment and enhance each other, but are each oriented to different purposes.
Learning to Share
Since we have a consistent interface of arrows, we can build tools to help determine the meaning of a request or return. Libraries and helper contracts can help us reuse common behaviour, make state changes, call singletons contracts, and so on.
In the diagram above, we see two flows sharing common nodes. This is absolutely possible without FISSION, but requires the developers to understand much more about the internals of the shared node. The FISSION helper library lets developers understand the context of a call or return, with more fidelity than a Boolean, and without having to understand a custom
enum. By watching for certain types of code, the helper library can even set assertions about the types of codes that you expect, and revert automatically — with human readable messages in whatever language the user prefers.
Smart contracts decompose into two parts that may be of interest to others: behaviour and data. Behaviour on its own may be deployed to the network as a library. They are very flexible chunks of code that may be verified by the community as safe to use. In fact, this is arguably safer than even redeploying it from your own contract, since you have certainty about that exact bytecode.
Libraries are similar to contracts, but their purpose is that they are deployed only once at a specific address and their code is reused
~ Solidity Library Documentation
Unlike behaviour, data is owned by particular contracts, and depends on a program to get and set values. The data itself may be valuable to other contracts (ex. a token balance or authorization). Rather than duplicating this data, a single home is both efficient and convenient. For any stateful data, this is obviously something that must live on chain rather than being redeployed.
Requesting data comes with its own set of use cases: access control, a missing value, expiration, out of range, and so on. Any smart contract that needs to request data from a collaborator (ie: lots of contracts) can leverage FISSION helpers to make it easy to autonomously handle these use cases without writing reams of code, or demanding human intervention.
The Future is Fissile
Today we see teams overwhelming using simple point-to-point request, with all the pros and cons that come from keeping requests limited. We envision a future with smart contracts able to intelligently react to requests — leveraging common infrastructure within and without a contract for higher confidence, rich and efficient behaviour, and interoperability with the broader world.
Common Autonomous Networks
A single call may spark calls to other contracts. Today these networks are very rare. Smart contracts should be able to react to a variety of common scenarios, ask each other for help, forward requests, and generally behave like complete agents. By leveraging the behaviour and data of larger networks of contracts, the system can become smarter than the sum of its parts.
Patterns can be generalized out into protocols. A protocol designer can marry FISSION with function signatures as a common language for single- and multi-step protocols. Ad hoc flows are still possible (and indeed easier) because we have some advance understanding of what’s potentially coming out of a request.
Validated High Quality Services
Rather than validate logic that keeps getting redeployed with varying levels of quality, third-party or community-driven verification can lead to trusted high quality contract services that live on chain and never change. By enabling these to receive and respond to requests with a common vocabulary, compatibility with a broader ecosystem increases, and these services are easier to integrate with.
Bridging the Divide
By using a common set of codes, we can translate these into other systems. Automatically generating HTTP status codes via a server library is the obvious case. Looking a bit further down the road, since one byte is so portable we can conceivably use this as standard messaging between chains when the bridges are built.
Get in Touch!
The FISSION team would love to talk to each of you about how status codes and localizations can help make your smart contracts easier to develop, more autonomous, and give your users better feedback! Feel free to schedule a call (phone or video), or make a comment on EthMagicians.