Every Division Picks a Winner: Rounding and Accounting Hazards in Lending Protocols

Devcon 8 · 4 Nov 2026 · 21:04

Aave Labs principal engineer Dhairya Sethi and independent security researcher Tomer Ganor dissect two findings from the published Aave V4 security reviews and generalize them into a hazard class for share-based protocols. Fee computation that rounds down can make observed share price drop, while fees earning interest before they become shares can push share price up faster than the interest rate itself.

Speakers

Topics

From Devcon 8, hosted by Ethereum Foundation.

Transcript

[music] Thank you everyone. So um I'm Tom Ganor independent security researcher formerly tech lead at Sartura and I designed a v4 and light v3. >> Uh I'm Daria Seti. Nice to meet you. I'm a principal smart contracts engineer at a labs primarily worked on a v4 and a v3. Our talk today is about rounding and division. It's a important aspect for any lending protocol or any D5 protocol to take into mind because as we know we only have fixed point arithmetic on chain. Uh there are precision losses, probabilistic losses when it comes to divisions. So here we're going to talk about rounding and especially mainly

cover two topics. What rounding direction you need to take because each direction you take results in a donation somewhere which means that somebody wins and somebody loses. We're going to define that. We're going to define the types types of entities and we're going to talk about another important aspect which is less commonly known about when a rounding needs to be committed. So let's start. So just a quick primer just to warm up very quickly what happens during a rounding. Let's say that you have 10 assets and a share price of three. Um you need to mint some shares. What do you do at that point?

Since 10 is not equally deserable by three. You have one modulo remaining. Who does that go to? You have to gift it to someone. It cannot vanish into thin air. It cannot uh be donated to an outside entity. You need to decide who it gets. Who gets this? Either it can be the protocol, it can be the user, it can be some some other entity. But the idea is somebody has to win. So rounding forces you to pick a winner. Let's talk about the types of entities here about so that we can classify who wins and who loses here. So we can roughly classify entities in D5 protocols as active and passive. Active entities are entities which initiate

transactions which control timing which control commitment because these entities come into play when any action needs to be committed on chain. These control when the transaction lands under what conditions at what price point at what thresholds at what parameters. So these are very important entities. So these entities are also usually incentivized which means since they're performing an operation maybe like a call back or uh an event trigger for a certain price drop during a liquidation they need to be incentivized for performing that action either being by repaying the gas or by being uh very minutely correct during ME auctions to correctly execute the transaction at a particular time for the most efficient

and um best auction. Then we have passive entities completely polar opposite. These are the entities on which an action takes place. Their account is modified. Their account basically either it's their account that gets the donation or somebody else. So it's these are the complete polar opposite not um usually incentivized and essentially the ones that bear the burden of this. So let's define them. Essentially in a D5 protocol you can classify entities these four kinds of entities protocol treasury user and liquidator we've moved them in order of what they look like. So um of whether they can be passive or active protocol

is always always always the most passive entity and we're going to come up with a blueprint which is that always round in favor of the most passive entity. So this is the blueprint that you have to take in mind. Protocol will always be the most passive entity and always round in favor of the protocol. Then comes treasury. Usually uh fee receiver or some other entity. Maybe you have a curated market or something else or you're sharing with somebody else the fees the revenue. So these entities can be active or passive but mostly lie on the passive side in most uh design protocols because they can also initiate operations maybe let's say harvest some fees back into the protocol. In that case they become a bit active. Then comes the user. the user lies more on the active side because to initiate

their account their operations they're active but when let's say there is an event trigger on their account let's say a liquidation at that point they become passive because the liquidator modifies their account to sell off some part of their collateral and repay their debt liquidator is always the most active relative to these guys and this is the blueprint that you have to take in mind because liquidator the old job of like liquidator or maybe a harvest uh keeper their job is just to rebalance the position or um perform certain action at a certain condition. So the blueprint is always round in favor of the most passive entity. So uh using this guidelines you can decide when to round against who?

>> Thank you Daria. So uh basically the key property that we want to the key behavior that you want to have is always round in favor of the passive entity whether it's the protocol or the user or the treasury or whatever always round in favor of the passive because the active as D said could control the timing couldn't control um the repetitiveness so we want to round against them always. So let's see in lending um what kind of roundings do we have? So we have the trivial cases. Let's start from them because they're the easiest and then we go to the more complicated ones. So the easiest one which is not only for round only for lending. So that's rounding in every protocol. It's for the supply and

the withdraw. So that's like in AMM also in lending also in in staking event. We always want to round against the user. So those are the easy ones. That's in the supply for example when the user supplies money to the protocol it want to rounds against the user. That's trivial y. Let's just say it um even though it's trivial case if the user gets rounded in his in his favor he will split his deposit to multiple deposits he will get extra uh value from the protocol either he steals money from the protocol or create insolveny or something bad is happening same withdraw the rounding up in the withdraw is not in favor of the user we round up the amount that we take from the user in the internal accounting um of the protocol and that means that for the example from

