false
true
0

Contract Address Details

0xEA5AE90a082927a65dd247fC1Df490710cc01c49

Contract Name
Allowed
Creator
0xf6976f–13926a at 0x75c4c0–16b193
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
10 Transactions
Transfers
0 Transfers
Gas Used
24,104,904
Last Balance Update
26035651
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
Allowed




Optimization enabled
false
Compiler version
v0.8.24+commit.e11b9ed9




EVM Version




Verified at
2026-03-13T02:47:22.240218Z

Constructor Arguments


              

src/allowed/Allowed.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.24;

import {IAllowedAdmin} from "./IAllowedAdmin.sol";
import {
    ListAlreadyRegistered,
    ListNotRegistered,
    OnlyListOwner,
    ListOwnerIsZeroAddress,
    OwnableInvalidOwner
} from "./Constants.sol";

/**
 * @title Allowed
 * @notice Multi-list registry with hash-based namespacing.
 *         Public good contract — anyone can register a list with keccak256(namespace).
 *         Each list has its own owner with 2-step ownership transfer.
 *
 * Design:
 *   - List IDs are bytes32 hashes (e.g., keccak256("Name of List"))
 *   - Registration is open — anyone can register any hash with a specified owner
 *   - Each list owner can add/remove addresses from their list only
 *   - 2-step ownership transfer pattern (initiate → accept) for safety
 *   - No global admin — fully decentralized per-list management
 *
 * Use cases:
 *   - Token distribution allowed lists
 *   - Access control lists for DeFi protocols
 *   - Airdrop eligibility tracking
 *   - Any scenario requiring isolated, owner-managed address sets
 */
