MCAI · Missing evidence
Missing chain state
The reference repair graded with half of the recorded chain state removed.
Recorded chain state is missing, so nothing can be established. The replay refuses the missing requests instead of guessing.
This fix is one of the five in the recorded run on the home page.
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 11 ETH- –The DeFiHackLabs reproduction of the attack: not establishedIt fails with a declared reason, but on incomplete chain state, so the result is not evidence.test/poc.t.sol:MCAI_PoC:testExploit()ERC20: transfer amount exceeds allowance
Security obligations
not established- –The tax wallet cannot move an ordinary holder’s tokens without approvalThe bug stated for every holder, not just the pool the exploit drained.test/hidden/security.t.sol:MCAI_Security:test_taxwallet_cannot_move_holder_tokens_without_approval()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 78543158553598686148638835834949107218613625935040866044242186441045927734796: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0xada5da3403a647afe8ac6178adca33
- –The tax wallet cannot move the pool’s tokens without approvalChecked on the balance change itself, so a fix that breaks the allowance view cannot game it.test/hidden/security.t.sol:MCAI_Security:test_taxwallet_cannot_move_pair_tokens_without_approval()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 64989051864140497098367551748748378792513452267687878274729726101262200356207: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0x8fae7ff504a4aa7fa0cff40a5843b2
- –With approval, the tax wallet’s allowance is debited by exactly what it movesThe original moved the tokens and debited nothing.test/hidden/security.t.sol:MCAI_Security:test_taxwallet_transferFrom_debits_allowance()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 24029004644379172903573199032907279622822360070542495345659692722274166502703: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0x351fef4637e11852f19f90d04aa41a
- –No one can burn a holder’s tokens to the dead wallet without approvalThe second branch of the same broken helper. A fix aimed only at the tax wallet leaves it open.test/hidden/security.t.sol:MCAI_Security:test_unapproved_spender_cannot_burn_to_dead_wallet()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 77041382601556914856323004842713349145705265199234382265622304951109847295127: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0xaa53e09608157f4979af27cf9d8807
Legitimate use · behaviour
not established- –Buying through the pool the incident used still worksIncludes the token’s fee path.test/hidden/regression.t.sol:MCAI_Regression:test_amm_buy_works()not established · vm.deal: failed to get account for 0x0000000000000000000000000000000000b0bb1E: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getBalance ["0x0000000000000000000000000000000000b0bb1e", "0x14b6d3c"]
- –Approve, then transferFrom, works and debits the allowance exactlyA fix that disables transferFrom, or never debits, fails here.test/hidden/regression.t.sol:MCAI_Regression:test_approved_transferFrom_works_and_debits_allowance()not established · vm.prank: failed to get account for 0x00000000000000000000000000000000000117E4: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getBalance ["0x00000000000000000000000000000000000117e4", "0x14b6d3c"]
- ✓Token metadata and the pool’s balance are intactDecimals still 9, supply and symbol present, the pool still holds MCAI.test/hidden/regression.t.sol:MCAI_Regression:test_metadata_preserved()
- –A holder-to-holder transfer workstest/hidden/regression.t.sol:MCAI_Regression:test_normal_transfer_works()not established · vm.prank: failed to get account for 0x00000000000000000000000000000000000A11cE: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getBalance ["0x00000000000000000000000000000000000a11ce", "0x14b6d3c"]
- –With approval, the tax wallet can still spend like anyone elseA fix cannot close the hole by blocking the tax wallet forever.test/hidden/regression.t.sol:MCAI_Regression:test_taxwallet_transferFrom_with_approval_moves_tokens()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 23028605044465907330590143408402628608607601776866354202691344135048377340262: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0x32e9ba82ba1265c9db926e9f9e962d
- –transferFrom takes exactly the amount out of the senderCatches a fix that quietly credits the sender back: an unlimited mint.test/hidden/regression.t.sol:MCAI_Regression:test_transferFrom_debits_the_sender_exactly()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 67636838853495343560857180560514193218047062097941764749123491324338045849287: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0x9589181ae2674f1203ccaa7dd8d502
- –A spender with no approval still cannot move tokensA fix that removes the allowance check altogether fails here.test/hidden/regression.t.sol:MCAI_Regression:test_unapproved_spender_cannot_move_tokens()not established · EVM error; database error: failed to get storage for 0x810B5902CB2ac2Fa63dFE4A6935EA32aED975cc8 at 92737304782526971453854896467636928138423086763491661016762623338135157954320: server returned an error response: error code -32000: OFFLINE: unrecorded eth_getStorageAt ["0x810b5902cb2ac2fa63dfe4a6935ea32aed975cc8", "0xcd077454f9a8b6b2a4a301928260d8
Legitimate use · interface
not established- –Original functions still answerCalls the original functions and requires an answer other than “no such function”.test/hidden/invariants_auto.t.sol:AutoInvariants:test_abi_selectors_dispatch()setUp failed: state missing (unrecorded eth_getStorageAt)
- –Every original function is still in the dispatch tableWalks the patched bytecode and requires each original selector in the dispatcher.test/hidden/invariants_auto.t.sol:AutoInvariants:test_abi_selectors_preserved()setUp failed: state missing (unrecorded eth_getStorageAt)
- –The patched contract has codetest/hidden/invariants_auto.t.sol:AutoInvariants:test_contract_has_code()setUp failed: state missing (unrecorded eth_getStorageAt)
- –Guard: the selector check can say noAn impossible selector must be reported absent, or the check above proves nothing.test/hidden/invariants_auto.t.sol:AutoInvariants:test_selector_check_is_not_vacuous()setUp failed: state missing (unrecorded eth_getStorageAt)
- –Guard: unknown calls are still rejectedWithout this, a catch-all fallback would make the dispatch probe meaningless.test/hidden/invariants_auto.t.sol:AutoInvariants:test_unknown_selector_is_rejected()setUp failed: state missing (unrecorded eth_getStorageAt)
The patch
against the original source@@ -255,8 +255,14 @@255255 address owner,256256 address spender,257257 uint256 amount258− ) private view returns (uint256) {259− return msg.sender != _taxWallet && (owner == _uniswapPair || spender != _deadWallet) ? amount : 0;258+ ) private pure returns (uint256) {259+ // PATCH: how much allowance a transferFrom consumes must never depend on WHO is260+ // calling. The original returned 0 whenever msg.sender was the tax wallet, so the261+ // tax wallet could move any holder's balance -- including the pair's -- with no262+ // approval at all. Every spender is now debited the full amount they moved.263+ owner;264+ spender;265+ return amount;260266 }261267262268 function transferFrom(
Re-run this grade
offline · same inputsNeeds 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.
$ git clone https://github.com/FarseenSh/evmpatch-env.git && cd evmpatch-env
$ git checkout 165c0ed
# built by the task's control runner, which regrades every control
$ uv run --no-project --python 3.12 python worked_example/mcai_2025_01/controls/run_controls.py
$ python -c "import json; g = json.load(open('worked_example/mcai_2025_01/controls/unrecorded_rpc_truncated_state/grade_canonical.json')); print(g['outcome'], g['reason'])"Expected: inconclusive unrecorded_rpc. This control’s hashes can differ between runs, because the partial evidence depends on which missing request each check hits first.
To check a downloaded grade file instead: shasum -a 256 grade.strict.json prints the strict hash.
Control note
from the repositoryTask copy whose frozen rpc_log.json keeps only 67 of 134 recorded responses; the reference fix is graded against it. The replay proxy fails closed on the missing calls. Expected: inconclusive with a typed infrastructure reason (unrecorded_rpc / setup_failed), score 0, distinguishable from not_solved.
unrecorded_rpc, never credited.