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 | 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D (etherscan) |
| Network | Ethereum Mainnet |
| Analysis Date | 2026-01-23 |
Storage Slots Overview
graph LR
subgraph "Slot 0"
S0[flexibleAllocation<br/>address 20 bytes]
end
subgraph "Slot 1"
S1[hasClaimed mapping<br/>mapping address => bool<br/>Also: eligibilityStatus]
end
subgraph "Slot 2 Packed"
S2A[owner<br/>address 20 bytes]
S2B[paused<br/>bool 1 byte]
S2C[claimingEnabled<br/>bool 1 byte]
end
S0 --> |Read by| F1[flexibleAllocation]
S0 --> |Modified by| F2[setFlexibleAllocation]
S1 --> |Read by| F3[hasClaimed]
S1 --> |Read by| F4[getEligibilityStatus]
S1 --> |Modified by| F5[claim]
S1 --> |Modified by| F6[setEligibilityStatus]
S2A --> |Read by| F7[owner]
S2A --> |Modified by| F8[transferOwnership]
S2B --> |Read by| F9[paused]
S2B --> |Modified by| F10[pause/unpause]
S2C --> |Read by| F11[claimingEnabled]
S2C --> |Modified by| F12[setClaimingEnabled]
Slot 0: flexibleAllocation
| PROPERTY | VALUE |
|---|---|
| Type | address |
| Size | 20 bytes |
| Initial Value | 0xe6c328360439b91727ec854bf2b6caeeaff5dc66 |
| Access Pattern | Read-heavy, rare writes |
Purpose: Stores the address of the external FlexibleAllocation contract used to verify user allocations during claims.
Functions That Read:
flexibleAllocation()- Returns the addressclaim()- Uses address to callgetAllocation(address)canClaim(address)- Uses address to callgetAllocation(address)
Functions That Modify:
setFlexibleAllocation(address)- Owner-only function to change the allocation contract
Current Value:
$ cast storage 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D 0
0x000000000000000000000000e6c328360439b91727ec854bf2b6caeeaff5dc66
Security Considerations:
- ☒ Owner can change this at any time with no timelock
- △ Changing mid-operation affects active claimers
- △ New contract must implement compatible
getAllocation(address)interface
Slot 1: hasClaimed / eligibilityStatus
| PROPERTY | VALUE |
|---|---|
| Type | mapping(address => bool) |
| Size | Dynamic (keccak256 hash-based addressing) |
| Initial Value | All addresses default to false |
| Access Pattern | Write-once per address (claim), frequent reads |
Purpose: Dual-purpose storage that tracks both:
- Whether an address has claimed (
hasClaimed) - Eligibility status for claiming (
eligibilityStatus)
Functions That Read:
hasClaimed(address)- Returns claim statusgetEligibilityStatus(address)- Returns eligibility status (same data!)claim()- Checks if address has already claimedcanClaim(address)- Checks claim status before allocation check
Functions That Modify:
claim()- Sets totruepermanently after successful claimsetEligibilityStatus(address, bool)- Owner sets eligibilitybatchSetEligibilityStatus(address[], bool[])- Owner sets eligibility for multiple addresses
Mapping Address Calculation:
Example Values:
# Check if address has claimed
$ cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "hasClaimed(address)" <ADDRESS>
# Check eligibility status (reads same storage!)
$ cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "getEligibilityStatus(address)" <ADDRESS>
Security Considerations:
- △ Critical: Same storage used for two purposes—cannot distinguish between "ineligible" and "already claimed"
- △ Setting eligibility to
falsemakes address appear as if it already claimed - △ Claims are permanent—even owner cannot reset
- ☑ Write-once pattern for claims prevents double-claiming
Slot 2: Packed Storage (owner + paused + claimingEnabled)
| PROPERTY | VALUE |
|---|---|
| Type | Packed: address (20 bytes) + bool (1 byte) + bool (1 byte) |
| Size | 22 bytes total (10 bytes unused) |
| Layout | Bytes 0-19: owner, Byte 20: paused, Byte 21: claimingEnabled |
| Access Pattern | Frequent reads, occasional writes |
Byte Layout
┌──────────────────────────────────────────┬──────────┬──────────────────┐
│ Bytes 0-19: owner (address) │ Byte 20 │ Byte 21 │
│ 0x2a4a36b59c47f9ed95b562ebeaefa8c19ef04902│ paused │ claimingEnabled │
└──────────────────────────────────────────┴──────────┴──────────────────┘
Current Value:
$ cast storage 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D 2
0x0000000000000000000001002a4a36b59c47f9ed95b562ebeaefa8c19ef04902
Parsing:
- Bytes 0-19 (owner):
0x2a4a36b59c47f9ed95b562ebeaefa8c19ef04902 - Byte 20 (paused):
0x00= false (not paused) - Byte 21 (claimingEnabled):
0x01= true (claiming enabled)
Subfield: owner (bytes 0-19)
Purpose: Stores the contract owner address with full administrative privileges.
Functions That Read:
owner()- Returns owner address- All admin functions - Check
msg.sender == owner
Functions That Modify:
transferOwnership(address)- Changes owner
Security Considerations:
- ☒ Single EOA owner—no multisig
- ☒ No renounceOwnership function—cannot burn ownership
- △ No timelock on ownership transfer
Subfield: paused (byte 20)
Purpose: Boolean flag indicating whether the contract is paused.
Functions That Read:
paused()- Returns pause stateclaim()- Reverts if paused
Functions That Modify:
pause()- Sets totrueunpause()- Sets tofalse
Values:
0x00= Not paused (claims allowed)0x01= Paused (claims blocked)
Security Considerations:
- △ Owner can pause at will with no timelock
- △ No automatic unpause mechanism
- ☑ View functions remain accessible when paused
Subfield: claimingEnabled (byte 21)
Purpose: Boolean flag indicating whether claiming is globally enabled.
Functions That Read:
claimingEnabled()- Returns enabled state
Functions That Modify:
setClaimingEnabled(bool)- Owner sets state
Values:
0x00= Claiming disabled0x01= Claiming enabled
Security Considerations:
- △ Critical: This flag is NOT checked in
claim()function logic based on bytecode analysis - △ Flag exists but appears to have no enforcement mechanism
- △ May be vestigial from earlier contract version
Storage Packing Benefits
The contract uses efficient storage packing in Slot 2, combining three variables into one storage slot:
Gas Savings:
- Single
SLOAD(2100 gas) reads all three values - Single
SSTORE(20000 gas first write, 5000 gas subsequent) updates packed data - Without packing: 3x
SLOAD(6300 gas), 3xSSTORE(up to 60000 gas first writes)
Trade-offs:
- More complex bit manipulation in bytecode
- All three values are loaded even if only one is needed
- Requires careful management to avoid overwriting adjacent values
Storage Verification Commands
# Read slot 0 (flexibleAllocation)
cast storage 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D 0
# Read slot 2 (owner + flags)
cast storage 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D 2
# Check if specific address has claimed (slot 1 mapping)
# First calculate storage location:
ADDRESS=<target_address>
SLOT=1
cast index address $ADDRESS $SLOT
# Then read from calculated location:
cast storage 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D <calculated_location>
# Or use the contract's view functions:
cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "owner()"
cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "paused()"
cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "claimingEnabled()"
cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "flexibleAllocation()"
cast call 0x33184cD3E5F9D27C6E102Da6BE33e779528A606D "hasClaimed(address)" $ADDRESS
Storage Diagram
classDiagram
class Slot0 {
+address flexibleAllocation
getCurrentAllocation(address)
}
class Slot1 {
+mapping hasClaimed
+mapping eligibilityStatus
<<Same Storage>>
}
class Slot2 {
+address owner (bytes 0-19)
+bool paused (byte 20)
+bool claimingEnabled (byte 21)
<<Packed Storage>>
}
note for Slot1 "Dual-purpose storage\nCannot distinguish\neligible vs claimed"
note for Slot2 "Gas-optimized packing\n22 of 32 bytes used\n10 bytes unused"
Key Observations
| OBSERVATION | IMPACT |
|---|---|
| Slot 1 serves dual purpose | Cannot distinguish between "ineligible" and "already claimed" |
| Efficient slot packing in Slot 2 | Saves gas on admin operations |
| claimingEnabled flag not enforced | Flag exists but has no apparent effect on claim() function |
| No storage for renounce ownership | Owner cannot burn ownership permanently |
| External contract dependency | Slot 0 address can be changed at any time by owner |
| Write-once claim pattern | Claims are permanent and cannot be reset |