// inside head tag
Some smart contract failures today do not come from complex bugs buried deep in business logic. They happen earlier, during deployment.
Upgradeable contracts introduce a critical risk window between contract creation and initialization. If that window is not handled correctly, an attacker can take control of the proxy before the team ever interacts with it. Audits often do not catch this because the code itself is correct. The failure sits in the deployment flow.
This article explains the CPIMP attack (Clandestine Proxy In The Middle of Proxy), a deployment-time exploit that targets uninitialized upgradeable proxies. It shows how initialization frontrunning works, why it's hard to detect, and how deployment decisions can compromise otherwise secure protocols.
The CPIMP attack targets the initialization of upgradeable smart contracts.
In a standard upgradeable architecture, a proxy contract holds state and delegates logic to an implementation contract. If a proxy is deployed but not initialized atomically, it exists on-chain in an uninitialized state.
An attacker can detect this condition, front-run the initialization transaction, and initialize the proxy first.
Rather than simply taking ownership, the attacker injects a malicious “middleman” contract, the CPIMP, as the implementation. This contract forwards calls to the intended implementation, allowing the protocol to appear functional. At the same time, it retains attacker-controlled logic that can later drain funds, hijack execution, or manipulate storage.

Research from Dedaub shows why this attack is particularly dangerous. Because the CPIMP sits between the proxy and the legitimate implementation contract, it can persist across upgrades. Standard tooling may continue to report the expected implementation address while the system remains compromised.
To understand how to prevent frontrunning during deployment, we must first look at the pattern that makes it possible. Some smart contract deployment scripts treat deployment and initialization as two separate steps. This is often done to handle protocols with complex or circular dependencies.
In this insecure scenario, the proxy is deployed first, and initialize() is called in a subsequent transaction.
// VULNERABLE DEPLOYMENT FLOW
// 1. Deploy the implementation
MyLogic implementation = new MyLogic();
// 2. Deploy the Proxy pointing to the implementation
// Note: The 'data' field is empty (""), meaning no atomic initialization occurs here.
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), "");
// --- DANGER ZONE ---
// The proxy is now on-chain but uninitialized.
// An attacker can see this in the mempool and front-run the next step.
// 3. Initialize the proxy (Too late!)
MyLogic(address(proxy)).initialize(admin, initialSupply);
In the danger zone above, an attacker observes the pending initialize transaction. They submit their own transaction with a higher gas fee to initialize the proxy first. Their initialization sets the implementation to their own malicious contract (the CPIMP), which then quietly proxies calls to the intended logic contract.
This is a classic case of proxy initialization frontrunning. Even if steps 1 to 3 from above are part of the same scripted flow, this is insufficient to mitigate the attack. Between steps 2 and 3, the attacker has an opening to frontrun the initialization.

The reliable mitigation for this class of attacks is atomic deployment. Atomic deployment means that the proxy is deployed and initialized in a single transaction. If the deployment succeeds, the proxy is already initialized; if the initialization fails, the deployment reverts. There is no gap for an attacker to exploit.
This can be achieved using the OpenZeppelin ERC1967Proxy standard. The constructor of the ERC1967Proxy accepts two arguments:
implementation: The address of the logic contract._data: This will typically be an encoded function call that allows initializing the storage of the proxy, like a constructor would.
If _data is populated, the proxy will do a delegatecall to the implementation using that _data during construction. If the _data parameter is empty, the proxy will be deployed, but it will be uninitialized.
Here is how to write a secure smart contract deploy script using Foundry or Hardhat that ensures atomicity.
// SECURE ATOMIC DEPLOYMENT
// 1. Deploy the implementation logic first
MyLogic implementation = new MyLogic();
// 2. Prepare the initialization data
// This encodes the function signature and arguments: initialize(address,uint256)
bytes memory initData = abi.encodeWithSelector(
MyLogic.initialize.selector,
deployerAddress,
someArbitraryDataHere
);
// 3. Deploy Proxy AND Initialize in one go
// The constructor will execute the 'initData' against the implementation immediately.
ERC1967Proxy proxy = new ERC1967Proxy(
address(implementation),
initData
);
// Result: The proxy is deployed and initialized.
// No gap exists for a CPIMP attack.
Passing initData to the ERC1967Proxy constructor prevents the proxy from ever existing in an uninitialized state, eliminating the CPIMP attack surface.
While atomic deployments are the gold standard, real-world protocols are messy. Some protocols may use "circular dependencies", where Contract A needs the address of Contract B to initialize, but Contract B needs the address of Contract A.
In practice, many teams do not handle circular dependencies safely. A common pattern is to deploy contracts first and postpone initialization, assuming the window between transactions is insignificant or unlikely to be exploited. This assumption is incorrect. Upgradeable proxies should never be deployed without being initialized in the same transaction. When circular dependencies exist, teams are expected to resolve them explicitly rather than rely on delayed initialization.
Two approaches are commonly used:
CREATE2 (deterministic deployment) to compute the address of a dependent contract before deployment. This allows all required addresses to be passed into an atomic initialization call.initialize function must be tightly restricted so only the intended deployer can call it. This reduces risk but does not eliminate it and should be treated as a last resort.
The CPIMP attack is not a flaw in proxy design or upgrade patterns. It is a mistake in how the deployment is done.
When proxies are deployed without being initialized atomically, they create a window where control can be lost before the protocol is even live. Once a malicious intermediary contract is inserted, the system may continue to function normally while being compromised.
To prevent this class of failure in practice:
_data parameter of the ERC1967Proxy constructor so deployment and initialization occur in a single transaction.initialize function.
Protocols that overlook these steps often discover the issue only after funds are at risk. Secure deployment is not a follow-up task. It is part of the security boundary.