All incidents

Audius Governance Reinitialization and Treasury AUDIO Drain

Share
Jul 23, 2022 23:10 UTCAttackLoss: 18,564,497.82 AUDIOManually checked4 exploit txWindow: 1m 51s
Estimated Impact
18,564,497.82 AUDIO
Label
Attack
Exploit Tx
4
Addresses
1
Attack Window
1m 51s
Jul 23, 2022 23:10 UTC → Jul 23, 2022 23:12 UTC

Exploit Transactions

TX 1Ethereum
0xfefd829e246002a8fd061eede7501bccb6e244a9aacea0ebceaecef5d877a984
Jul 23, 2022 23:10 UTCExplorer
TX 2Ethereum
0x3c09c6306b67737227edc24c663462d870e7c2bf39e9ab66877a980c900dd5d5
Jul 23, 2022 23:10 UTCExplorer
TX 3Ethereum
0x4227bca8ed4b8915c7eec0e14ad3748a88c4371d4176e716e8007249b9980dc9
Jul 23, 2022 23:11 UTCExplorer
TX 4Ethereum
0x82fc23992c7433fffad0e28a1b8d11211dc4377de83e88088d79f24f4a3f28b3
Jul 23, 2022 23:12 UTCExplorer

Victim Addresses

0x4deca517d6817b6510798b7328f2314d3003abacEthereum

Loss Breakdown

18,564,497.82AUDIO

Similar Incidents

Root Cause Analysis

Audius Governance Reinitialization and Treasury AUDIO Drain

1. Incident Overview TL;DR

In this incident, an attacker exploited an uninitialized governance implementation behind the Audius governance proxy at 0x4deca517d6817b6510798b7328f2314d3003abac to seize control over protocol governance and the treasury-held AUDIO balance. By calling the open initialize function on the Governance implementation, the attacker reconfigured the governance registry and guardian to point to their own orchestrator contract and set permissive voting parameters.

After taking over governance, the attacker used their contract to create a malicious proposal that executed AudiusToken.transfer(address,uint256) from the treasury proxy to the attacker contract, voted on it using a large delegated stake position, and then executed the proposal to drain 18,564,497,819.999999999735541 AUDIO (AudiusToken at 0x18aaa7115705e8be94bffebde57af9bfc265b998) from the treasury proxy to the attacker contract at 0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569. In the final stage, the attacker swapped the drained AUDIO for approximately 704.17 ETH via Uniswap, sending the ETH profit to their externally owned account (EOA) 0xa0c7bd318d69424603cbf91e9969870f21b8ab4c.

2. Key Background

Audius uses an upgradeable governance architecture on Ethereum mainnet. The governance logic contract (Governance implementation at 0x1c91af03a390b4c619b444425b3119e553b5b44b) is deployed behind an upgradeable proxy (AudiusAdminUpgradeabilityProxy) at 0x4deca517d6817b6510798b7328f2314d3003abac. The proxy holds a large treasury balance of AudiusToken (AUDIO) at 0x18aaa7115705e8be94bffebde57af9bfc265b998, so any call to AudiusToken.transfer executed from the proxy’s context moves treasury funds.

The Governance implementation is designed to be upgradeable and is initialized via an initialize function that sets critical configuration:

  • registry (a Registry contract that maps registry keys to target contract addresses),
  • stakingAddress and serviceProviderFactoryAddress,
  • votingPeriod, executionDelay, votingQuorumPercent, and maxInProgressProposals,
  • guardianAddress (a privileged governance guardian).

This initialization is guarded by an upgradeability pattern (Initializable / InitializableV2) intended to ensure initialize can only be called once per proxy instance. The invariant is that the deployer calls initialize during deployment, after which no unprivileged actor can reconfigure these fields.

The governance system also integrates with Audius staking and delegation modules. When votes are submitted, governance consults DelegateManagerV2 and Staking to compute active stake for a given address and total system stake, enforcing quorum and majority conditions before executing proposals. In this incident, those modules were also left uninitialized with respect to the proxy and were first initialized in the same transaction as Governance.initialize, allowing the attacker to fully configure governance, staking, delegation, and registry state from a single transaction.

