Regarding collisions: Ethereum already is affected by potential address collisions by the CREATE2 opcode. It affects EOAs, and counterfactually deployed contracts (before deployment). The IMPERSONATECALL opcode only makes collisions slightly worse, because they could also affect deployed contracts.
But believe me that stealing from EOAs with an address collision is so bad already that I wouldn’t worry about the slight increase in attack surface.
You can read Vitalik’s proposal on increasing the address space here.
Regarding the contracts checking other contracts existence: I’m not aware that contracts check non-zero balance existence using the BALANCE opcode. Some contracts check if an address is a contract using EXTCODESIZE. If we don’t change anything in the EIP, those contracts will not detect correctly impersonated contracts.
We could change the EIP in several ways to solve this minor issue:
the first time the parent contract impersonates a child contract and the child contract is calling another contract then we actually create the child contract with 1 byte of code (zero). I don’t like this because it would make IMPERSONATECALL more expensive in two ways. First the actual I/O of creating the contract and second the need to check if the code already exists on every IMPERSONATECALL.
We can add an in-memory map address->callDepth, where callDepth is an integer that counts the number of recursive IMPERSONATECALLs performed for that address. When IMPERSONATECALL is called for a child contract X then the depth of X is incremented in the map. Whenever a contract executes EXTCODESIZE, the address is looked up in the map, and if the depth counter is greater than zero then the value 1 is returned as code size, simulating a 1-byte code. A similar emulation is performed for EXTCODECOPY. When IMPERSONATECALL returns, the counter is decremented and the item is removed if counter reaches zero. While this solves the problem, I don’t like it either, because adding more state to VM execution complicates fraud proofs for L2 EVM rollups that attempt to emulate the EVM.
Still another possibility is to let IMPERSONATECALL dynamically apply codes to addresses on-the-fly on each call. A similar map like in (2) would be used, but containing the counter plus the code itself (for simplicity, no nested code changes would be allowed within a single transaction).
Another possibility is to allow a single IMPERSONATECALL per contract per transaction. This limitation would make calls from impersonated addresses limited very much like EOAs (the main difference would be that EOA have ORIGIN == SENDER while calls from impersonated addresses won’t).
To summarize, I think the best is to do nothing. I don’t like options (1) to (3) because of the added complexity. Option (4) is ok.
In the future, if a larger address space is implemented in Ethereum, then we can use an address space ID to identify impersonated addresses, so contracts can treat them as contracts if desired.