Calculated from recorded token losses using historical USD prices at the incident time.
0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a46610x549D0CdC753601fbE29f9DE186868429a8558E07Base0x142017b52c99d3dFe55E49d79Df0bAF7F4478c0cBase0x6345aF6dA3EBd9DF468e37B473128Fd3079C4a4bBaseOn Base, transaction 0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a4661 exploited Sumer's timelock at 0x549D0CdC753601fbE29f9DE186868429a8558E07 by using assets borrowed and redeemed from Sumer markets in the same transaction they were supposed to be time-locked. The attacker flash-loaned 150 WETH and 645,000 USDC, supplied collateral, borrowed all liquid wstETH from cWstETH and 310,000 USDC from cUSDC, redeemed helper-held cUSDC, and immediately claimed the resulting timelock agreements. The root cause was that Sumer configured lockDuration = 0 for the relevant supported underlyings, while the timelock implementation computed releaseTime = block.timestamp + lockDuration and allowed claims when block.timestamp >= releaseTime. That made the timelock a same-transaction passthrough instead of a delay mechanism. The attacker cluster exited with 309999999999 USDC units and 10877097943908610977 wstETH units after repaying Balancer inside the same transaction.
Sumer routes some borrow and redeem payouts through a timelock instead of transferring underlying assets directly to the borrower or redeemer. The timelock keeps per-underlying configuration in underlyingDetail, including the mapped cToken, a running , , and an flag. When a Sumer cToken chooses the timelock route, it transfers underlying assets into the timelock and creates an agreement for the beneficiary.
totalBalancelockDurationisSupportThe relevant victim components were the Sumer timelock, the cUSDC market at 0x142017b52c99d3dFe55E49d79Df0bAF7F4478c0c, the cWstETH market at 0x6345aF6dA3EBd9DF468e37B473128Fd3079C4a4b, and the cETH market at 0x7b5969bB51fa3B002579D7ee41A454AC691716DC. The attacker financed the position with a permissionless Balancer flash loan from 0xBA12222222228d8Ba445958a75a0704d566BF2C8, which made the exploit realizable by an unprivileged actor in one transaction.
The mechanism only enforces a delay if lockDuration is positive. Collector storage evidence for block 13076768 shows that USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) and wstETH (0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22) were supported and both had lockDuration = 0.
The vulnerability is an attack-class logic failure in the timelock delay enforcement. The timelock code allows administrators to set lockDuration to zero and does not validate that a created agreement must become claimable only after a strictly future timestamp. In Timelock.createAgreement, the contract computes releaseTime = block.timestamp + underlyingDetail[underlying].lockDuration. In Timelock._validateAndDeleteAgreement, the claim check is block.timestamp >= agreement.releaseTime. When lockDuration is zero, agreements created during a borrow or redeem become claimable immediately in the same block and same transaction.
That behavior breaks the intended invariant for time-locked borrow and redeem proceeds. Instead of forcing assets to remain escrowed for a delay window, the timelock simply records an agreement and then authorizes instant withdrawal. The exploit path used this flaw on both borrow-created and redeem-created agreements, so the attacker could drain market liquidity, claim the escrowed assets immediately, repay the flash loan, and keep the remainder as profit.
The timelock code contains the critical breakpoint directly:
function createAgreement(...) external onlyCToken(underlying) returns (uint256) {
...
uint256 releaseTime = block.timestamp + underlyingDetail[underlying].lockDuration;
agreements[agreementId] = Agreement({ ... releaseTime: releaseTime, ... });
}
function _validateAndDeleteAgreement(uint256 agreementId) internal returns (Agreement memory) {
Agreement memory agreement = agreements[agreementId];
require(block.timestamp >= agreement.releaseTime, "Release time not reached");
...
}
This code was verified from the collected source for Timelock.sol. There is no lower-bound check in setLockDuration, no minimum-delay guard in createAgreement, and no claim-side rule that requires releaseTime to be strictly greater than the current timestamp.
The Sumer market payout path routes assets into this timelock when the timelock path is active. For example, CEther.transferToTimelock sends underlying ETH to the timelock and immediately creates an agreement for the beneficiary:
doTransferOut(payable(timelock), underlyAmount);
ITimelock(timelock).createAgreement(
isBorrow ? ITimelock.TimeLockActionType.BORROW : ITimelock.TimeLockActionType.REDEEM,
underlyAmount,
to
);
The collector's decoded storage for block 13076768 proves the necessary pre-state. USDC and wstETH were both marked supported and both had lockDuration = 0 before the exploit transaction. The same artifact shows the timelock was not frozen and agreement count was 309 before the exploit.
The parsed receipt metadata for transaction 0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a4661 then shows the immediate consequence. Agreements 309, 310, and 311 were created with releaseTime = 1712942885, exactly equal to the block timestamp 1712942885. The same transaction emitted AgreementClaimed events for those same agreement IDs. That is direct evidence that the timelock's delay invariant had collapsed: the escrow records were created and consumed without any elapsed time.
The exploit also relied on the cETH refund callback path to re-enter from the helper contract after repayBorrowBehalf returned excess ETH. That callback was relevant to the execution choreography, but it was not the root cause. The root cause remained the zero-delay timelock configuration combined with the code path that treated zero as immediately claimable.
The adversary cluster consisted of EOA 0xbb344544ad328b5492397e967fe81737855e7e77, attack contract 0x13d27a2d66ea33a4bc581d5fefb0b2a8defe9fe7, and helper contract 0xff32d7182f60a107f662af3486929cb63b1d4c9c. The exploit was realized entirely in transaction 0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a4661 at block 13076769.
First, the attack contract borrowed 150 WETH and 645000000000 USDC units from Balancer. It unwrapped the WETH, minted cETH with the ETH, entered Sumer markets, and transferred the flash-loaned USDC to a freshly deployed helper contract, which minted cUSDC and also entered the same market set.
Second, the helper borrowed cETH and repaid it with repayBorrowBehalf. The CEther.repayBorrowBehalf implementation refunds excess ETH to msg.sender, and that refund invoked the helper's payable fallback. On the second ETH receipt, the helper called back into the attack contract, which then borrowed all available wstETH from cWstETH, borrowed 310000000000 USDC units from cUSDC, redeemed 150 ETH from cETH, and claimed its timelock agreements immediately.
Third, the helper redeemed its cUSDC position, which created agreement 311 for 644999999999 USDC units and immediately claimed it in the same transaction. Those redeemed USDC proceeds were returned to the attack contract.
Finally, the attack contract wrapped 150 ETH back into WETH, repaid Balancer's WETH and USDC flash-loan principal, and transferred the remaining USDC and wstETH to the attacker EOA. The collector balance-diff artifact confirms the final profit: 309999999999 USDC units and 10877097943908610977 wstETH units credited to the EOA, with corresponding losses from cUSDC and cWstETH.
The measurable loss to Sumer was the drain of liquid assets from two markets:
309999999999 USDC units (309999.999999 USDC) from cUSDC10877097943908610977 wstETH units (10.877097943908610977 wstETH) from cWstETHThe collector balance-diff data matches those exact quantities. cUSDC decreased from 310570845598 to 570845599, and cWstETH decreased from 10877097943908610977 to 0. Balancer was repaid in full by the end of the same transaction, so the economic loss remained with the Sumer markets rather than the flash-loan venue.
0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a46610x549D0CdC753601fbE29f9DE186868429a8558E070x142017b52c99d3dFe55E49d79Df0bAF7F4478c0c0x6345aF6dA3EBd9DF468e37B473128Fd3079C4a4b0x7b5969bB51fa3B002579D7ee41A454AC691716DC13076768