When a function call fails mid-function, the contract may continue execution without rolling back previous changes, leading to partial, unintended state changes.
Game
Imagine a contract that performs a series of critical operations, one of which involves an external call to another contract. What is missing here?
// SPDX-License-Identifier: MIT
// Open me in VSCode and really think before opening the hints!
// Add @audit tags wherever suspicious
// Go to the solidity docs to complete missing knowledge of what's happening here
// Solve by drafting a fix!
pragma solidity ^0.8.0;
interface IExternalContract {
function doAction() external returns (bool);
}
contract PartialExecutionGame {
IExternalContract public externalContract;
uint256 public balance;
constructor(address _externalContract) {
externalContract = IExternalContract(_externalContract);
}
// Function that performs a sequence of operations, including an unchecked external call
function executeSequence(uint256 amount) public {
balance += amount; // Step 1: Increase balance
bool success = externalContract.doAction(); // Step 2: External call
if (!success) {
balance -= amount; // Attempt to revert manually, but this is partial
}
}
}
Consider what would happen if doAction fails.
Is the balance effectively rolled back, and does the contract return to a fully consistent state?
A more reliable approach might involve enforcing that all steps succeed or none at all, using a mechanism to revert the entire transaction on failure.
function executeSequence(uint256 amount) public {
balance += amount; // Step 1: Increase balance
bool success = externalContract.doAction();
require(success, "External action failed"); // Fix: Revert the entire transaction on failure
}