On May 10, 2026 at 08:27:23 UTC, Renegade Dark Pool Proxy 1 on Arbitrum (0x30bd8eab29181f790d7e495786d4b96d7afdc518) was drained through an access-control failure in its initialization path. The attacker EOA 0x777253f28adc29645152b7b41be5c772a9657777 created an orchestrator contract, deployed malicious delegatecall logic, then called the proxy’s initialize(...) function with attacker-controlled addresses. The trace proves the proxy accepted that call, delegatecalled the Renegade implementation, then delegatecalled attacker logic that transferred 26 ERC-20 balances from the proxy to the attacker. funds_flow.json records the direct gain as 26 assets, including 104,383.594837 USDC, 10.276455529684622407 WETH, and 0.34658469 WBTC; the public incident brief reports the basket at approximately $209K.
The vulnerable contract is Renegade Dark Pool Proxy 1 at 0x30bd8eab29181f790d7e495786d4b96d7afdc518. It is a proxy whose implementation was resolved in the analysis plan and manifest as 0xc038933d0b33359f5c87b4b2f92ee0dad11eadc5.
The implementation source is unverified and was recovered as trace-guided pseudocode in analysis_0x0e494685ace16d372066c5b4db959b58ebac6d88166c2d9d618e0e421dc0c77e/0xc038933d0b33359f5c87b4b2f92ee0dad11eadc5/recovered.sol. Its decompile_meta.json confidence is low, so the root cause is derived from the trace first: an arbitrary caller reached initialize(...), supplied attacker-controlled addresses, and caused a nested DELEGATECALL into attacker code from the proxy context.
The vulnerable function is initialize(address,address,address,address,address,address,address,address,address,address,uint256,uint256[2],address) on the implementation, selector 0x92413afe. The selector was verified with cast sig and matches selectors.json.
The later updateWallet(bytes,bytes,bytes,bytes) call, selector 0x803f430a, reused the same attacker-controlled delegatecall path after the initializer had installed or accepted attacker-controlled configuration.
// [recovered -- approximation]
contract Recovered_RenegadeDarkPoolImplementation_adc5 {
// Large unverified implementation recovered trace-guided only.
// The transaction exercised initialize(...) and updateWallet(...), both via proxy DELEGATECALL.
function initialize(
address, address, address, address, address, address, address, address, address, address,
uint256, uint256[2] calldata, address
) external { // <-- VULNERABILITY: trace shows this initializer was callable by the attacker through the proxy.
// selector 0x92413afe; signature from cast 4byte
// Trace shows this path performs a nested DELEGATECALL from the proxy context
// into attacker-created logic 0x67da0e9245e2a9da74ac120d8c3caac21b9884da with selector 0xe1c7392a. // <-- VULNERABILITY
// Storage writes are present in disassembly; exact slot semantics unresolved.
}
function updateWallet(bytes calldata, bytes calldata, bytes calldata, bytes calldata) external {
// selector 0x803f430a; signature from cast 4byte
// Trace shows this path performs a nested DELEGATECALL from the proxy context
// into attacker-created logic 0x67da0e9245e2a9da74ac120d8c3caac21b9884da with selector 0x803f430a. // <-- VULNERABILITY: post-initialization path still executes attacker logic.
}
}
The malicious delegate target is recovered with medium confidence in analysis_0x0e494685ace16d372066c5b4db959b58ebac6d88166c2d9d618e0e421dc0c77e/0x67da0e9245e2a9da74ac120d8c3caac21b9884da/recovered.sol and matches the trace: it reads a helper-controlled beneficiary and asset list, then transfers every non-zero address(this) balance. Under DELEGATECALL, address(this) is the victim proxy.
// [recovered -- approximation]
function _sweepAssetsFromDelegateContext() internal {
address receiver = IHelper_92df(HELPER).beneficiary();
address[] memory assets = IHelper_92df(HELPER).getAssets();
for (uint256 i = 0; i < assets.length; i++) {
uint256 bal = IERC20Like(assets[i]).balanceOf(address(this));
if (bal != 0) {
IERC20Like(assets[i]).transfer(receiver, bal);
}
}
}
Expected behavior: an initializer on an already-deployed production proxy should be callable only once and only by an authorized deployer/admin. It should not accept attacker-controlled module or hook addresses and then execute those addresses with DELEGATECALL in proxy storage/context.
Actual behavior: the attacker-controlled orchestrator 0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 called the proxy with selector 0x92413afe. The calldata’s first ten address parameters were all 0x67da0e9245e2a9da74ac120d8c3caac21b9884da, and the trace shows the proxy delegatecalled the implementation and then delegatecalled that attacker-created address. No owner check, initialization guard, or caller restriction stopped the call.
This matters because a delegatecall target executes as the proxy. The malicious logic used beneficiary() and getAssets() from helper 0x92df7b51734d4d8f5de7676ab193ff2138cb4b5c, then called transfer(attacker, balance) on each token from the proxy context. The token Transfer events therefore show the victim proxy as from and the attacker EOA as to.
Normal flow should initialize trusted Renegade components once during deployment and reject later public initialization attempts. The attack flow let an arbitrary external account re-enter the initialization surface, point Renegade’s delegated execution at attacker code, and then sweep the proxy’s token inventory.
address(this).updateWallet(...), which again reached the attacker logic; by then the balances had already been drained.The following flow is derived from trace_callTracer.json; selector names come from selectors.json and were verified with cast sig.
0x777253f28adc29645152b7b41be5c772a9657777 -> creates 0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 (CREATE, root call, value 0).0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 -> token contracts: repeated balanceOf(address) (0x70a08231) static calls for victim 0x30bd8eab29181f790d7e495786d4b96d7afdc518.0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 -> creates 0x92df7b51734d4d8f5de7676ab193ff2138cb4b5c (CREATE, trace_callTracer.json path .calls[26]).0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 -> creates 0x67da0e9245e2a9da74ac120d8c3caac21b9884da (CREATE, path .calls[27]).0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 -> 0x30bd8eab29181f790d7e495786d4b96d7afdc518: initialize(address,address,address,address,address,address,address,address,address,address,uint256,uint256[2],address) (0x92413afe, CALL, path .calls[28]).0x30bd8eab29181f790d7e495786d4b96d7afdc518 -> 0xc038933d0b33359f5c87b4b2f92ee0dad11eadc5: same initialize(...) selector (DELEGATECALL, path .calls[28].calls[0]).0x30bd8eab29181f790d7e495786d4b96d7afdc518 -> 0x67da0e9245e2a9da74ac120d8c3caac21b9884da: init() (0xe1c7392a, DELEGATECALL, path .calls[28].calls[0].calls[0]).0x30bd8eab29181f790d7e495786d4b96d7afdc518 -> 0x92df7b51734d4d8f5de7676ab193ff2138cb4b5c: beneficiary() (0x38af3eed, STATICCALL) returns the attacker beneficiary.0x30bd8eab29181f790d7e495786d4b96d7afdc518 -> 0x92df7b51734d4d8f5de7676ab193ff2138cb4b5c: getAssets() (0x67e4ac2c, STATICCALL) returns the 26-token asset list.0x30bd8eab29181f790d7e495786d4b96d7afdc518 -> token contracts: repeated balanceOf(address) and transfer(address,uint256) (0xa9059cbb) calls send balances to 0x777253f28adc29645152b7b41be5c772a9657777.0x30bd8eab29181f790d7e495786d4b96d7afdc518 -> 0x67da0e9245e2a9da74ac120d8c3caac21b9884da: init(address) (0x19ab453c, DELEGATECALL, path .calls[28].calls[0].calls[1]), again queries beneficiary() and getAssets().0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34 -> 0x30bd8eab29181f790d7e495786d4b96d7afdc518: updateWallet(bytes,bytes,bytes,bytes) (0x803f430a, CALL, path .calls[29]).0xc038933d0b33359f5c87b4b2f92ee0dad11eadc5, then attacker logic 0x67da0e9245e2a9da74ac120d8c3caac21b9884da with the same selector (path .calls[29].calls[0].calls[0]).funds_flow.json is the primary evidence for impact. It records 26 ERC-20 transfers from victim proxy 0x30bd8eab29181f790d7e495786d4b96d7afdc518 to attacker 0x777253f28adc29645152b7b41be5c772a9657777; the victim has the exact negative net changes and the attacker has matching positive net changes.
The drained assets were:
| Token | Amount |
|---|---|
| SYNTH | 1,349.030733 |
| PENDLE | 9,416.895325523230308297 |
| CRV | 7,415.260610772991599751 |
| DeFAI | 3,231.358400000000262144 |
| LDO | 10,869.732916014088671746 |
| LPT | 791.159853095869991427 |
| WBTC | 0.34658469 |
| FTW | 15,000 |
| RDNT | 8,826.971015995232899515 |
| COMP | 89.156815942637822957 |
| EVA | 0.50314019 |
| XAI | 156,877.640373083310394068 |
| HOL | 3.6 |
| ZRO | 1,372.645794746838088638 |
| ETHFI | 6,445.161607004354946696 |
| WETH | 10.276455529684622407 |
| ARB | 15,471.646463948749671683 |
| GRT | 69,679.691872219913851209 |
| USDC | 104,383.594837 |
| BKC | 0.01 |
| AAVE | 30.99292894656266577 |
| SNL | 3,750 |
| LINK | 385.587121743292431151 |
| UNI | 528.460406629467438505 |
| GMX | 250.449691926370589435 |
| USD0 | 1,892.705374 |
The public incident brief reports the basket value as approximately $209K. No flash loan appears in this trace; the attacker paid only transaction gas. The receipt shows gasUsed = 2,688,710 and effectiveGasPrice = 20,052,000 wei, or about 0.00005391401292 ETH gas cost.
The loss source was the Renegade Dark Pool proxy’s held assets, so the impact falls on users/protocol balances custodied by that proxy. The proxy’s listed token inventory was effectively swept for every asset with a non-zero balance in the helper-provided list.
0x1, confirming the transaction succeeded; the transaction created orchestrator 0x33fb722c76d4e9fc0c86bbf10ebdea45a4434a34.trace_callTracer.json path .calls[28] shows attacker orchestrator -> victim proxy CALL with selector 0x92413afe.trace_callTracer.json path .calls[28].calls[0].calls[0] shows victim proxy -> attacker logic DELEGATECALL with selector 0xe1c7392a.trace_callTracer.json paths .calls[28].calls[0].calls[0].calls[0] and .calls[28].calls[0].calls[0].calls[1] show attacker logic, still from proxy context, querying helper selectors 0x38af3eed and 0x67e4ac2c.0x67da0e9245e2a9da74ac120d8c3caac21b9884da in the first ten address arguments.funds_flow.json contains 26 Transfer events with from = 0x30bd8eab29181f790d7e495786d4b96d7afdc518 and to = 0x777253f28adc29645152b7b41be5c772a9657777.trace_prestateTracer.json does not include the victim proxy account, so it is not useful for proving proxy storage changes in this dataset; the delegatecall sequence and transfer logs are the primary evidence.