Use Case

One of the most common things that happen when a protocol is exploited is that the attacker drains all the tokens from the protocol. Having assertions in place that prevent draining can be helpful to prevent or at the very least slow down the rate at which a protocol is drained.

Explanation

Check that the token balance of a contract does not decrease by more than X% in a single transaction. Each individual protocol should define their own percentage based on user patterns and expected behavior.

Note: This does not prevent draining from happening, as a somewhat capable attacker would just drain X-1% of the tokens in a single transaction. It’s better than nothing and if mitigation is in place it could pause the protocol on a draining attempt.

Code Example

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {Assertion} from "../../lib/credible-std/src/Assertion.sol";

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
}

interface IExampleContract {}

contract ERC20DrainAssertion is Assertion {
    IERC20 public erc20 = IERC20(address(0xbeef));
    IExampleContract public example = IExampleContract(address(0xf00));

    function fnSelectors() external pure override returns (bytes4[] memory assertions) {
        assertions = new bytes4[](1); // Define the number of triggers
        assertions[0] = this.assertionERC20Drain.selector; // Define the trigger
    }

    // Don't allow for more than x% of the total balance to be transferred out in a single transaction
    // revert if the assertion fails
    function assertionERC20Drain() external {
        ph.forkPreState();
        uint256 preBalance = erc20.balanceOf(address(example));
        ph.forkPostState();
        uint256 postBalance = erc20.balanceOf(address(example));
        if (preBalance > postBalance) {
            uint256 drainAmount = preBalance - postBalance;
            uint256 tenPercentOfPreBalance = preBalance / 10; // Change according to the percentage you want to allow
            require(drainAmount <= tenPercentOfPreBalance, "Drain amount is greater than 10% of the pre-balance");
        }
    }
}