Bitallx · Missing evidence

Syntax error

The reference repair with a syntax error injected.

Inconclusive

The patched contract does not compile, so no test could run. A build failure is never read as a blocked exploit.

Exploitnot runhistorical attack
Securitynot run
Legitimate usenot run
Canariesnonetamper and breakage flags
no resultNothing ran: the contract does not compile. No check, exploit-only or otherwise, can credit it.
The checks

What ran, and what it said.

Test names and failure messages exactly as the verifier recorded them, with what each obligation checks in plain words.

Exploit

1 replay · profit over 2,000 USDT
  • The DeFiHackLabs reproduction of the attack: not runThe contract did not compile.test/poc.t.sol:Bitallx_PoC:testExploit()

Security obligations

not run
  • A batch whose total exceeds what was funded pays nothingtest/hidden/security.t.sol:Bitallx_Security:test_multi_element_over_request_pays_nothing()
  • The incident’s own shape pays nothingtest/hidden/security.t.sol:Bitallx_Security:test_single_element_over_request_from_fresh_payer_pays_nothing()
  • The sum is bounded, not just each elementtest/hidden/security.t.sol:Bitallx_Security:test_sum_over_funded_total_rejected_even_when_each_element_fits()

Legitimate use · behaviour

not run
  • A funded batch payout pays everyone correctlytest/hidden/regression.t.sol:Bitallx_Regression:test_funded_batch_payout_distributes_correctly()
  • The patched code still reads the contract’s live storagetest/hidden/regression.t.sol:Bitallx_Regression:test_live_storage_still_readable()
  • The owner’s treasury sweep works, and only for the ownertest/hidden/regression.t.sol:Bitallx_Regression:test_owner_treasury_path_still_works()
  • An unfunded payer is refusedtest/hidden/regression.t.sol:Bitallx_Regression:test_payout_rejects_an_unfunded_payer()
  • The publisher can still pay a reward, within its limitstest/hidden/regression.t.sol:Bitallx_Regression:test_publisher_can_still_pay_a_reward()

Legitimate use · interface

not run
  • Original functions still answertest/hidden/invariants_auto.t.sol:AutoInvariants:test_abi_selectors_dispatch()
  • Every original function is still in the dispatch tabletest/hidden/invariants_auto.t.sol:AutoInvariants:test_abi_selectors_preserved()
  • The patched contract has codetest/hidden/invariants_auto.t.sol:AutoInvariants:test_contract_has_code()
  • Guard: the selector check can say notest/hidden/invariants_auto.t.sol:AutoInvariants:test_selector_check_is_not_vacuous()
  • Guard: unknown calls are still rejectedtest/hidden/invariants_auto.t.sol:AutoInvariants:test_unknown_selector_is_rejected()

The patch

against the original source
src/contracts/Token.sol+14 −1
@@ -91,7 +91,19 @@9191    uint256 totalSendAmount9292    ) external {9393        require(wallet.length == amount.length, "The length of 2 arrays should be the same");94    94+95+        // PATCH: bound the payout by what was actually funded. Every check below -- the96+        // allowance, the sender's balance, and the transferFrom that pulls money IN -- is97+        // against `totalSendAmount`, while the loop below paid out the caller-supplied98+        // `amount[]` with nothing tying the two together. Calling this with99+        // totalSendAmount = 0 and amount[0] = the contract's own balance therefore passed100+        // every check and paid the caller the contract's treasury.101+        uint256 requested = 0;102+        for (uint256 i = 0; i < amount.length; i++) {103+            requested += amount[i];104+        }105+        require(requested == totalSendAmount, "Payout total does not match the funded amount");106+95107        uint256 allowance = IBEP20(tokencontract).allowance(msg.sender, address(this));96108        require(allowance >= totalSendAmount, "Insufficient token allowance");97109    @@ -107,6 +119,7 @@107119    }108120109121    //FUNCTION TO USER CLAIM REWARD122+    this line is not solidity;110123    function claimReward(address wallet, uint256 amount) public onlyPublisher  {111124        require(amount >= minimumClaimAmount, "Claim amount below minimum limit!");112125        require(amount <= maximumClaimAmount, "Claim amount exceeds maximum limit!");

Re-run this grade

offline · same inputs

Needs Foundry 1.7.1 with solc 0.8.30, 0.8.26 and 0.8.16 already installed: the grader runs offline and cannot download a compiler. Python 3.12 or later. Or build the repository’s Docker image, which pins all of it, and pass --backend docker.

Terminal
$ git clone https://github.com/FarseenSh/evmpatch-env.git && cd evmpatch-env
$ git checkout 165c0ed
$ python -m evmpatch_env.sandbox tasks/bitallx_2025_05 \
    --patch worked_example/bitallx_2025_05/controls/compile_failed/Token.sol \
    --backend local --sha256

Expected output: core 738590218e3547f507d00750ab23dbd503bc104884540beef00a325b0c971a8e and strict d0592c5c6fedf42bd2195a6a4b1c6d768a4b336d71103b7b8dc3f6a6f81aadb2.

To check a downloaded grade file instead: shasum -a 256 grade.strict.json prints the strict hash.

Control note

from the repository

Reference repair with a syntax error injected. A compile failure is never credited as a block.