Bypassing the contract check

One way of differentiating an EOA from a contract address is to check if there has been additional bytecode size due to the contract being stored at the address using the `extcodesize` method. As for an EOA, the bytecode size will return zero. The extcodesize method is a built-in opcode in the Ethereum Virtual Machine (EVM) that allows smart contracts to retrieve the bytecode size at a specified address.

In order to restrict certain contracts/ functions to only EOA(s), this method might be seen as a way to. But it is invariably flawed as it can be bypassed for malicious intent.

The `extcodesize` Method & The Constructor Bypass

When an external call is made from a contract's constructor, the `extcodesize` opcode of the smart contract being deployed will indeed return zero during the constructor's execution. This is because the runtime code of the contract, which is what `extcodesize` checks, has not yet been deployed to the blockchain. At this stage, only the deployment code (also known as creation code or constructor code) is executed.

As a result, any attempt to verify the existence of code at the target address using extcodesize will return zero. Therefore, an attacker can use this behavior to make a malicious external call from a constructor.

In the constructor of the Attacker contract, it receives an instance of the Victim contract as an argument. Upon deployment, the constructor of the Attacker contract immediately calls the supposedToBeProtected function of the Victim contract.

Using require(msg.sender == tx.origin, “”)

The `require(msg.sender == tx.origin, “Only EOA allowed"); is a common approach for ensuring that only EOAs interact with a contract.

Since tx.origin defines the wallet address that initiated the transaction, while msg.sender is the address that called the smart contract; when an EOA interacts directly with a smart contract, the msg.sender and the tx.origin will be the user’s wallet address. Hence the required condition will be met. Else the error will be fired.

It is important to note that this method poses a great challenge with contracts implementing ERC-4337 and also, it cannot check for an arbitrary address.

Finally, above the essence of these checks, relying on the distinction between contract and non-contract addresses can be risky and may break composability in certain cases, especially in decentralized finance (DeFi) applications where smart contracts often interact with each other.