In our first major Alexandria milestone since the release of Scrypto v0.2 in December, we are introducing a critical new layer of the protocol - the transaction layer. Readers of our blogs and social channels have heard lots of talk around the importance of atomic composability - the ability to combine arbitrary actions across multiple components in a single transaction - and we’re now ready to show the first implementation of this capability which is so crucial to decentralized finance.
In this post we’ll talk about how this new transaction layer works. We’ll also talk about a new virtual badge system and component-based accounts that work together with the transaction layer to enable a much more robust security model than any other DLT today.
Developers can start working with these features using the Alexandria simulator tools for Scrypto v0.3, available right now. For those not familiar with our releases, please note that Alexandria is not integrated with mainnet; the fun stuff described here won’t be available on-network until the Babylon release.
The problem with existing transactions
Typically on smart contract platforms like Ethereum, transactions are mostly a one-trick pony: sending a method call to a smart contract. The result of that call is some combination of updating the smart contract’s internal state and often invisibly calling methods on other contracts to coordinate some state changes. The sequence of steps is locked-in; all your transaction can do is pick an entry point and the rest is out of your hands.
So where does that leave composability? If you want a series of contract calls that hasn’t been pre-wired, you need to deploy a new smart contract which will follow those steps. While this works (and is the basis of composable dApps on Ethereum), it’s very limiting. There’s no such thing as on-the-fly composition of applications.
Also, this type of transaction leaves the user virtually in the dark about what will actually happen. Even just sending tokens to a smart contract means giving permission to the smart contract to change your balance (leading to many exploits) and hoping that your desired movement of tokens actually takes place.
The asset-oriented components introduced with Radix Engine v2 and Scrypto address big parts of these problems by making assets first-class platform features. But to complete the picture, we need asset-oriented transactions that allow free composition of components and make asset movements transparent to the user.
Introducing Radix’s asset-oriented transaction layer
Transactions on Babylon (and the Alexandria simulator for now) are focused on orchestrating the movement of resources between components. This new form of transaction is built by composing a sequence of component calls, each of which can be passed data and (unique to Radix) resources, and each of which may return some set of resources for the transaction to then do something else with.
This sequence of instructions is described in a transaction manifest which fully defines the series of steps which all must take place, else the entire transaction will fail and nothing will happen. The amazing thing about this approach is that now transactions can directly compose components together in the transaction alone, defining the desired movements of assets between them (and your account). Additionally, the user is no longer blind-signing a smart contract call and hoping the right thing happens; they can see what they care about – the movement of assets – in the transaction itself. Let’s see how this works.
We’ll start with a simple example of a transaction manifest. Alice has an account component which holds some XRD, and she wants to send 100 to Bob. Here’s a picture of what that transaction manifest looks like, with some slightly simplified syntax:
Let’s break that down step-by-step and explain what’s going on.
First, she has to get the 100 XRD she wants to send. She does that by calling the withdraw method on her account component, which returns a bucket containing 100 XRD. The contents of that bucket are automatically emptied onto the transaction “worktop.” You can think of the worktop like a physical checkout counter where you can put and take resources (like tokens and NFTs), and make assertions about what is present at any time over the course of the transaction (more about those assertions later!).
Note: The worktop is ephemeral, and lives only for the duration of the transaction. At the conclusion of execution, the worktop has to be empty of resources, or the transaction will fail. It’s not possible for resources to be accidentally lost if you forget to send them somewhere.
Next, Alice takes 100 XRD from the worktop, and puts it in a bucket called my_xrd, so it’s packaged up to be passed somewhere else.
Finally, she calls the deposit method on Bob’s account component, passing in the my_xrd bucket. His account will securely store it within a vault that it controls, and Alice has completed her transfer.
This way of describing even something as simple as a token transfer is both more composable and more closely akin to how we think of physical money in the real world. I take money out of my wallet (because I’m allowed to do that), put it on the checkout counter, and push it to the other account (which is happy to accept money!).
On Ethereum even a simple ERC-20 token transfer means sending a call to the token smart contract to request a movement of balance from your account to Bob’s. It’s more like a bank interaction than working with money that you control – and like with a bank interaction, you have to trust that your request will be handled properly. With Radix transactions, the user is fully in control of how tokens move.
This more physical approach naturally expands to cover much more complex transactions, where movements of assets between many components can be described safely and intuitively.
Let’s look at a more complex example of a composed DeFi transaction.
There is a lending component, which accepts XRD tokens as collateral and loans out ZOMBO tokens. When providing a loan, it also hands out a non-fungible token which acts as a tokenized representation of the loan terms.
Let’s do something interesting with that lending component! Say Alice controls two different accounts that each have some XRD. She wants to take 50 XRD from one, 100 XRD from the other, and then put in all that XRD as collateral for a new loan. She then wants to give the resulting ZOMBO to Bob, but keep the NFT representing the loan. She wants all of these actions to happen at once, or else forget about the whole thing.
This will certainly be a one-time operation that Alice won’t be repeating in the future, yet to do it as a single atomic operation on other platforms would require writing, testing, and deploying a custom smart contract in order to pull it off. On Radix, all of this can be done on-the-fly, in a single transaction manifest, like so:
Neat! This ability to compose different things right at the transaction layer, without having to deploy code to bring together the pieces you need, is a powerful capability which is particularly useful for “DeFi dashboard” types of services. Such services on Ethereum deploy numerous smart contracts which chain specific combinations of calls together to make a series of normally independent actions become atomic and single-step, and then help direct you to the appropriate “combo contract” with a web UI. On Radix, such applications won’t even need to touch the code layer. They can live entirely at the transaction level, constructing ad hoc combinations in response to ledger conditions, without the need to develop and test a custom contract, or wait for (and pay for!) deployment of a copy of it to the network.
The transaction manifest isn’t just there to make life easier for web developers - it also makes it practical for wallets to display a visualization of a transaction that a user is being asked to sign. No more looking at a mysterious string of characters labeled “data” and hoping that you’re signing what you asked for. You can eyeball every step and see what’s going on.
Let’s get back to those “asserts” we mentioned earlier. Within the transaction manifest it’s also easy to insert instructions that add additional conditions that must be met for the transaction to go through. For example, this means that we can ensure that only some expected minimum amount of tokens must be deposited in our account. A DEX transaction might take 50 XRD and return about 100 ZOMBO, based on a current market price which isn’t guaranteed. Rather than hoping for the best (or relying on the DEX creator to let you specify a limit in your call), the transaction manifest can specify that you want to put in 50 XRD and expect to deposit at least 99 ZOMBO at the end. When the transaction is processed by the network, if the DEX tries to hand back only 92 ZOMBO, the transaction safely fails and no XRD ever leaves your account.
But wait a minute…in the transactions above, how does the transaction ensure that only Alice can withdraw from her account component? There’s one bit we haven’t shown yet.
Virtual badges from signatures
If you have dug into Scrypto at all, you will have come across one of our most important patterns - the use of badges for authorization. This lets Scrypto applications move away from the common Ethereum-style access control pattern of “who is the originator of this transaction” which has caused so much trouble, and instead use a model of “does my caller show me the proper proof of authority to execute this action?”
Prior to Scrypto v0.3 there was a piece missing from the story. Members of our dev community have been very excited by badge-based authorization, but have had no way to handle the cases when they do need to see if a certain signer existed on a transaction. Since most transactions start with a withdrawal from an account-type component, how can you guard access to that withdrawal? Enter virtual signature badges.
As with transactions on other DLT networks, Radix transactions must be cryptographically signed by a user (or users) before they are submitted. On Ethereum and most DLTs, these signatures are directly linked to an account address and immediately provide full authority to act as that account.
Radix uses signatures differently. Just before transaction execution, the system performs a step which verifies each signature attached to the transaction, and then automatically creates a (non-fungible) “virtual badge” for it. Each virtual badge is linked to a signer’s public key. Now a component can use a standard authorization pattern to require a virtual badge – or even a set of virtual badges – associated with the appropriate public key or keys.
Note: Virtual badges always vanish at the end of a transaction and can not be stored in any vault, so a given signature is good for only a single transaction.
The implications of this pattern are profound, allowing for a clean translation of signatures into our universal authorization mechanism: badges! This permits easy separation of authentication (who are you? Demonstrate possession of some secret knowledge to prove it) from authorization (what are you permitted to do?). This is an important concept in the non-DLT world that we are bringing into Radix in an asset-oriented way. It enables really exciting stuff like native multi-signature control of accounts.
Everyone we have demonstrated this to nods along politely, then a day or two later the lightbulb moment goes off while they’re in the shower, and then they can’t stop talking about it (and if you get said lightbulb moment, make sure to drop into the Scrypto channel on Discord to share your thoughts!).
Keys are not accounts and accounts are not keys
There’s an important concept that comes out of the transaction manifest and virtual badges: there is no 1:1 link between a signing key and an account, nor a single “caller” account in a transaction. An account might require multiple virtual badges to withdraw from it, and a transaction might include multiple accounts with resources moving in and out of them.
This is a key concept to understand for Alexandria and beyond. Coming from any other DLT network (or Olympia!) you are accustomed to this mapping:
Which is nice and simple and understandable, and a smart contract can always ask what account/public key initiated the transaction that called it. But, in this type of architecture, transactions are very limited (as we’ve seen) and worse, it creates a pretty fragile security model for accounts. If you lose access to your private key, well, bye-bye to your account and whatever it held. If you’re worried that your private key might be compromised, you have no choice but to generate a new one, find out your new address, and transfer everything there as quickly as you can.
This pattern is so well-known to any crypto head that it doesn’t even rise to the level of conscious thought. That’s just how accounts work, right? But moving to the Radix model has enormous real-world advantages.
Let’s examine the difference through an analogy. Your account is like your house. It’s where people can send you stuff. It’s where you keep your stuff. Taking the typical key-maps-to-account model, you only have a single house key, and you can’t ever change the locks. If you lose your key you have to move to a new house and abandon all your stuff forever, and tell all your friends and businesses your new address and start from zero again. Wait, what?
Nobody would buy such a house in the physical world, yet in the DLT space it’s all they’re selling so this terrifying situation is just accepted. On Radix, you will be able to change the locks on your house at will. In fact, you can set up multiple combinations of locks and safeguards, and leave a way for your lawyer to eventually get access to the house in the event you unexpectedly fall into a volcano with your keys in your pocket.
The important things for developers to remember for Alexandria & Babylon are that only components may store resources, and keypairs have no mapping to addresses. Everyone who transacts on the Radix network will have at least one account-type component that they control, and a standard account blueprint will be provided for all to use, but nothing stands in the way of community developers creating special-purpose account blueprints to meet a variety of specific needs.
What’s next for Scrypto?
We have a lot in the pipeline, but one thing in particular that we’re going to be focusing on is an update to the mechanics of how badges and authorization work now that we have the transaction layer to build upon. At the moment, authorization logic is all at the application level; anyone can call any method, and code in the blueprint controls whether the caller gets past the first step. That works, but it doesn’t allow for the right amount of customization for people who might want to instantiate a component from a blueprint and use a different authorization scheme than the one specified by the blueprint developer. Also, it’s currently a bit cumbersome to deal with manually passing the correct badges around. We can do better.
We’re in the late stages of a design which will enable system-level authorization, with a much more convenient way of placing appropriate badges on the worktop and not worrying about manually passing them around. A call which doesn’t have the appropriate authorization doesn’t even get to enter an inaccessible method - the system aborts the transaction immediately. The new design enables basic blueprint authorization rules to be defaults which can be overridden by instantiators where appropriate. Highly complex rulesets can still be implemented at the application level, but the system-level authorization will easily handle the 95% case.
We are also cooking up a nifty, asset-oriented mechanism to safely enable callback-like functionality, which will make applications like flash loans extremely straightforward to implement and understand. This will let us entirely disable re-entrancy on each transaction step (and the host of hard-to-reason-about problems that accompany it) while still giving developers the power to permit a called component to enforce certain transaction “post-conditions” before allowing it to complete.
If you haven’t checked out Scrypto yet, this is the perfect time to start building! We regularly hear from community members with little or no development experience that they have been able to create working applications just by following the documentation and examples, and asking questions in our Discord, so don’t be intimidated!