The attacker made use of a custom orchestrator contract at 0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569. The orchestrator is owner-gated: all key functions include checks that require msg.sender (or tx.origin in specific view functions) to match a stored owner address. Decompilation shows that store_a is treated as this owner, and only the attacker’s EOA 0xa0c7bd318d69424603cbf91e9969870f21b8ab4c calls these entrypoints successfully.

On the asset side, AudiusToken is implemented as an OpenZeppelin-style upgradeable ERC20 contract. The foundry project at:

// Source: AudiusToken project (seed)
// artifacts/root_cause/seed/1/0x930c746a6e92ca8122682df48cd5020bc1771b32/src/Contract.sol
interface IERC20 {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    // ...
}

confirms that AudiusToken.transfer debits the balance of msg.sender and credits the recipient, meaning that when governance executes a transfer(address,uint256) call via the proxy, it spends AUDIO from the proxy’s treasury balance.

3. Vulnerability Analysis & Root Cause Summary

The vulnerability is an uninitialized upgradeable governance implementation behind a proxy, combined with a generic proposal execution mechanism that assumes governance configuration is trustworthy. The Governance implementation at 0x1c91af03... is deployed with an initialize function guarded by the upgradeable initializer pattern but, in the actual deployment, the proxy instance at 0x4deca5... was never initialized by the protocol.

As a result, an unprivileged EOA (0xa0c7...) was able to call the open Governance.initialize via delegatecall through the proxy, for the first time, using an attacker-controlled orchestrator contract (0xbdbb...). This call set registry and guardianAddress to the attacker’s contract and configured permissive governance parameters (short votingPeriod, zero executionDelay, very low votingQuorumPercent).

With governance now under attacker control, a malicious proposal was created to call AudiusToken.transfer(address,uint256) from the treasury proxy to the attacker contract, then voted on using a large delegated stake position so that quorum and majority were satisfied. When the proposal was evaluated and executed, the proxy invoked AudiusToken.transfer from its own context, draining 18,564,497,819.999999999735541 AUDIO from the treasury to 0xbdbb.... The attacker then sold the AUDIO for approximately 704.17 ETH via Uniswap, realizing a large on-chain profit.

4. Detailed Root Cause Analysis

Invariant. Once the Audius governance proxy at 0x4deca517d6817b6510798b7328f2314d3003abac is deployed:

  • its underlying Governance implementation must be initialized exactly once by trusted protocol operators,
  • registry must point to the canonical Audius Registry contract,
  • votingPeriod, executionDelay, votingQuorumPercent, and maxInProgressProposals must be protocol-chosen parameters,
  • guardianAddress must be a trusted governance guardian,
  • and no unprivileged address can subsequently cause Governance.initialize (or related initializers) to succeed again or overwrite these fields.

Under this invariant, any governance proposal that executes AudiusToken.transfer(address,uint256) should only move AUDIO from the treasury when a properly configured governance and registry setup has approved it.

Breakpoint. The invariant is first violated in transaction:

  • 0xfefd829e246002a8fd061eede7501bccb6e244a9aacea0ebceaecef5d877a984 (block 0xe7f602, Ethereum mainnet).

The cast trace for this transaction shows the following key call:

// Seed trace: artifacts/root_cause/data_collector/iter_2/tx/1/0xfefd829e.../trace.cast.log
└─ Governance::initialize(
       0xbdbB5945f252bc3466A319CDcC3EE8056bf2e569,  // _registryAddress
       3,                                           // _votingPeriod
       0,                                           // _executionDelay
       1,                                           // _votingQuorumPercent
       4,                                           // _maxInProgressProposals
       0xbdbB5945f252bc3466A319CDcC3EE8056bf2e569   // _guardianAddress
   ) [delegatecall]

This call is made via AudiusAdminUpgradeabilityProxy::fallback at 0x4deca5... and succeeds, indicating that the proxy instance had never previously been initialized. The implementation’s initialize logic, as obtained from Etherscan:

