Back to Overview
A clean, graphic illustration of a cool penguin wearing a backpack, standing on the side of a digital blockchain highway with its thumb out to hitchhike.

The Hitchhiker's Guide to Rebasing Tokens

March 9, 2026

Rebasing tokens offer seamless UX but introduce additional complexity for smart contract integrations. Because balances fluctuate based on global indices rather than simple integers, rounding errors are inevitable. We dissect the four fundamental "quirks" that can cause logic reverts in AaveV3 and Lido integrations, and explain why internal accounting in "shares" is the golden rule for safety.

In the landscape of DeFi yield-bearing assets, developers typically encounter two types of tokens: those that increase in value by increasing their price (like RocketPool’s rETH), and those that increase in value by increasing the quantity of tokens in your wallet. The latter are known as rebasing tokens.

While rebasing tokens like AaveV3 aTokens and Lido stETH offer a seamless experience for end-users, they introduce some overhead for smart contract integrations. Because these tokens represent 1:1 ownership of a fluctuating pool of assets, their balances are not stored as simple integers. Instead, they are calculated on-the-fly using an underlying, per-user "share" count and a global decimal "index."

This dynamic calculation introduces rounding errors that, while negligible in value, can cause unexpected states and logic reverts in contracts interacting with them. For this reason, many protocols decide not to support rebasing tokens altogether.
Those wishing to integrate with Lido without facing the code complexities related to the rounding arithmetic of stETH can use the official wstETH token, which is its non-rebasing counterpart, mapping 1:1 with pool shares rather than underlying ETH. Another way to interact directly with shares is by using the custom transferShares() / transferSharesFrom() functions. Both these approaches are documented in Lido's official token integration guide.
A similar alternative exists for Aave, represented by the stataTokens: they are an EIP-4626 compliant, non-rebasing wrapper around aTokens. However, they are less well-known and widespread in the ecosystem, therefore aTokens remain dominant even for on-chain integrations.

While these non-rebasing variants are often the best option, sometimes a DeFi protocol just has to support rebasing tokens: if this is your case, or if you are simply curious to learn more, then read on. We will dissect how the correspondence between shares and balances affects integrations, and we will learn what to watch out for in order to build safe, production-ready vaults and strategies.

The 4 Quirks

The rounding involved in the share-balance conversion breaks some properties that would otherwise be obvious for standard ERC20s, and which developers might be relying on to ensure correctness of their logic. We have identified 4 such "quirks":

  1. Balance deltas can be different from the transferred amount, both for sender and for receiver.
  2. The sum of the two balances can change, i.e. the balance deltas may not be equal for sender and receiver.
  3. Transferring one's whole balance in one go is always possible, but might leave dust behind.
  4. Splitting one's whole balance in two parts, and transferring both, is not always possible.

We have investigated these properties, and have precisely bounded the resulting "deviation" from the non-rebasing behaviour. The following table summarizes the results. Note that Aave aTokens and Lido stETH employ different rounding conventions:

1. Directional Rounding: Who Pays the Weis?

When a user calls transfer(amount), the protocol must determine how many shares (σ) to move. Since the amount and the index are rarely perfectly divisible, the protocol must choose a rounding direction. If a is the transferred amount and i is the (decimal) index:

  • AaveV3 (Rounding UP): Uses σ=⌈a/i⌉. Aave prioritizes the recipient, ensuring they get at least the requested amount.
  • Lido stETH (Rounding DOWN): Uses σ=⌊a/i⌋. Lido prioritizes the sender, ensuring they never spend more than requested.

For both protocols, the maximum deviation turns out to be ⌈i⌉ weis: a larger index amplifies the rounding error. The index of Lido stETH, for example, is around 1.23 as of this writing, which implies a maximum deviation of 2 weis: this is a known behaviour, and is described formally in the relevant GitHub issue.

  • Takeaway: Balance differences are not necessarily equal to the transferred amount; the rounding direction matters, as it determines which side of the transfer is "favoured".

2. The 1-Wei Flutter

If we consider balances as decimal numbers with full precision, we see that sender and receiver may well have initial balances with different fractional parts. Then, when applying the same delta to both, calculated as σ⋅i (also a decimal), one of the balances might cross an integer threshold thanks to the fractional part of σ⋅i, while the other might not. Therefore, when finally rounding down, we might see that the two (rounded, integer) balances have not changed by the same quantity: one delta might be bigger than the other by one wei.

Our probabilistic analysis (confirmed by fuzz tests) shows that, in both protocols, the sum of balances stays unchanged roughly 66% of the time, whereas 33% of the time it changes by ±1 wei.

  • Best Practice: Do not rely on sender's and receiver's balance differences being equal. Allow for a 1-wei tolerance.

3. The "Ghost" Share (Lido Dust)

Lido's downward rounding creates a "dust" problem. When a contract attempts to transfer its entire stETH balance b, the rounding logic σ=⌊b/i⌋ typically results in only s−1 shares being moved, where s is the user's total share count.

  • The Result: The contract successfully sends the token balance but is left holding 1 share of dust.
  • Integration Impact: If your vault logic requires a zero-balance state to close a position or delete a mapping, this 1-wei "ghost" share will prevent the state from clearing. In this case, it is recommended to move shares directly using the transferShares() function to bypass the rounding logic.

4. The "Split-Transfer" Revert (Aave Risk)

This is perhaps the most dangerous pitfall for developers. Imagine a contract that calculates its total aToken balance and tries to send 5% to a treasury and 95% to a user.

Because Aave rounds up the shares required for each individual transfer, this can accumulate positive rounding errors and eventually revert on the last transfer:

In roughly 66% of cases, the sum of these rounded-up shares will exceed the contract's total share balance. The second transfer will revert with "Insufficient Balance," even if your balance-tracking logic says you have enough.

The Aave v3.5 Upgrade: Why the Maths Changed

It is worth noting that Aave’s current behavior is relatively new. Prior to the Aave v3.5 upgrade (August 2025), aToken transfers used "half-up" (banker's) rounding.

The 3.5 upgrade was executed via Aave's governance system, effectively changing the underlying math for thousands of existing integrations "under their feet."

  • The Good: This upgrade moved Aave toward the ERC-4626 philosophy of explicit, directional rounding. You now know for a fact that a recipient will never get less than they were sent.
  • The Risk: By choosing a side, the protocol creates a permanent "rounding bias" that accumulates. As we saw in Quirk #4, always rounding shares up means that a series of small transfers will often cost more than a single large one.

Conclusion: Mastering Rebasing Integrations

Rebasing tokens are a powerful tool for UX, but they break the "integer-exact" mental model of standard ERC20 tokens. When building integrations, keep these four fundamental deviations in mind:

  1. Deltas are not Amounts: Expect that balanceOf(user) will change by slightly more (Aave) or slightly less (Lido) than the amount passed to the transfer function.
  2. Accounting is not Zero-Sum: The global sum of all user balances is not a constant. It "flutters" by 1 wei as rounding errors shift between accounts. Use approximate equality in your unit tests.
  3. Emptying a Balance is not Straightforward: In Lido, transferring your "max balance" usually leaves 1 share behind. If your contract logic depends on a 0-balance state (e.g., to close a vault), you must explicitly sweep the remaining shares.
  4. Splits are Dangerous: In Aave, the "Rounding Up" bias makes splitting a balance into multiple transfers a high-risk operation. The final transfer will likely revert because the previous hops "over-consumed" the underlying shares.

The Golden Rule: The safest path is to perform internal accounting in shares rather than token amounts. Shares are the only source of truth in the contract's storage; the token balance is just a moving, rounded projection of that truth.