Rekt Retweet #11: Re-entrancy — Why the $11m Agave and Hundred Finance hacks could NEVER happen on Radix | The Radix Blog | Radix DLT
July 5, 2022
TLDR: The EVM requires complex stacks of messages sent between contracts, including re-entering previously called contracts, allowing for exploits in the ordering of logic. On Radix, re-entrancy is disabled, and Scrypto provides protections that forces correct ordering.
Agave DAO and Hundred Finance are forks of Aave and Compound, respectively, on the Gnosis (xDai) network. Like many “alt layer 1s”, Gnosis leverages the Ethereum Virtual Machine (EVM). These dApps allow users to lend tokens to receive interest, or borrow tokens and pay interest (fully backed by collateral).
To borrow, users first deposit collateral, such as Wrapped ETH ($wETH), into the dApp’s wETH account. With that collateral, tokens can be borrowed (such as the stablecoin $xDAI), so long as the value of the borrowed amount is lower than the collateral.
To prevent the collateral being reused for more borrowing, the dApp records this in its “debt balance”.
On March 15, 2022, both Agave DAO and Hundred Finance were exploited within minutes.
For Agave, the hacker created their own custom smart contract, deposited collateral, borrowed funds, and re-entered the contract’s execution before it could update its debt balance.
This allowed the hacker to re-use the same collateral and borrow against it multiple times.
The exploit boils down to three issues:
1. The Agave contract did not follow the “checks-effects-interactions” pattern, as the borrowed funds were released prior to the debt balance being updated.
2. The re-entrant call from XDAI back to the Hacker’s Contract was only possible because the XDAI contract allowed the “callAfterTransfer” function.
3. And at the root of it all, re-entrancy is mandatory for the EVM to work because tx’s can only call a single smart contract. Functionality across contracts requires developers to string complex chains of calls to one another that sometimes re-enter previous contracts.
But if contracts can be re-entered, exploits can happen if there is a single mistake in the ordering of logic, e.g. the check-effect pattern not being followed.
And it gets even more complicated when each and every token is its own smart contract, with its own logic that can contain mistakes, such as the xDAI token allowing callAfterTransfer.
So how does developing in Scrypto on Radix’s upcoming Babylon release solve this?
Re-entrancy is disabled, and the checks-effects-interactions pattern is enforced by Scrypto.
To use “Agave” on Radix, a user would construct a transaction that calls their own user account and the Agave smart contract component.
The transaction interacts directly with each component, specifying “physical” movements of tokens between vaults inside components.
With the EVM requiring a chain of calls between contracts to update their internal lists of balances, re-entrancy is necessary if a callback is required.
On Radix, each component interacts directly with the transaction. There is no chain of calls, so re-entrancy is disabled!
Furthermore Scrypto, Radix’s asset-oriented programming language, enforces the “checks-effects-interactions”pattern on developers, as “buckets” of tokens are returned only at the end of a function or method. Scrypto devs can’t make the mistake that led to this hack.
If the hack were attempted on “Agave” on Radix, this would fail immediately, as re-entrancy is not possible, and the checks-effects-interactions pattern is enforced by Scrypto.
If you’d like to learn more about Scrypto, why not check out our docs site to get started:
For the last in the Rekt Retweet series:
Rekt Retweet #10: Badges — Why the $326m Wormhole hack on #Solana could NEVER happen on Radix