contract Allowed is IAllowedAdmin {
    // ──────────────────────────────────────────────
    // Storage
    // ──────────────────────────────────────────────

    /// @dev listId => account => isListed
    mapping(bytes32 => mapping(address => bool)) private _listed;

    /// @dev listId => owner address
    mapping(bytes32 => address) private _listOwners;

    /// @dev listId => pending owner address (for 2-step transfer)
    mapping(bytes32 => address) private _pendingListOwners;

    // ──────────────────────────────────────────────
    // Constructor
    // ──────────────────────────────────────────────

    /// @dev Empty constructor — all lists are registered post-deploy
    constructor() {}

    // ──────────────────────────────────────────────
    // Registration
    // ──────────────────────────────────────────────

    /**
     * @notice Registers a new list with the specified owner.
     *         Anyone can register any hash. First-come, first-served.
     * @param listId The keccak256 hash identifying this list
     * @param owner The address that will own and manage this list
     */
    function registerList(bytes32 listId, address owner) external {
        if (_listOwners[listId] != address(0)) {
            revert ListAlreadyRegistered(listId);
        }
        if (owner == address(0)) {
            revert ListOwnerIsZeroAddress();
        }

        _listOwners[listId] = owner;
        emit ListRegistered({listId: listId, owner: owner});
    }

    // ──────────────────────────────────────────────
    // List queries
    // ──────────────────────────────────────────────

    /**
     * @notice Returns whether the given address is listed in the specified list.
     * @param listId The list to check
     * @param account The address to check
     * @return True if listed, false otherwise
     */
    function isListed(bytes32 listId, address account) external view returns (bool) {
        return _listed[listId][account];
    }

    /**
     * @notice Returns the owner of the specified list.
     * @param listId The list to query
     * @return The owner address (zero if not registered)
     */
    function getListOwner(bytes32 listId) external view returns (address) {
        return _listOwners[listId];
    }

    /**
     * @notice Returns the pending owner of the specified list (2-step transfer).
     * @param listId The list to query
     * @return The pending owner address (zero if no pending transfer)
     */
    function getPendingListOwner(bytes32 listId) external view returns (address) {
        return _pendingListOwners[listId];
    }

    // ──────────────────────────────────────────────
    // List management (owner only)
    // ──────────────────────────────────────────────

    /**
     * @notice Sets the listed status for a given address. Owner only.
     * @param listId The list to modify
     * @param account The address to update
     * @param status The new listed status
     */
    function setListed(bytes32 listId, address account, bool status) external {
        _checkCallerIsOwner(listId);
        _listed[listId][account] = status;
        emit ListUpdated({listId: listId, account: account, status: status});
    }

    /**
     * @notice Sets the listed status for multiple addresses at once. Owner only.
     * @param listId The list to modify
     * @param accounts Array of addresses to update
     * @param status Listed status to apply to all addresses
     * @dev For large lists, may need to be called multiple times in batches to avoid gas limits.
     *      Gas cost: ~26,000 per address. At 30M gas limit, max ~1,150 addresses per transaction.
     *      Recommended batch size: 300-500 addresses per transaction.
     */
    function setListedBatch(bytes32 listId, address[] calldata accounts, bool status) external {
        _checkCallerIsOwner(listId);
        uint256 length = accounts.length;
        for (uint256 i; i < length;) {
            _listed[listId][accounts[i]] = status;
            emit ListUpdated({listId: listId, account: accounts[i], status: status});
            unchecked { ++i; }
        }
    }

    // ──────────────────────────────────────────────
    // Ownership management (2-step transfer)
    // ──────────────────────────────────────────────

    /**
     * @notice Initiates ownership transfer for a list. Current owner only.
     *         The new owner must call `acceptListOwnership` to complete the transfer.
     * @param listId The list to transfer
     * @param newOwner The address to transfer ownership to
     */
    function transferListOwnership(bytes32 listId, address newOwner) external {
        _checkCallerIsOwner(listId);
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }

        _pendingListOwners[listId] = newOwner;
        emit ListOwnershipTransferStarted({listId: listId, previousOwner: msg.sender, newOwner: newOwner});
    }

    /**
     * @notice Completes ownership transfer by accepting as the pending owner.
     * @param listId The list to accept ownership of
     */
    function acceptListOwnership(bytes32 listId) external {
        address pendingOwner = _pendingListOwners[listId];
        if (msg.sender != pendingOwner) {
            revert OwnableInvalidOwner(msg.sender);
        }

        address previousOwner = _listOwners[listId];
        delete _pendingListOwners[listId];
        _listOwners[listId] = msg.sender;

        emit ListOwnershipTransferred({listId: listId, previousOwner: previousOwner, newOwner: msg.sender});
    }

    /**
     * @notice Cancels a pending ownership transfer. Current owner only.
     * @param listId The list to cancel transfer for
     */
    function cancelListOwnershipTransfer(bytes32 listId) external {
        _checkCallerIsOwner(listId);
        delete _pendingListOwners[listId];
    }

    // ──────────────────────────────────────────────
    // Internal helpers
    // ──────────────────────────────────────────────

    /**
     * @dev Reverts if msg.sender is not the owner of the list.
     */
    function _checkCallerIsOwner(bytes32 listId) private view {
        address owner = _listOwners[listId];
        if (owner == address(0)) {
            revert ListNotRegistered(listId);
        }
        if (msg.sender != owner) {
            revert OnlyListOwner(listId, msg.sender);
        }
    }
}
        

src/allowed/IAllowed.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.24;

/**
 * @title IAllowed
 * @notice Client interface for the Allowed multi-list registry.
 *         Consumers (like ProveX) should reference this interface to query list status.
 *         This interface contains only read operations — no admin functions.
 *
 * Design rationale:
 *   - Minimal surface area for client contracts
 *   - No exposure to admin operations (ownership, list management)
 *   - Clients cannot accidentally call admin functions
 *   - Clear separation of concerns: query vs. management
 */
interface IAllowed {
    /**
     * @notice Returns whether the given address is listed in the specified list.
     * @param listId The list to check
     * @param account The address to check
     * @return True if listed, false otherwise
     */
    function isListed(bytes32 listId, address account) external view returns (bool);

    /**
     * @notice Returns the owner of the specified list.
     * @param listId The list to query
     * @return The owner address (zero if not registered)
     */
    function getListOwner(bytes32 listId) external view returns (address);