the If the user tried to withdraw 10, we would actually remove 12 from his position. Or if the user try to supply 10, we actually create him nine. So these two roundings are trivial and not only for lending, but for all kind of protocols. Very important. It's also very easy, but it's not always happens. So whenever this is broken, probably there is a bug in the protocol. And these things happen in the past. And now something which is specific for lending, which is the bar and the repay. So very similar um to a seminology to the withdrawal supply. Whenever we borrow we always um credit more debt to the borrower which mean basically if you borrow 10 you will owe 12 in the example

with the index is three and when you repay 10 you only repay effectively nine in your position because that's the only the only way it makes sense. Otherwise, you repay 10 and you effectively repay 12, which is of course um very good for you because you just repay one, repay one, repay one and you effectively just owe less. So that's not good. So those are the trivial cases. Effectively, the user is donating money to the protocol or to the treasury or to someone which is not himself. So there is no incentive to initiate more operations than what is the minimal required which is a key uh point here that we'll get back to in the next example. So the fee so in the fees that I said it might be

two options here the treasury might be passive might be active. Let me give an example of the passive and the active. So when you do a liquidation um in for example in a V4 there is a fee that the treasury charge from the liquidator and the liquid wants to get paid. So if the liquidator uh gets this bonus uh which of course we run against them next example then the treasury needs to take a fee from that and the fee the treasury takes is rounded against the liquidator and in favor of the treasury. So in this case the treasury is passive. So we round against the liquidator in his in his favor in the treasury's favor. So the amount we charge is rounded up towards the treasury. But when we want to reinvest

this money to the protocol then treasury is active because now the treasury is effectively supplying into the protocol and in this case it's like a user. So I want to round against him round down like supply. So it's not like every entity has one key property always runs against this. The liquidator is the most extreme one. So we will say probably round against him always. But we want to recheck our our understanding just to make sure. But in in this case the treasury is both both active and passive in the liquidation which is a more trivial case but it's more complicated case. So you always want to round against the liquidator. But what does it mean round against the liquidator? In what point? So we have three different

points that we want to round against the liquidator. We have the borrow the amount of the borrow position the liquidator pays which is always uh we need to pay over the amount that we intend to pay. So overcharge. We have the amount that the liquidator gets paid which we underpay him because we take the the collateral from the user which is passive because he didn't initiate this. He doesn't want this. He's losing money. So we're going to under undercharge him and we're going to underpay to the liquidator and from this collateral there is the bonus that the liquidator gets again we will give him a bit less and from this bonus itself we will overcharge him again the liquidator to pay to the treasury from the previous

example and that way we are effectively underpaying the liquidator we're overcharging him basically we're doing all of this betting to the liquidator and why do we do it because the liquidator is in control if the liquid doesn't like this rounding, he doesn't like this behavior, he can just not initiate this operation. So we and and if we run it in his favor, what will happen is he will split his operation to as many as he can. Of course, there is a limit because of the target health factor in V4 because of the health factor in general. Maybe the position is already solvent. So there is no need for more liquidations. Um and but he will try to maximize this and and this in this way we incentivize him to just do one operation and that's it. And the

last example is the index which basically two examples. So in a v3 for example and for this example we have the debt index and the supply index and the debt index is basically the basically like the counter of how much interest was accured for the debt position. And as we said, we want to round against the user in every case which is not a liquidation. So we always round up the debt of the user. So if you have let's say the index is 3.5 and you owe and you borrowed like three when the index was one. So you you owed uh three at the start and now you owe 3 * 3.5 which is 10.5 which is 11 like round up. So this

calculation is easy. That's from before you owe 11. But in this rounding what I'm talking about is the calculation of the index itself. So whenever you calculate the index we have some um exponent some multiplication something in every calculation that we do in this context of the of the do that index we always want to round up always want to overcharge overcharge the user overcharge everyone and in the same in the same way but in the opposite way um for the supply index you always want to round down to underpay the user underpay all of the users to make the protocol solvent so every calculation that we have the debt index we round up up whenever what can want to calculate the supply index well as round

down h every time both in the value itself and also in the calculation of the index itself. So thank you Thomas to now we know who wins and what direction to take such that we always pick the right winner. It's very important to pick the right winner because if you don't the an external entity can extract value from the protocol. So super important. Now another overlooked part is when is the winner picked because it's very important for the same aspect of value extraction. So in when the main thing that we think about is commitment. When is the rounding committed? When is the donation given? This is uh important to

know um that this does not happen when you preview. So let's say that you have an external protocol that is interacting uh with your D5 protocol. It previews let's say your share price. It previews your balances. It previews some conversion somewhere. If you have donations in your preview then those donations are not committed and then then if they preview after the after some blocks you can retroactively take some donations back. So this is a very key property. So let's say that we take an example we take example of some uh let's say RV v4 an earlier design what we had was that we wanted to compound fees on every action and a core property that you need to know about uh a lending protocol is that in the case of deficit or shortfall we don't socialize the

