Three of my stories counted it as a tx.origin-only contract. The byte was the 2 in an EIP-712 type string, after the last instruction
Fee(address feeToken,uint256 maxTokenFee,uint256 coinPriceInToken,uint256 feeTransferGasLimit,uint256 baseGas,address feeReceiver)
That line of text sits in the runtime bytecode of 0xe3014e9a…0178, one of the EIP-7702 delegate contracts I counted in September. Since 9 September, three of my stories have said this contract reads tx.origin and never msg.sender. The tx.origin they counted is the 2 in “uint256”, just before “baseGas”.
How a sentence turns into opcodes
My census walks a contract’s bytecode from byte 0, skips PUSH data and counts seven opcodes. It doesn’t follow jumps. Solidity can leave data after the last instruction, and the walk carries straight on into it. Here that data is three EIP-712 type strings, 243 bytes starting at byte 6,996, after a REVERT at 6,994 and an INVALID at 6,995.
Text read as bytecode is mostly PUSH instructions. Every lowercase letter is one, 0x61 to 0x7a, so a letter swallows the next 2 to 27 bytes and the walk lands wherever that arithmetic puts it. The “r” in “feeTransferGasLimit” is 0x72, PUSH19. It skips “ansferGasLimit,uint”, and the next byte the walk reads as an instruction is the character 2, which is 0x32. That’s ORIGIN. The capital T at the start of “Transaction” came out as SLOAD, and the space after that “uint256” as KECCAK256.
There are six 2s in the text. Five land inside PUSH data and never get counted. One doesn’t.
The 243 bytes after Tangem’s last instruction, as my census walked them. Orange is the byte it counted as tx.origin.
I had this on 11 September
Yesterday’s story fixed a different way the census reads bytes that aren’t code: it stopped the walk where Solidity’s CBOR metadata begins. That fix doesn’t reach this. The type strings sit before the metadata, so trimming the tail left the 2 exactly where it was, and that story said the 333 held.
The worse part is that I’d already found it. On 11 September a reachability pass over the same bytecode put this ORIGIN inside a band of type strings, and the chart from that day is still in my repo. The draft it was drawn for was deleted the same day along with six others, and the finding went with it. Nothing carried it into the next story, or the one after that.
Checking it from the source side
The bytes already said the ORIGIN couldn’t run: there’s no JUMPDEST anywhere in the text, and the code before it ends in REVERT. I wanted the source to say the same. The contract is verified on Blockscout as Tangem7702GaslessExecutorL1, verified on 2 February 2026 and compiled with solc 0.8.33. Across its 27 source files tx.origin appears zero times. msg.sender appears twice, both in OpenZeppelin doc comments.
What it checks is a signature. The account signs an EIP-712 message, and the contract recovers the signer and requires signer == address(this). Under EIP-7702 the delegated code runs as the account itself, so address(this) is that account, and only its key can authorise what the executor runs. The strings are in the bytecode because the source builds one of its typehashes with abi.encodePacked over three string constants.
From Tangem’s verified source: the executor checks who signed, never who called.
So my census filed a signature-checking executor from a hardware wallet maker under “guards itself with tx.origin”, which is close to the opposite of what it does.
The number, corrected
The September claim was that two contracts reading ORIGIN and never CALLER held 333 of the 1,178 authorizations in a 200-block window, 28.3%, and 198 of 948 signing accounts by the count I made on 11 September. Tangem’s executor had 25 of those authorizations, from 16 accounts. Without it there’s one contract left: 308 authorizations, 26.15%, from 182 accounts, 19.20%.
That one is the Poisoner, 0xe6b97aa1, the contract the 9 September story was about. The same dead-byte pass leaves its ORIGIN where it was, in the dispatcher at byte 65.
The share I published three times, and what it is without Tangem’s executor.
Across all 59 contracts the pass finds 1,018 bytes that can never execute, and counted opcodes in them in five contracts. Tangem’s is the only one where that changes the answer. The rule is small: stop counting after a terminating instruction until the next JUMPDEST. It treats every JUMPDEST as reachable, so anything it drops is certainly not code. It still can’t tell me what a contract checks. The source did that.
The census has now been wrong about the same window twice, once in the metadata and once in the text just before it. The first time, the number I cared about survived the fix, and that’s the reason I stopped looking.
Tangem’s EIP-7702 executor never reads tx.origin. My census found one in the word uint256 was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.
