MultiOwnable — Shared Control
One-line pitch: MultiOwnable lets a contract have many owners instead of just one. Every owner has equal admin power. This is the pattern every token in Dysnomia inherits.
What this is
Section titled “What this is”Standard OpenZeppelin Ownable has one owner. MultiOwnable flips that to a set of authorized addresses. The onlyOwners modifier gates any privileged function; any address in _owners passes.
This isn’t just a convenience — the whole Dysnomia system depends on contracts being able to add each other as owners. That’s how LAU gives VOID permission to mint for it, how WAR gets permission to mint H2O, and so on.
If you’ve played a web3 game before
Section titled “If you’ve played a web3 game before”- Closest analogy: a joint bank account — any signer can transact.
- Or: OpenZeppelin’s AccessControl (a role-based extension with multiple admins).
- Or: a Safe with 1-of-N threshold — any owner can solo-sign.
| Dysnomia term | What it maps to |
|---|---|
| MultiOwnable | Base class for “set of owners” |
| _owners | The mapping address → bool |
| onlyOwners | Modifier — reverts unless caller is in _owners |
| OwnershipUpdate | Event fired when owners are added/removed |
| owner(address) | Tests one address against the owner mapping |
| owner() | Returns the contract’s own address; it does not enumerate or identify a current owner |
What you actually do with it
Section titled “What you actually do with it”- Understand it’s there — every token you interact with is MultiOwnable.
- Don’t give ownership to random addresses — an owner has total power over that token.
- Normal actions (transfer, approve, Purchase/Redeem) don’t require ownership; only admin-level operations do.
Use these calls here. Simulations preview the current state; each confirmed call is a separate transaction.
LAU.owner(address cOwner)Read
Open this function to load its call form.
QING.owner(address cOwner)Read
Open this function to load its call form.
Rewards / costs
Section titled “Rewards / costs”- Cost: none directly.
- Reward: none — it’s infrastructure.
Requirements & gating
Section titled “Requirements & gating”- To add/remove an owner, you must already be an owner.
onlyOwnersaccepts eithermsg.senderortx.originwhen that exact address is in the mapping. Ownership held by a separate LAU does not automatically authorize its wallet on another contract.
Where it connects
Section titled “Where it connects”Inherited by every token in the system — DYSNOMIA, DYSNOMIA V2, and through them, everything else.
Quick FAQ
Section titled “Quick FAQ”Q: Why not just use Ownable? A: Because game contracts need to call privileged functions on each other. If there’s only one owner, you can’t do that without an EOA in the middle.
Q: Can I become a co-owner of a public token? A: Only if an existing owner adds you. In practice, ownership is set up at deployment and rarely modified.
Q: How do I list every owner?
A: Replay OwnershipUpdate events and apply them in order. owner(address) verifies one candidate; owner() always returns the contract address in this implementation.
Q: What if all owners are removed? A: The contract becomes un-admin’d. Most contracts in Dysnomia maintain at least one owner specifically to avoid this bricking scenario.
Want the Solidity? The contract reference lives at
technical/docs/lib/MULTIOWNABLE.md.