Modsefa's Origin: A New View of Validators and Transactions
When I first started prototyping Cardano dApps, I didn't begin with the on-chain validator script. Like many developers, I took a "transaction-first" approach. I used a simple always-succeed validator that would allow me to do anything. My focus was entirely on the off-chain client and conceiving of the set of transactions an end-user would actually want to build. This let me prototype the end-user's experience quickly.
If the prototype proved successful, the validator script felt like a late addition—a check or safeguard I'd add on top of the transaction builders I had already perfected. But this created a fundamental problem: I was writing two sets of code that were clearly just two sides of the same coin. The off-chain transaction builder knows what a valid transaction should be, and the on-chain validator checks for that very same thing. The trouble with this traditional development is that it forces us to define this logic twice. There's such a strong connection between them, so why must we do this?
To explore this, I went back to first principles and developed a simple mental model. I want to be clear that this isn't a formal model or a rigorous argument, just an articulation of the simple model I had in my head to think through the problem.
First, I simplified. I ignored real-world complexities like context-dependency (e.g., transactions that must happen in a specific order) or multi-validator dApps. I focused on a single validator dApp and its set of non-context-dependent transactions.
In this simplified model, I conceived of the "set of all possible transactions" on the blockchain. From this perspective, a validator is just a "classifier function". Its only job is to look at any given transaction and include it into one of two subsets: "part of my dApp" or "not part of my dApp".
This led to the key insight: the validator and the "set of valid transactions" are in a kind of duality. If you have the validator, you could (in theory) discover the set by exploring all possible transactions. And if you have the complete set of valid transactions, that is a (non-analytic) form of the validator! They aren't truly separate things.
The problem, of course, is that in the real world, you can't just "have" the "set of all valid transactions". You need a way to formally define it. This is the central idea that led to Modsefa.
Modsefa is a type-level DSL designed to do exactly this: formally define the set of valid user "Actions" (transactions) for your dApp. You define your dApp's logic once in this high-level specification. From this single source of truth, Modsefa generates both the on-chain validator script and the off-chain transaction builders.
It solves the "two codebases" problem by making them one. The generator is the "function that could generate all valid transactions", and the validator is the "classifier" that perfectly matches because they came from the same source. And while this all started with a simplified model, the Modsefa DSL is powerful enough to re-introduce and handle the real-world complexities like context-dependency and multi-validator applications that we initially ignored.
The shift in perspective from viewing validators and builders as separate, duplicated tasks to viewing them as one unified definition is the core concept of Modsefa. I encourage you to explore our documentation and tutorials to see how this single-source-of-truth approach works in practice.