losses we keep them aside until an insurance fund like umbrella can come in and repay the debt so this leads to a core invariant of the protocol that the share price does not go down share price monotonicity So let's just focus on that. So the idea would be that we have a protocol which compounds fees on every action. Like Tor said fee can be thought of as a supply. That's the mental model that you need to take. So whenever let's say there's some acrruel to for the fees it's like the uh protocol or the uh treasury supplying on their behalf into the protocol which means you round against them. So if there's like an imprecision the external donation amount goes to the protocol and effectively goes to all suppliers. it gets socialized to all suppliers and treasury

gets a little less. So let's visualize this so that it's easier to compl comprehend and understand. Let's say that we start with a share price of one. There's no donation. There's no u acrruel. Let's say some time passes and there's a donation of 6. At this point, let's say that someone previews. First of all, what would happen with the treasury? They would let's say that they need to supply this 6 uh on behalf of the treasury. Since uh the share prices are um it's not a perfect share price, you need to divide that by let's say that the share price is one. We're taking multiples here. So since it's not perfectly divisible the 6 that's the donation. So what happens is that treasury gets zero shares because you've just previewed it's there's not there's

not even been one share worth of increase. So if you preview here that 6 that gets donated to the protocol to all suppliers gets socialized everywhere at this point let's say that somebody previews at this point they say that everybody gets socialized the 6 and the protocol uh has not earned any share because there's not been any compounding but remember we just previewed this there was no storage right on our protocol it has not been committed yet now let's say some more time passes and now the share price has gone up or like the value Cruel has gone up to 2.3 for example. At this point there has been more than one share worth since the last commitment. Last commitment happened at one. Now there's been 1.3 above that. If

you supply 1.3 shares 1.3 assets on behalf of the treasury, you get one share back and.3 gets donated out. But we've already made the donation in the preview if somebody interacted with our protocol. So what happens is that here we retroactively take back the 6 and we give it to the treasury back from the users. So this is a very core property. So here you can see that at this point when somebody previewed the share price would have been 0.01.006 and if they came back uh after a certain time because we did not commit them we retractively took back the share price went down 2.03. Our core invariant that we talked about the share price does not go down. and share price monotonicity that has been broken and that is a very

critical path because this can be exploited. So just to sum things up here, you need to know when to round, you need to know who to round in favor of and always think about your winners. Just to just so that you don't have any lingering feelings after this, the way that we solve this in our V4 is that you don't want any donations or probabilistic models in your previews. You want them to be as simple as possible. So the way that we solve this in our V4 is that we don't compound on every action. We compound we choose that uh we control the timing of that because as we talked about before we want to be the most active entity here. So we keep treasury more passive. So the idea would be that we only keep the treasury uh share aside in asset terms. We don't

compound it. We don't donate it back. So it's just a simple division. It does not go into the denominator. So it does not get there's no donation there. And we choose on our own terms when we want to reinvest those um earnings back into the protocol. Uh that's all from us folks. Thank you so much. Let's see if we have any questions. Yes, I'm back. So, I forgot to mention we have a QR code for questions. Um, I don't know if any of you got it. Um, but yes, just like a a couple of notes. I know that if I remember right both of

you were talking about this topics uh already right [snorts] DSS last year >> yeah it was separate than this but yes we talked about it talk about this and DSS as well >> cool so yeah so actually right now what we are having is like uh after the deployment when you see the results and the the reports are out so you got the the pre- one and then the other one so you so you're collaborating for a while right >> u basically I've been working with on this on V4 for the past what year and a half or something, right? I think basically >> like from March last year it's been >> Yeah. And a V4 has been under brainstorming for a year and then Tom joined in and helped us fix the design

and basically shipped the code. >> Yes, it was very very fun uh process. >> Nice. Yeah. Uh sounds like and you mention about if I got it right I write a note during the talk that um like finding like uh price like finding price is uh like rising faster than interest is that price is that part of what you have? Uh so that's kind of a gift for depositors.

So who is actually paying for that? Uh so this earning that that interest that gets donated back if uh it's not enough to uh basically mint for the treasury. But the idea is that um when borrowers take a loan they pay back the loan with interest and that interest is what gets distributed to all suppliers and to the treasury. That's the only source of yield essentially. So the borrower interest is the one which increases the share price. >> Got it. Cool. Uh nice. um like uh oh we have >> so for that maybe I can answer and so the previous slide discrepancy in v3 so in v3 this uh doesn't exist this behavior that I described it's because

that in v3 there is as I said supply index and that index and v4 there is only that index and share price so the shares because they are full accounting of the the all of the suppliers the full accounting of all of the assets on in the system um Then this potential problem doesn't exist but potential problem exist would have exist only in sharebased um supplies which is like v4 and other lending protocols that have this approach maybe even uh other protocols which which is not lending and uh so this doesn't exist in v3 just for the to >> cool we have another one >> um for the audience what triggers compound in v4 does it even happen

automatically >> yeah so I can take this one on RV4 like I said we fix this by controlling the compounding ourselves we have certain permissionless keepers which can trigger this at a certain threshold let's say that it's enough to it's worth it to compound at that point but we don't compound it at every block we take some time but yes anybody can compound in a v4 it's a manual trigger right now >> anymore Yeah. So, [music]