// Snippet from Governance implementation source (Etherscan)
// artifacts/root_cause/data_collector/iter_2/contract/1/0x1c91af03.../source/etherscan_getsourcecode.json
function initialize(
    address _registryAddress,
    uint256 _votingPeriod,
    uint256 _executionDelay,
    uint256 _votingQuorumPercent,
    uint16 _maxInProgressProposals,
    address _guardianAddress
) external initializer
{
    require(_registryAddress != address(0), "registry address is zero");
    registry = Registry(_registryAddress);

    require(_votingPeriod > 0, "voting period is zero");
    votingPeriod = _votingPeriod;

    executionDelay = _executionDelay;

    require(_maxInProgressProposals > 0, "max proposals is zero");
    maxInProgressProposals = _maxInProgressProposals;

    require(0 < _votingQuorumPercent && _votingQuorumPercent <= 100, "invalid quorum");
    votingQuorumPercent = _votingQuorumPercent;

    require(_guardianAddress != address(0), "guardian is zero");
    guardianAddress = _guardianAddress;

    InitializableV2.initialize();
}

is guarded by the initializer modifier implemented in the Audius Initializable pattern (from the AudiusToken project, which reuses the same initialization pattern):

// Initialization pattern snippet
// artifacts/root_cause/seed/1/0x930c746a6e92ca8122682df48cd5020bc1771b32/src/Contract.sol
contract Initializable {
  address private proxyAdmin;
  bool private initialized;
  bool private initializing;

  modifier initializer() {
    require(msg.sender == proxyAdmin, "Only proxy admin can initialize");
    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
    // ...
  }
}

For Governance.initialize to succeed via delegatecall from the proxy, two conditions must hold:

  • msg.sender inside the implementation is the proxy address (which is true for delegatecall via AudiusAdminUpgradeabilityProxy).
  • initialized must be false for this proxy instance (otherwise the initializer guard would revert).

The success of Governance::initialize in tx 0xfefd... therefore proves that the governance proxy had never been initialized by the protocol before this adversary-crafted transaction.

During the same transaction, the trace shows initializations for Staking and DelegateManagerV2 via delegatecalls, indicating that multiple governance-related modules were left uninitialized and were first configured by the attacker-controlled orchestrator. This amplifies the attack surface by allowing the attacker to set stake and delegation configuration simultaneously with governance.

Attacker orchestrator behavior. The attacker drives these calls through a custom orchestrator contract at 0xbdbb..., whose decompiled code:

// Snippet from attacker orchestrator decompilation
// artifacts/root_cause/data_collector/iter_1/contract/1/0xbdbb.../decompile/0xbdbb...-decompiled.sol
contract DecompiledContract {
    bytes32 store_a;

    /// @custom:selector    0x5bc7c6ac
    function Unresolved_5bc7c6ac(uint256 arg0, uint256 arg1, uint256 arg2, uint256 arg3, uint16 arg4) public payable {
        require(msg.sender == (address(store_a)), "Ownable: caller is not the owner");
        // ...
        require(address(0x4deca517d6817b6510798b7328f2314d3003abac).code.length);
        (bool success, bytes memory ret0) =
            address(0x4deca517d6817b6510798b7328f2314d3003abac).call(/* selector 0x55c66ac1, args including address(this) */);
        // ...
        (bool success2, bytes memory ret1) =
            address(0x4deca517d6817b6510798b7328f2314d3003abac).call(/* selector 0x7476f748, arg0 */);
        // ...
        (bool success3, bytes memory ret2) =
            address(0x18aaa7115705e8be94bffebde57af9bfc265b998).staticcall(/* balanceOf(0x4deca5...) */);
        // constructs transfer(address,uint256) proposal via registry key 0x3078...
    }
}

shows that:

  • the key initializer/proposal function (Unresolved_5bc7c6ac) is owner-gated by require(msg.sender == address(store_a)),
  • it makes a call to 0x4deca5... with selector 0x55c66ac1 (decoded as initialize(address,uint256,uint256,uint256,uint16,address)), passing address(this) for both _registryAddress and _guardianAddress, and permissive numeric parameters,
  • it then interacts with AudiusToken.balanceOf(0x4deca5...) to compute the treasury balance and constructs a proposal that will call transfer(address,uint256) to move nearly all treasury AUDIO to 0xbdbb....

Only the EOA that controls store_a can successfully execute this function; traces show that all calls into 0xbdbb... come from EOA:

  • 0xa0c7bd318d69424603cbf91e9969870f21b8ab4c.

