Same-Slot L1 <> L2 Composability
EthCC[8] · 1 Jul 2025 · 21:05
Lin Oshitani presents a same-slot composability design between Layer 1 and Layer 2. Deposits, transactions and withdrawals are combined into one atomic bundle within a single Layer 1 slot.
Speakers
Topics
From EthCC[8], hosted by Ethereum France.
Transcript
Hi everyone. Um I'm Lynn Oitani from Nethermind and today I'm going to talk about same slot L1 L2 composability. Okay. So let's first understand the problem here. Like suppose you have assets in L1 and now suppose you want to deposit into an L2 do some complex dex trade in L2 and withdraw back to the L1. Now right now in the current like uh non-based rollups what happens is that what you have to do is when you and when you use a conlo bridge you when you deposit into the L2 it takes like two to five minutes. Next, the dex trade. It happens pretty fast once you're in the L2.
But next, withdrawing back to the L1. This also takes very long like 30 minutes if you're a ZK rollup or 7 days if you're an optimistic rollup. So like here like L2s which are supposed to scale the L1 feel like a completely different execution environment. It doesn't really feel like you're extending the L1 execution. So what if you can do like the deposit and the dex trade and withdrawing back to the L1 all in a same L1 slot.
So in this talk we call this like same slot L1 L2 composability because you're composing between L1 and L2 in the same L1 slot. And once you have this like this will make L2s feel much more like a natural extension of the L1. You can keep your assets in L1 like do crazy stuff in L2 come back to L1. you wouldn't even have to feel like you're using an L2. Okay, so that's what we want to do like but before like getting into the solution, let's go through some background. So like first like let's start with the L1 block because the L1 block is where everything happens like everything starts. So the L1 block you have O1 block and you have a bunch of L1 transactions within it.
Okay. And you also have the O1 state and this has account balances and smart contract state. So Alice Bob they have an EOA and there's a DEX contract. Now like how these two interact is that basically the L1 transaction in the L1 block like it says that Alice sends Bob one ETH and this happens in L1 state and like the very nice thing about L1 is that it provides like a single thread of synchronous execution. So Alice can do send one to Bob. Bob can immediately use that one in a DEX and then like Carl can use that same DEX with the same like against the same liquidity pool.
So now these are like all three like consecutive transactions. So what's happening here is that like this is possible because L1 transactions can depend on the result of the previous L1 transaction. So you have like a bunch of L1 transactions. Each of them depend on the exact result of the previous one. This seems very very like obvious but eventually this is not going to become obvious. Okay. So now let's go to L2s like L2 roll-ups specifically. So rollups are like a scalability solution that works by embedding the L2 block within the L1 block. So now you have like the L1 block within it. This specific transaction you can see that it has a L2 block attached to it and within it like you have a bunch of L2 transactions.
And this can be in a blob. This can be in call data. And like these L2 transactions, they are posted to what's called like the L2 inbox contract in the L1. So this transaction that has a bunch of these L2 transactions, it's submit to L2 inbox. And furthermore, L2s introduce a separate layer for L2 state and execution. And what happens is that like this transaction goes to the L2 inbox and instead of executing these L2 transactions like in the L1 state, you throw it to the L2 state and the L2 state will handle like the execution and yeah exe in the the execution and the like maintaining the L2 state. So L2 scale by like L1 by outsourcing the execution to the L2 while like keeping
the data in the L1. Okay, so that's all nice, really good scalability. But however like the L1 and L2 are now like completely different threads of execution and dependencies. So like you can see here like all these L1 transactions they can depend on the previous L1 transaction and so on so forth. And the L2 is the same like L2 transaction can depend on the previous L2 transaction but they're like completely separate threads now. So we lost that like cool synchronous like um thing that we had when we just had L1. So the big question here is how do you introduce a dependency between the L2 transaction and the L2 L1 transactions?
Okay, so like how do we do this? Like one way is to actually like inject the past L1 state roots into the L2. I'm not going to go into too much detail about this, but like one problem around this is that the L1 state route of an L1 slot is not available during that slot. It's only available like for the past slots. So you can't do like same slot composibility using state routt. So like one of the solutions that we have is like what we call same slot L1 L2 message passing and the the like what we want to do is simple like we want to have L2 transactions that depend on the L1 transaction. For example, a deposit would be a good example in the same L1 slot. So within the same L1 block you
could have a deposit and then you could have like L2 transactions that actually reference the fact that this deposit happened. So you want to introduce this dependency. Okay. So how do we do this? So the idea is this like first you have the L1 transaction like like this first L1 transaction which can for example be a deposit thing. you have this like call into on what's called an L1 messaging messaging contract in the L1 to store the messages. So the messages for example can be a deposit but it's more can be more general. So this transaction is pushing a bunch of messages into the message service and the message service like stores the hashes of these messages.
Okay. Next what happens is that you have the batch proposal like so with the L2 transactions and what what's new here is that this batch proposal is specify the hashes that it depends upon. So it's saying that hey I want to depend up among these messages a bunch of messages that like hashes to these like hashes. Okay. And next what happens is that the L2 inbox it can like condition on the fact that the hashes exist in the message first. So like this transaction it previously pushed the hashes to the message service like it conditions against it.
Now what happens is that you can have like the L2 inbox emit the event to actually trigger the L2 execution and you can pass in the L2 hashes together with it and then these like hashes are like stored in the L2 message service. So like the nice thing that you get here like you tunnel through all the hashes up until the message service with the condition that they actually exist in the message service like in the L1 side too.
What you get is that like L2 transactions can now depend on the messages injected into the in the same L1 slot. So now like let's say that that first transaction in that L2 batch it can like call to an EOA that calls into a contract and this contract can check that whether hey does these hashes actually exist you can condition it against it and now like so like yeah so you push the transactions message service p to L2 and then like in Z2 you also have this conditioning so now you can read these hashes in the same slot in the same L1 slot duration as this like the L1 transaction that happened.
Okay. So now we got like one leg of the dependency in the same slot by conditioning the L2 batch against the previous L1 transaction. So what basically what happened is that you have this like for example a deposit transaction in the L1 side then you can have like this L2 transaction actually depend upon the fact that this deposit happened because like you're depending upon the message that the deposit actually emitted and yes so this enable so yes you get this this leg of a dependency so they're no longer like these independent like executions so note that this was only possible because like the L2 is willing to reorg with the L1 one hence most likely be based because like if this deposit reorgs out
like the L2 transactions like these will be like reorked out too because there's a dependency so like this is one of kind of like the nice feature of being based like you're willing to reorg together with L1 so you can get this fast synchronous composability with L1 so yes this enables L1 to L2 deposits so how about the other leg as in the withdrawals we can get into L1 to L2 who in the same slot how do you get back? Okay. So here like there is another solution that we came up with which is we call like fast and slow L2 to one withdrawals and the idea is this um like or like before the idea like let's see what what
a withdrawal is in the first place like so withdrawal is basically like in the L2 batch that you have here you have a withdrawal transaction like that that guy at the end. Now how this withdrawal transaction works is that like you okay you post a batch to the L2 inbox you push the batch which includes a withdrawal to the L2 and then this withdrawal transaction let's say that A actually initiated this A will be sending one E to a withdrawal contract and next like the L2 state will be be eventually settled to the L1 which means so like this L2 the fact that the L1 E was submitted like sent to the
withdrawal contract eventually after some time passes it will settle to the L1 and once it's settled to the L1 the L1 know that like the L2 it kind of knows that the L2 state is like this specific state so it can actually trigger the deposit so like one will actually be sent to a okay so like this is what we call the slow path of withdrawal and this is basically what you already have like in the most of the L2s Now the question is like how can we make withdrawals faster like ideally in the same L1 slot.
So one way is to have real-time proving. So like the thing that we have to wait here is like we have to wait until settlement which means like the proof of the L2 execution actually lands on L1. So if you can actually pro prove within this like same L1 slot then you can actually get like same slot L1 L2 L1 withdrawals. But like the when and how is unclear like when as in like when can we actually get real-time proving like a lot of say people say it's soon but how soon is it and how or whether we actually do it do it if even if it's technically possible is also an open question because like if you actually settle every L1 slots it's going to be very expensive. So you need some form of like proof aggregation which is very open research topic.
Okay. So like the another way that we like actually we are looking into is to rely on solvers. Okay. So like what does our solver solution look like? First like the solver we assume that the solver like will backrun the L2 batch submission which includes a like withdrawal with a solver transaction. So this is L1 transaction and it back runs this batch submission that includes a withdrawal transaction. Okay, next what we like one of the things that we do is that like we have this batch submission happen and when the batch submission happens like we actually store the L2 batch hash into the L2 inbox. This actually already
happens to make the proof work but here I'm making it explicit. Okay, so after storing the batch hash like next what happens is that like the event is emitted to trigger the L2 execution. The withdrawal happens as we've seen before. And here's where things get interesting. So like you have the solver transaction like what does this guy do. So what this guy do is that like the solver actually like what they're trying to do is they want to front the withdrawal liquidity to the user. So they want to like enable immediate withdrawals for the L2 user. So A is figuring the you want to send one E to E. How this happens is that like so the solver can actually condition
their solver transaction this transaction against the L2 batch hash. So you can see that I'm only going to be willing so S is a solver I'm only going to be willing to send one E and send one E to A if the L2 batch is exactly this and by saying that if the L2 batch is exactly this also specifying that the withdrawal is exactly that. So by conditioning on the batch hash you're conditioning on the batch. By conditioning on the batch you're conditioning on the withdrawal. Okay. So like this is the idea of like this um L2 to L1 fast withdrawal through the solver. So the key idea here is that you enable the solver to condition against the batch. This is one of the things that existing solver solutions do not have.
Okay. And then like later on the solver gets compensated through the soul path. So like you'll settle and then in the deposit contract it'll check the solver contract check if it's solved and if it is like it'll send one to the solver and some tip payment can happen also. Okay, so like we went through the L1 to L2 and the L2 to L1. Now like combine this, okay, like we went through the L2 to L1. Combine this with the L1 to L2 solution and revert protection or like a shared sequencer or builder between L1 and L2. Like there's basically a base rollup. What you get is a very very nice atomic bundle between the L2 and the L2 transactions. So what you have here is that you can have like a deposit and you can have a L2 batch that conditions
against the deposit and then you can have like the batch transaction and then you can have a solver transaction backing it condition against the fact that the withdrawal actually happened. So now like before they these two were like completely separate lanes of like execution now like you can have these interdependent dependencies like between the L1 and L2 and all in the same one block. Okay. So like the conclusion is that like dependency and conditioning is like the key to composibility I would say. So like if you look into where you can introduce like these conditioning things like I think it's a very good way of thinking about composibility.
Another thing is I haven't touched too much around the base rollup part but like a lot of this is enabled by being a based rollup. So base rollups are great for L1 L2 composibility and like in the best case it can make the boundaries between the L1 and L2 like virtually disappear. like you don't even know that you're using an L2 in the best case. And yes, we like at another mind are implementing these solutions and more together with the Tao team and also like we're also implementing it in our search stack which is like built on top of the Tao stack and we are also like researching a lot of like more cool extensions around this too.
Okay. So here I have a bunch of references against yeah the posts the first post and the third one is interesting post from fabric guys. I won't go into too much detail but it makes a not the L1 to L2 not only about message like deposits but more general like view calls to the contract. And the fourth one is from Gwennneth's team which is like goes much much further like you get really really synchronous composability which is super cool basically by like re-executing all the L2 L1 transactions in the L2 side. So you can get like the latest L1 state route in real time. So that's a pretty cool solution.
Okay. And the last one is from uh James in Devcon and yeah it goes over like some of the silver topics that I go over and it's a very good talk so recommend it. Okay. Thank you. All right, we're going to have a few minutes for questioning. We're going to raise your hands. I'm going to bring the mic to you. This guy was first and I'll bring bring it around. Thank you. Just to understand the dependencies when we can actually use that stuff. My understanding was we first need based rollups and real time proving. Is that correct? Okay. So like you you so for the L1 to L2 side so like
this leg well wait um so like this dependency like you you basically like realistically speaking you need to be a base to because you have to rearch together with the 01 and but this like actually like one way to do it is real time proving but other way is the solution that we talked about which you includes the solver so it's either like you have the solver with conditioning or you have real time proving and yeah if you have real time proving quickly then we actually like don't need all this but yeah that's an open question. Thanks for the talk. Uh actually I didn't get a clear uh concept of the the server for the the fast path. Can you go to the fast pass? Yes. So how does the
the server the someone who provides a liquidity here catches this opportunity and provides liquidity like uh will there some user interface for it like like order book? Yeah. So like the solver like will have to somehow know the fact that this batch is like being proposed right like because you want to background this. So like and knowing that they can like put this back on transaction like and the question is like okay like how do they know that this actually going to happen like you need some form of coordination between like who's building this and who the solver is and one way would be to like have once you have pre-confirmations in the base rollup then the pre-confer will
can say that hey I'm pre-confing this withdrawal and now the solver can condition against the the block that has this withdrawal And like since preformation is off protocol they'll know ahead of time. So like they have you already have the co coordination mechanism. Another way would be to just have this like solver transaction be actually included by the L1 proposer who's actually also the L2 proposer. So they know exactly what the withdrawal is. So like they can insert but in that case you don't really need the batch hash conditioning because like you you trust yourself. But yeah those are the two coordination mechanisms you can have.
Uh great talk. Um you mentioned Gwyneith and how they have sort of an alternative approach. So what kind of uh relative advantages or drawbacks would there be of of that approach? So like the Gwennneth is I would say much much more ambitious like it's trying to do really really crazy stuff. So like the idea is like this is an L1 block, right? You say that this isn't an L1. You have a bunch of L1 transaction. You have a bunch of L2 transactions. you put this whole thing in a single L1 transaction. So this is what they call ot transaction and this whole thing becomes an input to the L2. So the L2 is actually like re-executing all the L1 transactions and the L2 transactions in that whole
bundle. So like the L2 like in real time knows exactly what the L1 state is. So you can have like real time reads against L1 state against like all the L1 state actually. So it's not just about these deposits, it's all the messages. So like the so that's very cool. The drawback is that it's um you need a lot more like coordination between like well actually like the builder has to have create that ultra transaction that has one bundle that includes all the one and two transactions. So it requires like a lot of buyin from the builders and like the tech is yeah it's like I would say very like early stage but I think it's super cool. So I think like we're taking more of the like incremental approach of how
we can get to 01 and good composibility. They're taking like more more like drastic approach like starting from what the ideal thing is and working from there. All right, we got time for one more question. Going once, twice. All right, thank you so much today, man. I appreciate it. Thank you.