Web3 Underlying Language (II): How Does Move Avoid Flash Loan Reentrancy Attacks?
Author: Song Jiajie, Ren Heyi, Guosheng Securities Research Institute
The previous report compared the advantages and characteristics of Move and Solidity (Ethereum) from the perspective of underlying language features. As a foundational research in Web3, this article analyzes how Ethereum and Move implement flash loans from the perspective of this distinctive application, and how Move avoids flash loan attacks.
The interaction between Ethereum contracts is achieved through message passing to maintain state consistency, allowing reentrancy and dynamic calls, which provides a foundation for implementing flash loans. During this process, functions between contracts can call each other back and forth—control is transferred during the call. If there are vulnerabilities in the DeFi project platform contract, arbitrageurs can exploit the malicious code in its contract to call the corresponding functions for asset theft—by utilizing the information discrepancy in state synchronization between contracts, they can double-spend assets (Ethereum assets are merely assigned values) before a process is completed, or repeatedly execute functions (which should not be allowed) to steal.
The premise of reentrancy attacks is that the contract deployed by the attacker contains malicious code, but the core factors are:
1) There is a transfer of control during Ethereum contract calls, which gives malicious contracts the initiative;
2) The process can be reentered (repeatedly called) before it ends, allowing malicious contracts to exploit vulnerabilities to repeatedly call functions for asset theft (e.g., repeatedly withdrawing funds while a withdrawal process is not yet complete);
3) Additionally, since Ethereum account assets exist in the form of numerical balances, there is a possibility of repeatedly stealing assets (double spending), or malicious contracts can modify the relevant account balance values to achieve theft before the process ends.
Move proposes a new operational process for flash loans—the hot-potato model, fundamentally discarding reentrancy. The "hot-potato" model is a structure that lacks key, store, copy, and drop capabilities, and is used only once during the execution of a Move program. Due to the absence of drop, key, or store capabilities, the hot potato can only be concluded by calling the "destroy" function—just as its name suggests, it is a hot potato, and any handling during the process is "hot," and can only be concluded by the destroy function. The specific process is as follows:
1) The flash loan smart contract creates a "hot potato" receipt while working;
2) When the arbitrageur borrows from the flash loan contract, the flash loan contract sends the loan funds and a hot potato receipt;
3) The arbitrageur uses the loan funds for arbitrage operations;
4) When the arbitrageur repays, they call the repay function, sending the funds and receipt to the repay function, which destroys the receipt after receiving it.
The completion of the flash loan relies on the correct repayment and the conclusion of the process. Before the process ends, malicious contracts can implement repeated calls in the Ethereum system, modifying the corresponding asset account values to achieve theft. In the Move system, the conclusion of the flash loan process requires not only the correct return of funds but also the one-time recovery and destruction of the hot potato resource, ensuring the atomicity of the flash loan.
1: Core Viewpoints
As the most distinctive application in the Ethereum DeFi ecosystem, flash loans are fundamentally based on the Solidity language used by Ethereum, which allows dynamic calls and reentrancy. Although this dynamic calling is an important manifestation of the openness and composability of smart contracts, the transfer of control and repeated calls to contract functions pose significant security risks, leading to incidents in the industry where flash loans exploit vulnerabilities in DeFi liquidity pools. Since the Move language does not allow dynamic calls and reentrancy, it implements flash loans simply through a "hot potato" model, thus completely avoiding the security issues seen in Ethereum.
The operational modes of the two systems in ecological applications have significant differences, rooted in the characteristics of the underlying Move language, which we have detailed in the previous report "Web3 Underlying Language: What Shortcomings Does Move Address in Solidity?" This report compares the different ways the two systems implement applications from the perspective of flash loans.
2: The Foundation of Ethereum Flash Loans: Dynamic Calls and Reentrancy
Flash loans are a native DeFi innovation, understood as instant loans. Users only need to complete borrowing, arbitrage, repayment, and pay a fee within the same transaction (block). Since it is an atomic transaction, borrowers do not need to collateralize any assets, providing a no-capital arbitrage solution.
As mentioned in our previous report "Web3 Underlying Language: What Shortcomings Does Move Address in Solidity?":
"For modularity and contract composability, contracts on Solidity (like Ethereum) use libraries (equivalent to static libraries) to pass messages, enabling calls and interactions between contracts. In contrast, the Move language employs modules and scripts, with the former resembling contract contracts, and the composability of Move contracts being a combination of modules by passing resources (i.e., the resources mentioned earlier). The differences in composability between Solidity and Move are very clear."
The interaction between Ethereum contracts is achieved through message passing to maintain state consistency, allowing reentrancy and dynamic calls, meaning that functions between contracts can call each other back and forth—control is transferred during the call. This characteristic provides a foundation for implementing flash loans.
Specifically, in an Ethereum transaction, transfer operations and a series of contract operations can be performed by calling the functional functions within the smart contract, executing multiple complex functions—meaning that a transaction based on Ethereum can integrate a series of complex operations: borrowing, arbitraging, repaying, and so on.
All operations in a flash loan are completed within a single block time. Given the current Ethereum block generation speed, after the Merge, this is about 12 seconds—however, the core is not the 12 seconds, but that this series of transactions must ultimately be profitable and repayable. If this is not achieved, the transaction will not be packed into a block, meaning that the borrower's borrowing and arbitrage (if failed) operations are not valid transactions, but merely temporary states—i.e., atomic transactions. Therefore, users must program all the steps to form a smart contract transaction and complete the three steps of borrowing, using, and repaying.
The complex transaction operations mentioned above are realized through dynamic calls between several contracts, and this calling is reentrant (i.e., can be called repeatedly).
Currently, users can also use third-party projects like FURUCOMBO to complete simpler plug-in programming for flash loans without actually writing code to design smart contracts, ultimately achieving the full set of operations required for flash loans. The specific arbitrage process is illustrated in the following diagram (using the FURUCOMBO platform, with specific exchange rates as examples). Currently, the price situation on the Kyberswap platform is 1 sUSD = 0.9927 DAI, while on Uniswap, 1 DAI = 1.2411 sUSD. Users find that there is a significant arbitrage opportunity in the DAI-sUSD trading pair prices on these two platforms and can design the arbitrage process through the FURUCOMBO interface.
Including: 1. Borrowing 100 DAI from the flash loan function of the AAVE lending platform; 2. Exchanging 100 DAI for about 122 sUSD tokens through Uniswap; 3. Exchanging sUSD tokens for about 122 DAI tokens through the Kyberswap platform; 4. Repaying the 100 DAI borrowed from AAVE and a fee of 0.09 DAI; 5. The entire arbitrage process using flash loans is completed within a single Ethereum transaction, yielding a profit of about 22 DAI.
If the funds borrowed in this Ethereum transaction are not repaid, the entire borrowing transaction will not be packed into a block, meaning that the borrowing did not actually occur, so the lender's funds will not be affected— the intermediate borrowing and arbitrage processes are merely temporary states and have not been confirmed by miners.
Based on the characteristics and time requirements of flash loans, their most widespread application is arbitrage trading. Arbitrageurs do not need to use their own assets for arbitrage operations; they only need to obtain the required amount of funds through flash loans to complete the arbitrage transaction and repay the borrowed funds in a timely manner. This greatly lowers the entry threshold for arbitrageurs, as theoretically anyone can become an arbitrageur and operate with unlimited arbitrage funds.
From the above process, it can be seen that the control of Ethereum contracts switches between the FURUCOMBO flash contract-arbitrageur account contract-Uniswap contract-Kyberswap contract-flash loan contract, and can dynamically call the corresponding functions. If there are vulnerabilities in the DeFi project platform contract, arbitrageurs can exploit the malicious code in its contract to call the corresponding functions for asset theft—by utilizing the information discrepancy in state synchronization between contracts, they can double-spend assets (Ethereum assets are merely assigned values) or repeatedly execute functions (which should not be allowed) to steal.
For the Move ecosystem, since assets are not simply assigned values and dynamic calls and reentrancy are prohibited, risks are fundamentally eliminated. We will elaborate on the specific implementation later.
3: What Are the Specific Differences Between Move and Solidity Flash Loan Implementations?
3.1. Ethereum Flash Loans: A Double-Edged Sword of Dynamic Calls and Reentrancy
From the implementation perspective, the Ethereum EVM (based on Solidity) has dynamic scheduling and can implement flash loans through reentrancy. As mentioned in our previous report "Web3 Underlying Language: What Shortcomings Does Move Address in Solidity?":
"Assets in Ethereum (Solidity) are controlled by the corresponding contracts. If we compare the Token A contract to a safe, the safe assigns a numerical balance to all users to express the quantity of Token A assets they own, but the assets themselves are still stored in the Token A contract's safe. In contrast, a Move user account is a separate large safe controlled by the user, with all Token assets stored in this safe. Moreover, these tokens do not exist in numerical form but as non-copyable, user-controlled resources (types)."
Thus, the process of implementing flash loan arbitrage in Ethereum EVM is:
1) The user hands over control to the flash loan contract;
2) The flash loan contract calls the execution function from the external arbitrage contract program, sending the requested loan amount to the arbitrage contract, which performs the arbitrage operation;
3) The arbitrage contract completes the arbitrage and returns the loan to the flash loan contract, completing the execution function and returning control to the flash loan contract;
4) The flash loan contract checks whether the repayment amount is correct; if correct, the arbitrage transaction is successful; otherwise, it fails.
In this process, the corresponding functions are dynamically called, and the flash loan contract must check whether the repayment amount is correct before concluding, so the transfer of control can occur at any time, which is to say it is reentrant—at the same time, it is important to note that Ethereum account assets exist in the form of numerical balances, and reentrancy can lead to double spending, which is where vulnerabilities arise. One of the main dangers of calling external contracts is that they can take over control, and if these external contract programs have vulnerabilities, attackers can exploit them through repeated calls, leading to reentrancy attacks.
Reentrancy attacks have led to one of the most severe attacks in Ethereum's history, directly resulting in the Ethereum fork, namely the DAO collapse event on June 17, 2016. The hacker deployed a contract, acting as an "investor," storing some ETH in the DAO. The hacker then called the withdraw function in the DAO contract, causing the DAO contract to send funds to the hacker. Due to the malicious vulnerability in the hacker's contract (fallback function)—which lacked an ending logic—the DAO was unable to complete the withdrawal (at this point, the DAO was unaware that the hacker had received the withdrawal and had reduced their account balance to zero) and regain control, allowing the hacker to repeatedly call the withdraw function to send more ETH than their initial deposit.
In flash loan attack incidents, attackers often implement reentrancy attacks through malicious contracts. For example, on March 16, 2022, a hacker borrowed through a flash loan, exploiting a vulnerability in the lending project Hundred Finance to execute a real-time reentrancy attack, ultimately profiting about 2363 ETH. The specific process is not complicated; since Hundred Finance transfers before recording, the hacker borrowed through a flash loan and used the attack contract to deposit into the Hundred Finance borrowing pool to achieve collateralized loans.
The onTokenTransfer function deployed by the attack contract repeatedly calls the borrowing function (reentrancy attack) before recording, extracting loans from different borrowing pools with a single collateral asset. Since it is a transfer before recording, when the recording occurs (process ends), the hacker has already executed the attack and profited. The core of the attack is: before the process ends, the attacker’s contract can repeatedly call relevant functions to achieve asset theft.
It is akin to the attacker’s contract pledging assets to Bank A (Project A's borrowing pool) for a loan, while Bank A begins disbursing funds before completing the accounting settlement. The completion of the accounting settlement process by Bank A signifies the success of the flash loan transaction (atomicity), at which point the assets are recorded in Bank A.
However, due to the asynchronous information between Bank A and other banks (the state is not synchronized between contract functions), the attacker can re-pledge the same assets for loans at Banks B, C, etc., while the asset recording at Bank A has not been completed (i.e., the loan process of the attacked contract has not ended, and the flash loan workflow is still ongoing, allowing contract function calls to continue), and by the time the process ends, the attacker has completed the flash loan attack and profited.
The premise of reentrancy attacks is that the contract deployed by the attacker contains malicious code, but the core factors are:
1) There is a transfer of control during Ethereum contract calls, which gives malicious contracts the initiative;
2) The process can be reentered (repeatedly called) before it ends, allowing malicious contracts to exploit vulnerabilities to repeatedly call functions for asset theft (e.g., repeatedly withdrawing funds while a withdrawal process is not yet complete);
3) Additionally, since Ethereum account assets exist in the form of numerical balances, there is a possibility of repeatedly stealing assets (double spending), or malicious contracts can modify the relevant account balance values to achieve theft before the process ends.
Reentrancy is the foundation for implementing flash loans, but once the target contract has vulnerabilities, attackers can execute reentrancy attacks. This has been detailed in our previous report.
3.2. MOVE's "Hot Potato": Flash Loans Without Reentrancy
The Move language prohibits dynamic calls and reentrancy, fundamentally eliminating the risk of reentrancy attacks. However, since assets in the Move system are treated as resource types, once borrowed, it is equivalent to a real transfer occurring. How can we ensure the smooth repayment of flash loans?
Move proposes a new operational process for flash loans—the hot-potato model, fundamentally discarding reentrancy. The "hot-potato" model is a structure that lacks key, store, copy, and drop capabilities, and is used only once during the execution of a Move program. Due to the absence of drop, key, or store capabilities, the hot potato can only be concluded by calling the "destroy" function—just as its name suggests, it is a hot potato, and any handling during the process is "hot," and can only be concluded by the destroy function. The specific process is as follows:
1) The flash loan smart contract creates a "hot potato" receipt while working;
2) When the arbitrageur borrows from the flash loan contract, the flash loan contract sends the loan funds and a hot potato receipt;
3) The arbitrageur uses the loan funds for arbitrage operations;
4) When the arbitrageur repays, they call the repay function, sending the funds and receipt to the repay function, which destroys the receipt after receiving it.
As analyzed in our previous report, both account assets and receipts are resource types that "cannot be copied, discarded, or reused, and can be safely stored and transferred." Therefore, the receipt must be processed (and can only be used once), rather than simply assigning numerical values to accounts as in Ethereum.
Thus, the hot potato model ensures that the borrowed assets (resource types, which represent a real transfer once borrowed) must be returned. The receipt, as a hot potato, is akin to a timed detonator (the timing here refers to the "time" of the flash loan arbitrage as an atomic transaction, not a specific duration), with the funds and detonator bound together when borrowed, and neither party can keep the detonator; it must be returned to its original place to be disarmed—otherwise, the transaction cannot be completed, ensuring that the flash loan funds will be returned.
The completion of the flash loan relies on the correct repayment and the conclusion of the process. Before the process ends, malicious contracts can implement repeated calls in the Ethereum system, modifying the corresponding asset account values to achieve theft. In the Move system, the conclusion of the flash loan process requires not only the correct return of funds but also the one-time recovery and destruction of the hot potato resource, ensuring the atomicity of the flash loan.
From an application perspective, the underlying representation of Web3 must enhance code security while ensuring openness and reconfigurability. In Web3, code not only contains information but also directly involves asset calls, making the security of user assets paramount; otherwise, Web3 will become a dark forest. The Ethereum ecosystem has shown the vitality of smart contracts, and the next era will continue to evolve towards security and compliance based on this foundation. This is also the core logic of our current focus on the evolution of Web3 underlying languages, which may give rise to the next wave of innovation.