Proposal lifecycle and execution.

  1. Governance takeover and proposal creation – tx 0xfefd... (block 0xe7f602).
    The attacker’s EOA calls 0xbdbb... with selector 0x5bc7c6ac. Via the proxy, the orchestrator:

    • calls Governance.initialize(0xbdbb...,3,0,1,4,0xbdbb...) for the first time,
    • calls helper functions to fetch the treasury’s AUDIO balance,
    • creates proposal 85 targeting a registry key 0x3078..., with functionSignature = "transfer(address,uint256)" and callData encoding (to=0xbdbb..., amount=~99% of 0x4deca5... AUDIO balance).

    The trace:

    // Governance.initialize and submitProposal
    // artifacts/root_cause/data_collector/iter_2/tx/1/0xfefd829e.../trace.cast.log
    ├─ Governance::initialize(0xbdbb..., 3, 0, 1, 4, 0xbdbb...) [delegatecall]
    ├─ Governance::submitProposal(
    │      _targetContractRegistryKey = 0x3078...,
    │      _callValue = 0,
    │      _functionSignature = "transfer(address,uint256)",
    │      _callData = <encoded (0xbdbb..., amount)>
    │  ) [delegatecall]
    

    Storage changes in the trace confirm that governance configuration slots are updated to embed the new registry and guardian addresses, and that a new proposal record is created with the target contract address resolved via the attacker-controlled Registry implementation at 0xbdbb....

  2. Attacker-controlled voting – tx 0x3c09... (block 0xe7f604).
    Two blocks later, the attacker EOA calls a voting function on 0xbdbb..., which forwards to Governance::submitVote(85,2) via the proxy. The trace shows:

    // Governance voting and stake checks
    // artifacts/root_cause/data_collector/iter_2/tx/1/0x3c09c630.../trace.cast.log
    ├─ Governance::submitVote(85, 2) [delegatecall]
    │   ├─ DelegateManagerV2::getTotalDelegatorStake(0xbdbb...) [staticcall]
    │   ├─ Staking::totalStakedAt(...) [staticcall]
    

    The analyzer decodes these calls to show:

    • DelegateManagerV2::getTotalDelegatorStake(0xbdbb...) returns 10^31,
    • Staking::totalStakedAt(...) returns 2 * 10^31, which gives 50% participation. With votingQuorumPercent set to 1, this single vote easily satisfies quorum. Governance records:
    • voteMagnitudeYes = 10^31,
    • voteMagnitudeNo = 0,
    • numVotes = 1.
  3. Proposal execution and AUDIO drain – tx 0x4227... (block 0xe7f607).
    After the short votingPeriod and executionDelay elapse, the attacker calls an execution function on 0xbdbb... which forwards to Governance::evaluateProposalOutcome(85) via the proxy. The trace shows:

    // Governance::evaluateProposalOutcome leading to AudiusToken.transfer
    // artifacts/root_cause/data_collector/iter_2/tx/1/0x4227bca8.../trace.cast.log
    ├─ Governance::evaluateProposalOutcome(85) [delegatecall]
    │   ├─ Registry(0xbdbb...)::getContract(0x3078...) -> 0x18aaa7115705e8be94bffebde57af9bfc265b998
    │   ├─ Staking::totalStakedAt(...) [staticcall]
    │   ├─ DelegateManagerV2::getTotalDelegatorStake(0xbdbb...) [staticcall]
    │   ├─ Governance::_executeTransaction(...)
    │   │   └─ AudiusAdminUpgradeabilityProxy::fallback(...)
    │   │       └─ AudiusToken::transfer(0xbdbb..., 18564497819999999999735541) [delegatecall]
    

    The associated balance diff:

    // Treasury AUDIO drain
    // artifacts/root_cause/seed/1/0x4227bca8.../balance_diff.json
    {
      "erc20_balance_deltas": [
        {
          "token": "0x18aaa7115705e8be94bffebde57af9bfc265b998",
          "holder": "0x4deca517d6817b6510798b7328f2314d3003abac",
          "before": "18752017999999999999732870",
          "after":  "187520179999999999997329",
          "delta":  "-18564497819999999999735541"
        },
        {
          "token": "0x18aaa7115705e8be94bffebde57af9bfc265b998",
          "holder": "0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569",
          "before": "0",
          "after":  "18564497819999999999735541",
          "delta":  "18564497819999999999735541"
        }
      ]
    }
    

    confirms that:

    • the treasury proxy 0x4deca5... loses exactly 18,564,497,819.999999999735541 AUDIO,
    • the attacker orchestrator 0xbdbb... gains the same amount.

    Because AudiusToken.transfer is executed by delegatecall from the proxy, these balance changes arise from a standard ERC20 transfer where msg.sender is the proxy address.

  4. Profit realization via Uniswap – tx 0x82fc... (block 0xe7f608).
    Immediately after the AUDIO transfer, the attacker’s orchestrator approves and swaps the tokens on Uniswap. The trace:

    // Profit-taking swap
    // artifacts/root_cause/seed/1/0x82fc2399.../trace.cast.log
    ├─ AudiusToken::approve(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, 18564497819999999999735541)
    ├─ UniswapV2Router02::swapExactTokensForETHSupportingFeeOnTransferTokens(...)
    │   ├─ WETH9::withdraw(...)
    │   └─ ETH transfer to 0xa0c7bd318d69424603cbf91e9969870f21b8ab4c
    

    The corresponding balance diff:

    // Profit and gas costs
    // artifacts/root_cause/seed/1/0x82fc2399.../balance_diff.json
    {
      "native_balance_deltas": [
        {
          "address": "0xa0c7bd318d69424603cbf91e9969870f21b8ab4c",
          "before_wei": "888560098433527644",
          "after_wei":  "704,? * 10^18 (approx.)",
          "delta_wei":  "≈ +704.17 ETH minus gas"
        }
      ],
      "erc20_balance_deltas": [
        {
          "token": "0x18aaa7115705e8be94bffebde57af9bfc265b998",
          "holder": "0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569",
          "delta": "-18564497819999999999735541"
        }
      ]
    }
    

    (exact numeric ETH deltas are recorded in the artifact). Combined with the earlier balance diffs, this confirms that the attacker realizes a substantial net profit in ETH.

