Curriculum·G301 Solidity Mental Models·about 33 min
Access control: who is allowed to call this
By the end of this lesson you can
- →Explain that every privileged function needs an explicit, correct check on who may call it
- →Describe how a flawed initialization let an attacker seize Audius governance and move treasury funds
- →Reason that access control is the code that answers 'who,' and a missing or defeatable check answers 'anyone'
- →Identify the privileged functions in a contract and ask what guards each one
Graduate · enrolled learners
This lesson opens with Audius governance takeover, July 2022.
- What happened
- Audius ran its governance on upgradeable contracts, and the code that was supposed to set up and lock down those contracts, the initialization, was flawed: the proxy kept its admin address in storage slot 0, the same slot OpenZeppelin's Initializable used for its own guard, so the guard could be passed and an attacker was able to re-run initialization on the governance contracts on July 23, 2022. With governance re-initialized under their settings and a self-delegation of 10 trillion AUDIO of voting power, they submitted and passed a malicious proposal that moved 18,564,497 AUDIO from the community treasury to their own address, then sold it in one Uniswap trade for about 704 ETH. The contracts were not broken cryptographically and no signing key was stolen; the access control, the code that decides who is allowed to perform privileged actions, was defeatable, so the answer to 'who may control governance' turned out to be 'whoever exploits the initializer,' rather than 'only the project.' The privileged doors existed as intended, but the lock on them could be picked from the code, and once through, the attacker could do everything the project itself could do.
- The decision point
- Access control is the code that answers the question 'who is allowed to call this,' and every function that can move funds, change parameters, mint, upgrade or govern needs an explicit and correct check, because a function without a working guard answers 'anyone.' Audius is the case: its privileged governance role was meant for the project, but a flaw in initialization let an attacker re-run setup and take over governance, and from there they passed a proposal that drained the treasury, because once the guard on 'who' failed, the attacker inherited every privilege the project had. The mental model from the prior lesson, that the machine executes the literal code, applies sharply here: a privileged function is protected only by the check actually written on it, so 'only the owner can call this' is true only if the code verifies the caller is the owner, and a check that can be bypassed, through a re-runnable initializer, an unset variable, a wrong comparison, is a check that is not there. So the discipline is to enumerate every privileged function in a contract and, for each, find the exact code that restricts who may call it, confirm that restriction cannot be re-run or bypassed, and treat any privileged function whose guard is missing, defeatable or merely intended as open to anyone, because access control is not a property a contract has by default; it is code that must be written, correct, and impossible to reset, and Audius is what a privileged door with a pickable lock hands to whoever tries the handle.
- Recorded loss
- $6,000,000
What you will be able to answer
- →How was Audius governance taken over (July 2022)?
- →What does access control answer, and how?
- →What does a privileged function without a working guard permit?
- →How to check a contract's access control
Orientation and Year One are open: anyone can read them without an account. From Year Two onward the lessons are for enrolled learners, because progress through the later years only means anything if it is tracked against a record.
It is free. We do not sell the list and there is nothing to buy at the end of it.
Sources and review
- https://rekt.news/audius-rekt
- https://decrypt.co/105913/how-audius-was-hacked-6m-ethereum-tokens
- https://docs.soliditylang.org/en/latest/contracts.html#function-modifiers
- https://consensysdiligence.github.io/smart-contract-best-practices/attacks/
Confidence high·Volatility low·Reviewed 2026-09-17·Owner unassigned
Contested
The roughly 6 million dollar figure is the notional value of the 18,564,497 AUDIO the attacker moved (Decrypt gives nearly 6.1 million at the time); selling it in one Uniswap trade realized only about 704 ETH, roughly 1.07 million dollars by Decrypt's count, and Audius halted its Ethereum contracts within hours. The lesson uses the access-control mechanism, not a precise loss.
The exact technical root cause, as Rekt summarizes the Audius post-mortem, is a storage collision: the proxy admin address in slot 0 overlapped OpenZeppelin's Initializable storage, so the initializer guard could be passed. This lesson summarizes it as a defeatable guard on a privileged role, which is the transferable point; G303 returns to the storage detail. The Audius blog post-mortem itself no longer loads at its original address, so the figures here come from Rekt and Decrypt, which quote it.
