Skip to content

Storage Layout

DISCLAIMER // NFA // DYOR

This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.

⊙ generated by robots | curated by humans

METADATA
Contract Address 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E (etherscan)
Network Ethereum Mainnet
Analysis Date 2026-01-13

Variables

SLOT VARIABLE NAME TYPE DEFAULT VALUE PURPOSE
0x00 sentToken address 0xe88BAab9192a3Cb2C0a50182AB911506e5aDc304 SENT token contract for balance checks
0x01 hiveContract address TBD Hive eligibility verification contract
0x02 participants mapping(address => Participant) - Main participant data storage
0x03 registeredAddresses address[] [] Array of all registered addresses
0x04 isRegistered mapping(address => bool) - Quick lookup for registration status
0x05 participantHistory mapping(address => HistoryEntry[]) - Historical records per participant
0x06 totalUniqueParticipants uint256 0 Count of unique addresses ever registered
0x07 totalRegistrations uint256 0 Total registration events (includes re-registrations)
0x08 totalActiveMembers uint256 0 Current count of active members
0x09 totalHistoricalMembers uint256 0 Count of members who were once active
0x0a minimumBalanceThreshold uint256 1000000000000000000000 Minimum SENT balance (1000 SENT = 1e21 wei)
0x0b balanceCheckPercentage uint256 95 Percentage of minimum for daily checks
0x0c lastDailyCheckTime uint256 0 Timestamp of last daily check execution
0x0d cooldownPeriod uint256 604800 Cooldown in seconds (default 7 days)
0x0e registrationOpen bool true Registration enabled/disabled flag

EIP-1967 Standard Slots

SLOT VARIABLE NAME TYPE PURPOSE
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc implementation address Current implementation contract address
0x9016d09d72d2aeb2b285fb8228a6fb9957a1e32e0356c0b8a96f91c58e71ce87 admin/owner address Contract owner address
0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00 initializer + reentrancy bytes32 Packed: initializer flag + reentrancy guard status

Struct Definitions

Participant

struct Participant {
    uint256 registeredAt;        // Timestamp of initial registration
    uint256 lastCheckAt;         // Timestamp of last balance check
    bool isActive;               // Current active membership status
    uint256 reRegistrationCount; // Number of times re-registered after lapse
}

Storage location: Slot 0x02 (mapping base)

HistoryEntry

struct HistoryEntry {
    uint256 timestamp;    // When the event occurred
    bool wasActive;       // Status at this point in time
    uint8 eventType;      // Type of event (registered, deactivated, re-registered)
}

Storage location: Slot 0x05 (mapping base)


Diagrams

Core Storage Layout

graph TB
    subgraph External["External Dependencies - Slots 0-1"]
        S0["Slot 0: sentToken<br/>0xe88BAab9192a3Cb2C0a50182AB911506e5aDc304"]
        S1["Slot 1: hiveContract<br/>Address TBD"]
    end

    subgraph Mappings["Data Structures - Slots 2-5"]
        S2["Slot 2: participants<br/>mapping(address => Participant)"]
        S3["Slot 3: registeredAddresses<br/>address[] dynamic array"]
        S4["Slot 4: isRegistered<br/>mapping(address => bool)"]
        S5["Slot 5: participantHistory<br/>mapping(address => HistoryEntry[])"]
    end

    subgraph Counters["Statistics - Slots 6-9"]
        S6["Slot 6: totalUniqueParticipants<br/>0"]
        S7["Slot 7: totalRegistrations<br/>0"]
        S8["Slot 8: totalActiveMembers<br/>0"]
        S9["Slot 9: totalHistoricalMembers<br/>0"]
    end

    subgraph Config["Configuration - Slots 10-14"]
        S10["Slot 10: minimumBalanceThreshold<br/>1000 SENT (1e21 wei)"]
        S11["Slot 11: balanceCheckPercentage<br/>95"]
        S12["Slot 12: lastDailyCheckTime<br/>0"]
        S13["Slot 13: cooldownPeriod<br/>604800 (7 days)"]
        S14["Slot 14: registrationOpen<br/>true"]
    end

    subgraph Proxy["EIP-1967 Proxy Slots"]
        P1["0x360894a1...: implementation<br/>0xc3431410ff157c3971b9160c1f80da51b787e374"]
        P2["0x9016d09d...: admin/owner<br/>0x9fBcc72A6bc74D0060e14Fe8b8f4b7CcFA63eA03"]
        P3["0xf0c57e16...: initializer + reentrancy<br/>Packed flags"]
    end

    External --> Mappings
    Mappings --> Counters
    Counters --> Config
    Config -.-> Proxy

    style S0 fill:#e1f5ff
    style S1 fill:#e1f5ff
    style S3 fill:#fff4e1
    style S6 fill:#e1ffe1
    style S7 fill:#e1ffe1
    style S8 fill:#e1ffe1
    style S9 fill:#e1ffe1
    style P1 fill:#ffe1e1
    style P2 fill:#ffe1e1