    /**
     * @notice Returns the pending owner of the specified list (2-step transfer).
     * @param listId The list to query
     * @return The pending owner address (zero if no pending transfer)
     */
    function getPendingListOwner(bytes32 listId) external view returns (address);
}
          

src/allowed/IAllowedAdmin.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.24;

import {IAllowed} from "./IAllowed.sol";

/**
 * @title IAllowedAdmin
 * @notice Complete admin interface for the Allowed multi-list registry.
 *         Extends IAllowed with registration, list management, and ownership operations.
 *         List owners and operators should reference this interface.
 *
 * Design rationale:
 *   - Extends IAllowed (clients get read-only, admins get full control)
 *   - Exposes all registry operations (registration, list updates, ownership)
 *   - Single source of truth for the complete Allowed API
 *   - Enforces interface compliance on the Allowed implementation
 */
interface IAllowedAdmin is IAllowed {
    // ──────────────────────────────────────────────
    // Events
    // ──────────────────────────────────────────────

    /// @dev Emitted when a new list is registered.
    event ListRegistered(bytes32 indexed listId, address indexed owner);

    /// @dev Emitted when list ownership transfer is initiated (2-step).
    event ListOwnershipTransferStarted(bytes32 indexed listId, address indexed previousOwner, address indexed newOwner);

    /// @dev Emitted when list ownership transfer is completed.
    event ListOwnershipTransferred(bytes32 indexed listId, address indexed previousOwner, address indexed newOwner);

    /// @dev Emitted when a list entry is updated.
    event ListUpdated(bytes32 indexed listId, address indexed account, bool status);

    // ──────────────────────────────────────────────
    // Registration
    // ──────────────────────────────────────────────

    /**
     * @notice Registers a new list with the specified owner.
     *         Anyone can register any hash. First-come, first-served.
     * @param listId The keccak256 hash identifying this list
     * @param owner The address that will own and manage this list
     */
    function registerList(bytes32 listId, address owner) external;

    // ──────────────────────────────────────────────
    // List management (owner only)
    // ──────────────────────────────────────────────

    /**
     * @notice Sets the listed status for a given address. Owner only.
     * @param listId The list to modify
     * @param account The address to update
     * @param status The new listed status
     */
    function setListed(bytes32 listId, address account, bool status) external;

    /**
     * @notice Sets the listed status for multiple addresses at once. Owner only.
     * @param listId The list to modify
     * @param accounts Array of addresses to update
     * @param status Listed status to apply to all addresses
     */
    function setListedBatch(bytes32 listId, address[] calldata accounts, bool status) external;

    // ──────────────────────────────────────────────
    // Ownership management (2-step transfer)
    // ──────────────────────────────────────────────

    /**
     * @notice Initiates ownership transfer for a list. Current owner only.
     *         The new owner must call `acceptListOwnership` to complete the transfer.
     * @param listId The list to transfer
     * @param newOwner The address to transfer ownership to
     */
    function transferListOwnership(bytes32 listId, address newOwner) external;

    /**
     * @notice Completes ownership transfer by accepting as the pending owner.
     * @param listId The list to accept ownership of
     */
    function acceptListOwnership(bytes32 listId) external;

    /**
     * @notice Cancels a pending ownership transfer. Current owner only.
     * @param listId The list to cancel transfer for
     */
    function cancelListOwnershipTransfer(bytes32 listId) external;
}
          

src/allowed/Constants.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.24;

// ──────────────────────────────────────────────
// Custom errors — Allowed registry only
// ──────────────────────────────────────────────

/// @dev List with this ID is already registered.
error ListAlreadyRegistered(bytes32 listId);

/// @dev List with this ID is not registered.
error ListNotRegistered(bytes32 listId);

/// @dev Caller is not the owner of this list.
error OnlyListOwner(bytes32 listId, address caller);

/// @dev List owner address is zero.
error ListOwnerIsZeroAddress();

/// @dev Invalid owner address for ownership transfer.
error OwnableInvalidOwner(address owner);
          