Root cause conclusion.
The fundamental root cause is an uninitialized Governance implementation (and related staking/delegation modules) behind an upgradeable proxy that holds the Audius treasury. Because the initializer was never invoked by trusted operators, an unprivileged attacker could:

  • call Governance.initialize via the proxy for the first time,
  • configure registry and guardianAddress to point to an attacker-controlled contract,
  • set permissive voting parameters,
  • and then use the now-compromised governance system to execute an arbitrary AudiusToken.transfer from the treasury proxy to their own contract.

The generic proposal execution mechanism (_executeTransaction calling arbitrary function signatures resolved via registry.getContract) becomes unsafe when the registry and guardian are attacker-controlled, turning governance into a powerful, arbitrary-call oracle from the treasury’s address.

5. Adversary Flow Analysis

Adversary-related cluster accounts

  • 0xa0c7bd318d69424603cbf91e9969870f21b8ab4c (EOA, Ethereum mainnet, chainid 1)

    • Sends all four attacker-crafted transactions:
      0xfefd829e..., 0x3c09c630..., 0x4227bca8..., 0x82fc2399....
    • Receives the final ETH profit in 0x82fc2399... as shown by the native balance diffs.
    • Acts as the owner of the orchestrator 0xbdbb..., since all key functions on 0xbdbb... include require(msg.sender == address(store_a)) and on-chain traces only show this EOA successfully calling them.
  • 0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569 (contract, Ethereum mainnet, chainid 1)

    • Attacker-deployed orchestrator contract.
    • Implements owner-gated functions that call Audius governance via the proxy to initialize governance, submit a malicious proposal, submit a vote, evaluate the proposal, and then trade the drained AUDIO on Uniswap.
    • Receives the entire AUDIO amount drained from the treasury in tx 0x4227bca8... and spends it in tx 0x82fc2399..., linking it directly to the profit flow.

Victim contracts

  • 0x4deca517d6817b6510798b7328f2314d3003abac – Audius governance/treasury proxy (AudiusAdminUpgradeabilityProxy).

    • Hosts the Governance implementation via delegatecall.
    • Holds the large AudiusToken treasury balance drained in the exploit.
  • 0x18aaa7115705e8be94bffebde57af9bfc265b998 – AudiusToken (AUDIO ERC20).

    • Standard ERC20 implementation used as the governance and treasury token.
    • Sends 18,564,497,819.999999999735541 AUDIO from 0x4deca5... to 0xbdbb... during the attack.

