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`.
## Root Cause
### Vulnerable Contract
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.
### Vulnerable Function
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.
### Vulnerable Code
“`
// [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); } } }
“`
### Why It’s Vulnerable
**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.
## Attack Execution
### High-Level Flow
1. The attacker sent a contract-creation transaction from their EOA.
2. The created orchestrator checked the victim proxy’s balances across a prepared list of ERC-20 assets.
3. The orchestrator deployed a helper contract containing the attacker beneficiary and token list.
4. The orchestrator deployed malicious delegatecall logic that sweeps all listed token balances from `address(this)`.
5. The orchestrator called the Renegade proxy initializer with attacker-controlled addresses.
6. The proxy delegatecalled the Renegade implementation, which then delegatecalled the attacker’s malicious logic.
7. The malicious logic ran in the proxy context and transferred every listed non-zero token balance from the proxy to the attacker EOA.
8. The orchestrator called `updateWallet(…)`, which again reached the attacker logic; by then the balances had already been drained.
### Detailed Call Trace
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]`).
– The proxy delegatecalls implementation `0xc038933d0b33359f5c87b4b2f92ee0dad11eadc5`, then attacker logic `0x67da0e9245e2a9da74ac120d8c3caac21b9884da` with the same selector ( `path .calls[29].calls[0].calls[0]`).
– The attacker logic again queries the helper for beneficiary and assets; no further non-zero transfers occur because the first sweep drained the listed balances.
## Financial Impact
`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:
TokenAmountSYNTH1,349.030733PENDLE9,416.895325523230308297CRV7,415.260610772991599751DeFAI3,231.358400000000262144LDO10,869.732916014088671746LPT791.159853095869991427WBTC0.34658469FTW15,000RDNT8,826.971015995232899515COMP89.156815942637822957EVA0.50314019XAI156,877.640373083310394068HOL3.6ZRO1,372.645794746838088638ETHFI6,445.161607004354946696WETH10.276455529684622407ARB15,471.646463948749671683GRT69,679.691872219913851209USDC104,383.594837BKC0.01AAVE30.99292894656266577SNL3,750LINK385.587121743292431151UNI528.460406629467438505GMX250.449691926370589435USD01,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.
## Evidence
– Receipt status is `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`.
– The initializer calldata contains attacker logic `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.
