Skip to content

MultiOwnable

Overview

MultiOwnable is an abstract contract providing multi-owner access control. Unlike OpenZeppelin's Ownable which has a single owner, MultiOwnable allows multiple addresses to have owner privileges simultaneously.

  • License: Sharia
  • Solidity: ^0.8.21
  • Type: Abstract Contract

What This Means For Players

Plain English Summary: MultiOwnable allows shared control - it lets multiple people manage the same thing. Instead of having just one admin, a contract can have many admins who all have equal power. This is essential for the Dysnomia ecosystem where game contracts need to interact with each other.

Real-World Analogy: Think of a shared bank account that has multiple signatories. Any of the signatories can make transactions - you don't need everyone to approve. MultiOwnable works the same way: if you're on the list of owners, you can control the contract.

How It Affects Your Gameplay: - Shared access - Your LAU, SHIO, and other tokens may have multiple owners (you + game contracts) - Chain of trust - Game contracts add each other as owners to enable coordination - Revocable - Owners can be added or removed by existing owners

State Variables

Variable Type Visibility Description
_owners mapping(address => bool) private Tracks owner status per address

Read Functions

owner (address check)

function owner(address cOwner) public view virtual returns (bool)
- Access: public view - Parameters: - cOwner (address): Address to check - Returns: - bool: True if address is in the _owners mapping - Description: Checks if an address has owner privileges. - Logic Flow: 1. Return _owners[cOwner] - In Plain English: Check if an address has admin rights on this contract. Returns true if they're on the owners list.

owner (contract address)

function owner() external view virtual returns (address)
- Access: external view - Returns: - address: Returns address(this) (the contract itself) - Description: Compatibility function that returns the contract's own address for owner checks. - In Plain English: Returns the contract's address for compatibility with code that expects a single owner.

Write Functions

addOwner

function addOwner(address newOwner) public virtual onlyOwners
- Access: onlyOwners - Parameters: - newOwner (address): Address to add as owner - Description: Adds a new address to the owners set. - Logic Flow: 1. Validate: if(newOwner == address(0)) revert OwnableInvalidOwner 2. Call: _changeOwnership(newOwner, true) - Side Effects: Updates _owners mapping; emits OwnershipUpdate event - In Plain English: Give admin powers to a new address. Once added, they can manage the contract just like existing owners. Cannot add the zero address.

renounceOwnership

function renounceOwnership(address toRemove) public virtual onlyOwners
- Access: onlyOwners - Parameters: - toRemove (address): Address to remove from owners - Description: Removes an address from the owners set. - Logic Flow: 1. Call: _changeOwnership(toRemove, false) - Side Effects: Updates _owners mapping; emits OwnershipUpdate event - In Plain English: Remove admin powers from an address. Any owner can remove any other owner (or themselves) from the list.

Modifier

onlyOwners

modifier onlyOwners() {
    _checkOwner();
    _;
}
- Description: Restricts function access to owners only - Checks: Both msg.sender AND tx.origin - allows if either is an owner

Events

Event Parameters Description
OwnershipUpdate newOwner (indexed), state (indexed) Emitted when ownership changes

Errors

Error Parameters Description
OwnableUnauthorizedAccount origin, account, what Caller is not an owner
OwnableInvalidOwner origin, owner, what Invalid owner address (zero)

Internal Functions

_checkOwner

function _checkOwner() internal view virtual
- Description: Verifies msg.sender or tx.origin is an owner, reverts otherwise

_changeOwnership

function _changeOwnership(address cOwner, bool cState) internal virtual
- Parameters: - cOwner: Address to modify - cState: New ownership state (true = owner, false = not owner) - Description: Internal function to modify ownership mapping

Contract Interactions

Depended On By

  • DYSNOMIA - All tokens inherit this
  • Every contract in the ecosystem

Special Mechanisms

Dual Sender Check

The _checkOwner function checks both:

if (!owner(msg.sender) && !owner(tx.origin)) {
    revert OwnableUnauthorizedAccount(...);
}

This allows: - Direct calls from owner addresses - Calls through contracts where tx.origin is an owner - Factory patterns where the deploying contract is trusted

Initial Owner

Constructor requires a non-zero initial owner:

constructor(address initialOwner) {
    if (initialOwner == address(0)) {
        revert OwnableInvalidOwner(...);
    }
    _changeOwnership(initialOwner, true);
}

No Enumeration

Unlike some implementations, owners cannot be enumerated. You can only check if a specific address is an owner. This is a gas optimization trade-off.

Usage Pattern

contract MyContract is MultiOwnable {
    constructor() MultiOwnable(msg.sender) {}

    function adminFunction() public onlyOwners {
        // Only owners can call this
    }

    function addAdmin(address newAdmin) public onlyOwners {
        addOwner(newAdmin);
    }
}

Security Considerations

  • Any owner can add new owners
  • Any owner can remove any owner (including themselves)
  • No protection against removing all owners
  • tx.origin check enables some use cases but has known security implications
  • Consider carefully before using in high-security contexts

Contract Verification

Property Value
Keccak256 Hash 0xd5bc09d51ea952718d51fc0c347aad20278f6a1c7a0fda63e40ea9317fbd7720
Source URL https://raw.githubusercontent.com/busytoby/atropa_pulsechain/main/solidity/dysnomia/lib/multiownable.sol
Hash Generated 2026-02-08T00:29:08Z