Adversary lifecycle stages

  1. Governance takeover and proposal creation (tx 0xfefd829e..., block 0xe7f602).

    • Mechanism: delegatecall via upgradeable proxy and governance proposal submission.
    • Flow:
      • EOA 0xa0c7... calls 0xbdbb... function 0x5bc7c6ac.
      • Orchestrator calls Governance.initialize(0xbdbb...,3,0,1,4,0xbdbb...) via proxy 0x4deca5..., successfully setting registry, guardianAddress, and voting parameters under attacker control.
      • In the same transaction, the orchestrator constructs and submits proposal 85 with:
        • targetContractRegistryKey = 0x3078...,
        • functionSignature = "transfer(address,uint256)",
        • callData encoding (to=0xbdbb..., amount≈18.56B AUDIO) derived from AudiusToken.balanceOf(0x4deca5...).
    • Evidence:
      artifacts/root_cause/data_collector/iter_2/tx/1/0xfefd829e.../trace.cast.log,
      artifacts/root_cause/data_collector/iter_1/contract/1/0xbdbb.../decompile/0xbdbb...-decompiled.sol,
      artifacts/root_cause/data_collector/iter_2/contract/1/0x1c91af03.../source/etherscan_getsourcecode.json.
  2. Attacker-controlled voting to satisfy quorum (tx 0x3c09c630..., block 0xe7f604).

    • Mechanism: governance vote with large delegated stake.
    • Flow:
      • EOA 0xa0c7... calls 0xbdbb..., which forwards to Governance::submitVote(85,2) via the proxy.
      • Governance queries staking and delegation modules:
        DelegateManagerV2::getTotalDelegatorStake(0xbdbb...) and Staking::totalStakedAt(...).
        These calls report an active stake for 0xbdbb... of 10^31 and total stake of 2*10^31, giving 50% participation.
      • With votingQuorumPercent=1 and the attacker casting a Yes vote, governance records a decisive approval for proposal 85.
    • Evidence:
      artifacts/root_cause/data_collector/iter_2/tx/1/0x3c09c630.../trace.cast.log,
      Governance._quorumMet in 0x1c91af03... source.
  3. Proposal execution and AUDIO drain (tx 0x4227bca8..., block 0xe7f607).

    • Mechanism: governance proposal evaluation and arbitrary call execution.
    • Flow:
      • After the configured votingPeriod and executionDelay, EOA 0xa0c7... calls 0xbdbb... to execute proposal 85.
      • Orchestrator forwards to Governance::evaluateProposalOutcome(85) via the proxy.
      • Governance:
        • calls registry.getContract(0x3078...) on 0xbdbb... (as the configured registry), which returns the AudiusToken address 0x18aaa7...,
        • recomputes quorum and majority using staking and delegation modules,
        • calls _executeTransaction(targetContractAddress,0,"transfer(address,uint256)",callData).
      • _executeTransaction builds the calldata for AudiusToken.transfer(0xbdbb...,18564497819999999999735541) and executes it via the proxy’s context.
      • AudiusToken.transfer debits the proxy’s balance and credits 0xbdbb....
    • Evidence:
      artifacts/root_cause/data_collector/iter_2/tx/1/0x4227bca8.../trace.cast.log,
      artifacts/root_cause/seed/1/0x4227bca8.../balance_diff.json.
  4. AUDIO liquidation and ETH profit realization (tx 0x82fc2399..., block 0xe7f608).

    • Mechanism: DEX swap and profit extraction via Uniswap.
    • Flow:
      • Orchestrator 0xbdbb... approves UniswapV2Router at 0x7a250d5630b4cf539739df2c5dAcb4c659F2488D to spend 18,564,497,819.999999999735541 AUDIO.
      • It then performs a swap against the AUDIO/WETH pair at 0xc730ef0f4973da9cc0ab8ab291890d3e77f58f79, sending the entire AUDIO balance from 0xbdbb... to the pool and receiving approximately 704.17 ETH (via WETH), which is forwarded to EOA 0xa0c7....
    • Evidence:
      artifacts/root_cause/seed/1/0x82fc2399.../trace.cast.log,
      artifacts/root_cause/seed/1/0x82fc2399.../balance_diff.json.

6. Impact & Losses