Compiler Settings

{"viaIR":true,"remappings":["forge-std/=lib/forge-std/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"cancun"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"error","name":"ListAlreadyRegistered","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"}]},{"type":"error","name":"ListNotRegistered","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"}]},{"type":"error","name":"ListOwnerIsZeroAddress","inputs":[]},{"type":"error","name":"OnlyListOwner","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"},{"type":"address","name":"caller","internalType":"address"}]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"event","name":"ListOwnershipTransferStarted","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32","indexed":true},{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ListOwnershipTransferred","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32","indexed":true},{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ListRegistered","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ListUpdated","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"bool","name":"status","internalType":"bool","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"acceptListOwnership","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelListOwnershipTransfer","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getListOwner","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getPendingListOwner","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isListed","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registerList","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setListed","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setListedBatch","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"},{"type":"address[]","name":"accounts","internalType":"address[]"},{"type":"bool","name":"status","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferListOwnership","inputs":[{"type":"bytes32","name":"listId","internalType":"bytes32"},{"type":"address","name":"newOwner","internalType":"address"}]}]
              

Contract Creation Code

0x60808060405234610016576105b0908161001b8239f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f3560e01c80630c55a58c1461048957806324879bdc1461045a57806329227260146103cb578063432451bb146103425780635cee8c69146102ff57806370bbc61c146102525780638d2cb9381461015a578063bcb907ff146100b15763c18a73211461007f575f80fd5b346100ad5760203660031901126100ad57602091355f526002825260018060a01b03815f2054169051908152f35b5f80fd5b50346100ad57806003193601126100ad578135906100cd6104c0565b5f8381526001602052829020546001600160a01b039190821661014357169283156101355750815f5260016020525f20826001600160601b0360a01b8254161790557faa7b418a1b880af41df3685bd1f32b509f3186ac4ca6ab95c4b922a30d0b2fd65f80a3005b90516339619eb760e01b8152fd5b8251634a99cccd60e11b8152808601859052602490fd5b50346100ad5760603660031901126100ad578135906024359167ffffffffffffffff938484116100ad57366023850112156100ad578301359384116100ad576024830192602436918660051b0101116100ad576101b56104d6565b936101bf8261051d565b841515935f5b8281106101ce57005b600190845f5260205f8152857fe06b67235de399be357719c701038d0e4b97839bb2add8576c835e684c63bd5a885f20858060a01b039081610219610214888c8c6104e5565b610509565b165f5284526102368c8b5f209060ff801983541691151516179055565b610244610214868a8a6104e5565b169289518b8152a3016101c5565b50346100ad5760203660031901126100ad5781355f81815260026020528290205490926001600160a01b0391821633036102e85750825f526001602052815f205416906002602052805f20906001600160601b0360a01b9182815416905560016020525f2090339082541617905533917fdd39129cb9b7dd599250295414a3358ff23bbdd042f94c752078660daea70fd15f80a4005b602490835190631e4fbdf760e01b82523390820152fd5b50346100ad57806003193601126100ad5760209161031b6104c0565b90355f525f8352815f209060018060a01b03165f52825260ff815f20541690519015158152f35b5090346100ad5760603660031901126100ad57357fe06b67235de399be357719c701038d0e4b97839bb2add8576c835e684c63bd5a60206103816104c0565b9361038a6104d6565b906103948561051d565b5f8581528084528181206001600160a01b0397909716808252968452819020805460ff191660ff84151516179055519015158152a3005b50346100ad57806003193601126100ad578135906103e76104c0565b6103f08361051d565b6001600160a01b03169283156104455750815f5260026020525f20826001600160601b0360a01b82541617905533907fc924d854d2ee1ca8704e17e607219243ce1340f8b88735ca9a8fa73d56e681e15f80a4005b5f6024925191631e4fbdf760e01b8352820152fd5b50346100ad5760203660031901126100ad57602091355f526001825260018060a01b03815f2054169051908152f35b5090346100ad5760203660031901126100ad57356104a68161051d565b5f90815260026020522080546001600160a01b0319169055005b602435906001600160a01b03821682036100ad57565b6044359081151582036100ad57565b91908110156104f55760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036100ad5790565b5f818152600160205260409020546001600160a01b031680156105615733036105435750565b60449060405190630e7bb12160e01b82526004820152336024820152fd5b604051638b22cf9960e01b815260048101839052602490fdfea26469706673582212200b34c67dba21c6390122b83fd51bcd8672ca1a5072b749a5bdb8f925a0363d5864736f6c63430008180033

Deployed ByteCode

0x60406080815260049081361015610014575f80fd5b5f3560e01c80630c55a58c1461048957806324879bdc1461045a57806329227260146103cb578063432451bb146103425780635cee8c69146102ff57806370bbc61c146102525780638d2cb9381461015a578063bcb907ff146100b15763c18a73211461007f575f80fd5b346100ad5760203660031901126100ad57602091355f526002825260018060a01b03815f2054169051908152f35b5f80fd5b50346100ad57806003193601126100ad578135906100cd6104c0565b5f8381526001602052829020546001600160a01b039190821661014357169283156101355750815f5260016020525f20826001600160601b0360a01b8254161790557faa7b418a1b880af41df3685bd1f32b509f3186ac4ca6ab95c4b922a30d0b2fd65f80a3005b90516339619eb760e01b8152fd5b8251634a99cccd60e11b8152808601859052602490fd5b50346100ad5760603660031901126100ad578135906024359167ffffffffffffffff938484116100ad57366023850112156100ad578301359384116100ad576024830192602436918660051b0101116100ad576101b56104d6565b936101bf8261051d565b841515935f5b8281106101ce57005b600190845f5260205f8152857fe06b67235de399be357719c701038d0e4b97839bb2add8576c835e684c63bd5a885f20858060a01b039081610219610214888c8c6104e5565b610509565b165f5284526102368c8b5f209060ff801983541691151516179055565b610244610214868a8a6104e5565b169289518b8152a3016101c5565b50346100ad5760203660031901126100ad5781355f81815260026020528290205490926001600160a01b0391821633036102e85750825f526001602052815f205416906002602052805f20906001600160601b0360a01b9182815416905560016020525f2090339082541617905533917fdd39129cb9b7dd599250295414a3358ff23bbdd042f94c752078660daea70fd15f80a4005b602490835190631e4fbdf760e01b82523390820152fd5b50346100ad57806003193601126100ad5760209161031b6104c0565b90355f525f8352815f209060018060a01b03165f52825260ff815f20541690519015158152f35b5090346100ad5760603660031901126100ad57357fe06b67235de399be357719c701038d0e4b97839bb2add8576c835e684c63bd5a60206103816104c0565b9361038a6104d6565b906103948561051d565b5f8581528084528181206001600160a01b0397909716808252968452819020805460ff191660ff84151516179055519015158152a3005b50346100ad57806003193601126100ad578135906103e76104c0565b6103f08361051d565b6001600160a01b03169283156104455750815f5260026020525f20826001600160601b0360a01b82541617905533907fc924d854d2ee1ca8704e17e607219243ce1340f8b88735ca9a8fa73d56e681e15f80a4005b5f6024925191631e4fbdf760e01b8352820152fd5b50346100ad5760203660031901126100ad57602091355f526001825260018060a01b03815f2054169051908152f35b5090346100ad5760203660031901126100ad57356104a68161051d565b5f90815260026020522080546001600160a01b0319169055005b602435906001600160a01b03821682036100ad57565b6044359081151582036100ad57565b91908110156104f55760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036100ad5790565b5f818152600160205260409020546001600160a01b031680156105615733036105435750565b60449060405190630e7bb12160e01b82526004820152336024820152fd5b604051638b22cf9960e01b815260048101839052602490fdfea26469706673582212200b34c67dba21c6390122b83fd51bcd8672ca1a5072b749a5bdb8f925a0363d5864736f6c63430008180033