Web3 and DeFi today aren't ready for mainstream users; it’s all too confusing, risky, and expensive for anyone who isn’t a dedicated crypto head. There are some obvious things that need to be fixed, like the horrible developer experience and the crazy requirement to manage a long seed phrase in order to safely backup an account. But there’s another huge barrier that doesn’t get much attention: transactions make no sense, and don’t give users and developers the control they need to confidently use this very new technology.
The problem isn’t just one of wallet UI; in fact it’s mostly a problem with the architecture of the L-1 platform itself. Today’s transactions are defined in a way that is limited by technology assumptions, rather than defined by the needs and expectations of the developers and users that will interact with them everyday. No wallet can get around these limitations.
That’s why Radix had to redefine the concept of transactions from scratch as part of its full technology stack. The result is one of Radix’s most powerful advantages, fixing a surprising range of important DeFi problems like unsafe wallet user experience, difficult dApp interoperability, sandwich trading, and the impossibility of delegated fee payment. It also lets the Radix Wallet present transactions to users like this, completely trustlessly:
But before we get to Radix’s solution, let’s talk about why today’s transactions are so in need of an overhaul.
What’s In a Transaction?
To understand how deep the problem with transactions is, we have to talk about what a blockchain transaction actually is.
The contents of a transaction on a smart contract blockchain today are driven very much by the tech. On most smart contract networks, everything is a smart contract – not only dApp logic, but the definition of assets themselves. Within the constraints of this model, it’s unsurprising that a transaction is defined as a call to a smart contract.
This means that on these networks, the main part of a transaction is essentially a message that is sent to a single smart contract, containing whatever data is needed to tell it what to do. When the smart contract receives that message, it might make some changes to its own internal data, and it might call other smart contracts behind the scenes (like ERC-20 token smart contracts) that in turn make some changes to their internal data. Anything and everything that happens as a result of that transaction has to be kicked off by that single message to the smart contract.
A little more is needed before the transaction can be submitted to the network. The smart contract call is signed by a single account’s private key as the “caller”, and that caller tells the network how much they’re willing to spend from that account for network fees (or “gas”).
Generally speaking, that’s all the transaction contains: a message to a smart contract, a signature from the user’s wallet, and a specification of network fees to pay.
So What’s the Problem?
This way of defining a “transaction” works, technically speaking, but it leaves a lot to be desired because it doesn’t describe things the way the user signing it would. As a user, I might be trying to do a transaction that swaps tokens through a DEX, or buys an NFT, or takes out a loan – but what I’m signing is always just a single message to a smart contract black box that I hope will do what I expect.
That “message to a black box” transaction design creates some serious shortcomings that we may often take for granted in crypto today:
- Wallet users (and wallet software) can’t know the actual results of the transaction. The wallet only knows that a certain smart contract is being called. All transaction results are internal changes committed by internal smart contract logic that is practically unknowable ahead of time. In fact, on many chains, the user signs a hash of the smart contract call, making it even more obscure.
- Users can’t protect themselves from “sandwich trading” or slippage. Taking the example of a DEX, the only way to offer the user slippage protection is for the DEX smart contract to offer it as part of its internal logic (and the user to trust that implementation).
- Authorization patterns are very simplistic. The only way for a user to authorize themself to smart contracts is via their single account key signature. Anything more complicated means (you guessed it) deploying another smart contract.
- Composing together multiple smart contracts means deploying another smart contract. The transaction needs its single entry point, which can then go make multiple calls to other smart contracts. This means composability requires planning ahead, and is laborious and rigid.
- dApps can’t pay network fees on behalf of their users. The single-signer caller pattern means that only that user account can pay the fee.
There are various proposals that seek to reduce the severity of these shortcomings of the fundamental transaction model by working around them. For example, ERC-4337’s “account abstraction” allows (among other things) the possibility of a form of delegated fee payment, but at the cost of significant system complexity and risk.
No matter what workarounds are added, however, the issue remains that transactions don’t work in the way that users or developers would want them to – if the platform technology weren’t the limitation.
To fix the problems above, we need to redefine the concept of transactions so that they are much more powerful and flexible. They should give developers more power to directly define more complex interactions, and should put the user in control of what matters to them when they sign, rather than having to trust black box smart contract logic.
In the next blog, we’ll talk about how Radix does exactly that with a new kind of transaction design, enabled by Radix’s unique full-stack platform.