The immediate on-chain impact is the unauthorized transfer of:

  • 18,564,497,819.999999999735541 AUDIO from the Audius treasury proxy at 0x4deca5... to the attacker orchestrator at 0xbdbb....

This transfer is fully confirmed by the erc20_balance_deltas in:

  • artifacts/root_cause/seed/1/0x4227bca8.../balance_diff.json.

The attacker then liquidates the entire AUDIO position for approximately 704.17 ETH, which is sent to EOA 0xa0c7... after accounting for gas and swap details. The precise ETH deltas and fee payments are recorded in:

  • artifacts/root_cause/seed/1/0xfefd829e.../balance_diff.json (setup tx gas),
  • artifacts/root_cause/seed/1/0x4227bca8.../balance_diff.json (execution tx gas),
  • artifacts/root_cause/seed/1/0x82fc2399.../balance_diff.json (swap and final profit).

Beyond the direct financial loss, the exploit demonstrates that the Audius governance and treasury architecture allowed an unprivileged actor to:

  • assume governance control via an uninitialized implementation,
  • configure a malicious registry and guardian,
  • and execute arbitrary treasury transfers under the guise of governance proposals.

This undermines trust in the governance system, exposes the treasury to complete loss if repeated, and affects circulating AUDIO supply and liquidity on the AUDIO/WETH pair at 0xc730ef0f....

7. References

  • Governance implementation source and ABI

    • Governance implementation contract: 0x1c91af03a390b4c619b444425b3119e553b5b44b
    • Source (Etherscan metadata JSON):
      artifacts/root_cause/data_collector/iter_2/contract/1/0x1c91af03a390b4c619b444425b3119e553b5b44b/source/etherscan_getsourcecode.json
  • AudiusToken (AUDIO) source

    • Token contract: 0x18aaa7115705e8be94bffebde57af9bfc265b998
    • Foundry project and ERC20 implementation:
      artifacts/root_cause/seed/1/0x930c746a6e92ca8122682df48cd5020bc1771b32/src/Contract.sol
  • Attacker orchestrator decompiled contract

    • Orchestrator/registry contract: 0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569
    • Decompilation:
      artifacts/root_cause/data_collector/iter_1/contract/1/0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569/decompile/0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569-decompiled.sol
  • Setup and proposal-creation trace

    • Tx 0xfefd829e246002a8fd061eede7501bccb6e244a9aacea0ebceaecef5d877a984 (governance initialization and proposal creation):
      artifacts/root_cause/data_collector/iter_2/tx/1/0xfefd829e246002a8fd061eede7501bccb6e244a9aacea0ebceaecef5d877a984/trace.cast.log
  • Vote trace

    • Tx 0x3c09c6306b67737227edc24c663462d870e7c2bf39e9ab66877a980c900dd5d5 (attacker vote and stake checks):
      artifacts/root_cause/data_collector/iter_2/tx/1/0x3c09c6306b67737227edc24c663462d870e7c2bf39e9ab66877a980c900dd5d5/trace.cast.log
  • Governance execution trace and AUDIO drain balance diffs

    • Tx 0x4227bca8ed4b8915c7eec0e14ad3748a88c4371d4176e716e8007249b9980dc9 (proposal evaluation and AudiusToken.transfer):
      artifacts/root_cause/data_collector/iter_2/tx/1/0x4227bca8ed4b8915c7eec0e14ad3748a88c4371d4176e716e8007249b9980dc9/trace.cast.log
    • Balance diff for AUDIO drain:
      artifacts/root_cause/seed/1/0x4227bca8ed4b8915c7eec0e14ad3748a88c4371d4176e716e8007249b9980dc9/balance_diff.json
  • Profit-taking trace and balance diffs

    • Tx 0x82fc23992c7433fffad0e28a1b8d11211dc4377de83e88088d79f24f4a3f28b3 (AUDIO swap and ETH profit):
      artifacts/root_cause/seed/1/0x82fc23992c7433fffad0e28a1b8d11211dc4377de83e88088d79f24f4a3f28b3/trace.cast.log
    • Balance diff for profit realization:
      artifacts/root_cause/seed/1/0x82fc23992c7433fffad0e28a1b8d11211dc4377de83e88088d79f24f4a3f28b3/balance_diff.json