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:
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:
-
Registration Consistency: If
isRegistered[addr] == true, thenaddrmust exist inregisteredAddressesarray. -
Active Member Subset:
totalActiveMembers <= totalUniqueParticipants(active members are a subset of all registered addresses). -
Registration Count:
totalRegistrations >= totalUniqueParticipants(total registrations includes re-registrations). -
Array Length Match:
registeredAddresses.length == totalUniqueParticipants(each unique participant appears once in array). -
Configuration Bounds:
-minimumBalanceThreshold > 0
-1 <= balanceCheckPercentage <= 100
-cooldownPeriod <= 2592000(30 days max) -
Active Status Consistency: If
participants[addr].isActive == true, thenisRegistered[addr] == true. -
EIP-1967 Implementation: Implementation slot must point to a contract with
proxiableUUID()function. -
EIP-1967 Admin: Admin slot must contain a valid address (or address(0) if renounced).