Mapping Storage Calculation

EVM mappings use keccak256 hashing to determine storage locations for each key-value pair.

graph LR
    subgraph Participants["participants mapping (slot 2)"]
        Key1["address: 0x1234..."] --> Hash1["keccak256(0x1234... || 0x02)"]
        Hash1 --> Loc1["Storage location for Participant struct"]
    end

    subgraph IsRegistered["isRegistered mapping (slot 4)"]
        Key2["address: 0x1234..."] --> Hash2["keccak256(0x1234... || 0x04)"]
        Hash2 --> Loc2["Storage location for bool value"]
    end

    subgraph History["participantHistory mapping (slot 5)"]
        Key3["address: 0x1234..."] --> Hash3["keccak256(0x1234... || 0x05)"]
        Hash3 --> Loc3["Storage location for HistoryEntry[] array"]
        Loc3 --> Hash4["keccak256(arrayLocation)"]
        Hash4 --> Loc4["Location of array elements"]
    end

    style Loc1 fill:#e1ffe1
    style Loc2 fill:#e1ffe1
    style Loc4 fill:#e1ffe1

Array Storage Layout

The registeredAddresses dynamic array uses two storage locations: the slot itself stores the array length, and array elements are stored sequentially starting at keccak256(slot).

graph TB
    subgraph Slot3["Slot 0x03: Array Length"]
        Length["registeredAddresses.length<br/>Current: variable"]
    end

    subgraph Elements["Array Elements"]
        Base["Base location:<br/>keccak256(0x03)"]
        E0["Element [0]:<br/>base + 0"]
        E1["Element [1]:<br/>base + 1"]
        E2["Element [2]:<br/>base + 2"]
        EN["Element [n]:<br/>base + n"]
    end

    Length --> Base
    Base --> E0
    E0 --> E1
    E1 --> E2
    E2 --> EN

    style Length fill:#fff4e1
    style E0 fill:#e1ffe1
    style E1 fill:#e1ffe1
    style E2 fill:#e1ffe1
    style EN fill:#e1ffe1

Storage Access Patterns

Read Operations

High Frequency (called in every daily check iteration):
- Slot 0x00: sentToken - read to call balanceOf()
- Slot 0x02: participants[addr] - check isActive status
- Slot 0x0a: minimumBalanceThreshold - calculate required balance
- Slot 0x0b: balanceCheckPercentage - calculate required balance

Medium Frequency (called on user actions):
- Slot 0x04: isRegistered[addr] - verify registration status
- Slot 0x01: hiveContract - read for eligibility checks
- Slot 0x0e: registrationOpen - verify registration is enabled

Low Frequency (admin or statistics):
- Slots 0x06-0x09: Global counters for statistics
- Slot 0x0c: lastDailyCheckTime - timestamp tracking
- Slot 0x0d: cooldownPeriod - configuration value

Write Operations

Registration Flow:
1. Write to isRegistered[msg.sender] (slot 0x04)
2. Append to registeredAddresses array (slot 0x03)
3. Create participants[msg.sender] struct (slot 0x02)
4. Increment counters (slots 0x06, 0x07, 0x08)

Daily Check Flow:
1. Read registeredAddresses array (slot 0x03)
2. For each address:
- Read participants[addr] (slot 0x02)
- Potentially write status change to participants[addr]
- Potentially append to participantHistory[addr] (slot 0x05)
- Potentially decrement totalActiveMembers (slot 0x08)
3. Write lastDailyCheckTime (slot 0x0c)

Configuration Updates:
- Owner can modify slots 0x0a, 0x0b, 0x0d, 0x0e
- Owner can modify slots 0x00, 0x01 (external contracts)
- Owner can modify EIP-1967 implementation slot (upgrades)


Storage Risks

Unbounded Growth

VARIABLE RISK MITIGATION
registeredAddresses Array grows indefinitely; eventually may exceed gas limits for full iteration Batch processing with 100-participant cap
participantHistory[addr] Nested array grows with each status change per address Read-only; not iterated in critical paths

Storage Collisions

RISK ASSESSMENT
Proxy-Implementation Collision ☑ Uses EIP-1967 standard slots; low collision risk
Mapping Key Collision ☑ Keccak256 preimage resistance prevents intentional collisions
Upgrade Storage Layout Changes △ Owner can upgrade without layout validation; could corrupt state

Access Control

VARIABLE WRITE ACCESS RISK
sentToken (slot 0x00) Owner only △ Owner can swap dependency
hiveContract (slot 0x01) Owner only △ Owner can swap dependency
registrationOpen (slot 0x0e) Owner only △ Owner can disable registration
Configuration (slots 0x0a-0x0d) Owner only △ Owner can change requirements
Implementation (EIP-1967) Owner only △ Owner can upgrade logic

Verification Commands

Tip

Commands below use cast from the Foundry Toolkit. To run the commands below, you must set the RPC URL environment variable:

export ETH_RPC_URL=https://eth.llamarpc.com

Read Storage Slots

# READ SENT TOKEN ADDRESS (SLOT 0)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 0

# READ HIVE CONTRACT ADDRESS (SLOT 1)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 1

# READ REGISTERED ADDRESSES ARRAY LENGTH (SLOT 3)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 3

# READ TOTAL UNIQUE PARTICIPANTS (SLOT 6)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 6

# READ TOTAL REGISTRATIONS (SLOT 7)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 7

# READ TOTAL ACTIVE MEMBERS (SLOT 8)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 8

# READ MINIMUM BALANCE THRESHOLD (SLOT 10)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 10

# READ BALANCE CHECK PERCENTAGE (SLOT 11)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 11

# READ COOLDOWN PERIOD (SLOT 13)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 13

# READ REGISTRATION OPEN FLAG (SLOT 14)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 14

# READ EIP-1967 IMPLEMENTATION SLOT
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

# READ EIP-1967 ADMIN/OWNER SLOT
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 0x9016d09d72d2aeb2b285fb8228a6fb9957a1e32e0356c0b8a96f91c58e71ce87

Calculate Mapping Storage Locations

# CALCULATE STORAGE LOCATION FOR participants[address]
# Formula: keccak256(abi.encode(address, slot))
cast index address 0x<USER_ADDRESS> 2

# CALCULATE STORAGE LOCATION FOR isRegistered[address]
cast index address 0x<USER_ADDRESS> 4

# CALCULATE STORAGE LOCATION FOR participantHistory[address]
cast index address 0x<USER_ADDRESS> 5

Calculate Array Element Locations

# CALCULATE BASE LOCATION FOR registeredAddresses ARRAY ELEMENTS
# Formula: keccak256(slot)
cast keccak 0x0000000000000000000000000000000000000000000000000000000000000003

# READ FIRST ELEMENT (base + 0)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E 0x<BASE_LOCATION>

# READ SECOND ELEMENT (base + 1)
cast storage 0x93Ad33AC2d4cbA339389F42D9345Db3B34174c9E $(cast --to-hex $(cast --to-dec 0x<BASE_LOCATION> + 1))

Storage Invariants

These conditions should always hold true if the contract is functioning correctly:

  1. Registration Consistency: If isRegistered[addr] == true, then addr must exist in registeredAddresses array.

  2. Active Member Subset: totalActiveMembers <= totalUniqueParticipants (active members are a subset of all registered addresses).

  3. Registration Count: totalRegistrations >= totalUniqueParticipants (total registrations includes re-registrations).

  4. Array Length Match: registeredAddresses.length == totalUniqueParticipants (each unique participant appears once in array).

  5. Configuration Bounds:
    - minimumBalanceThreshold > 0
    - 1 <= balanceCheckPercentage <= 100
    - cooldownPeriod <= 2592000 (30 days max)

  6. Active Status Consistency: If participants[addr].isActive == true, then isRegistered[addr] == true.

  7. EIP-1967 Implementation: Implementation slot must point to a contract with proxiableUUID() function.

  8. EIP-1967 Admin: Admin slot must contain a valid address (or address(0) if renounced).