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 0x99fdbD43eDd7f4ABA1F745dB29705766946217Dd (etherscan)
Network Ethereum Mainnet
Analysis Date 2026-01-13

Variables

Based on bytecode analysis and function selector patterns, the contract storage layout appears structured as follows. Note that exact slot assignments cannot be confirmed without verified source code.

SLOT VARIABLE NAME TYPE ESTIMATED VALUE PURPOSE
0 owner address 0xc013Bb57...d29d72e76 Contract owner with administrative privileges
1 paused bool false Emergency pause state flag
2 initialized bool true Initialization completion flag
3 locked bool false Allocation lock state (one-way when true)
4 totalAllocations uint256 Unknown Cumulative sum of all allocation amounts
5 allocations mapping(address => uint256) - Maps recipient addresses to allocation amounts
6 recipients address[] ~Unknown length Dynamic array of all recipient addresses
7 exchangeRates mapping(address => uint256) - Optional exchange rates per token address
8+ Potential additional state - - Reserved for additional contract state

Storage Estimation

This storage layout is reconstructed from bytecode analysis and observable function behavior. Without verified source code, exact slot assignments and variable names cannot be definitively confirmed. Solidity compiler optimizations may pack multiple variables into single slots or reorder declarations.


Diagrams

%%{init: {'theme': 'base'}}%%
graph TB
    subgraph Core["Core Control - Slots 0-3"]
        S0["Slot 0: owner<br/>0xc013Bb57...d29d72e76"]
        S1["Slot 1: paused<br/>false"]
        S2["Slot 2: initialized<br/>true"]
        S3["Slot 3: locked<br/>false"]
    end

    subgraph Counters["Tracking - Slot 4"]
        S4["Slot 4: totalAllocations<br/>Sum of all amounts"]
    end

    subgraph Mappings["Allocation Data - Slots 5+"]
        S5["Slot 5: allocations<br/>mapping(address => uint256)"]
        S6["Slot 6: recipients<br/>address[] dynamic array"]
        S7["Slot 7: exchangeRates<br/>mapping(address => uint256)"]
    end

    Core --> Counters --> Mappings

    style S0 fill:#ffe1e1
    style S1 fill:#fff4e1
    style S2 fill:#e1ffe1
    style S3 fill:#ffe1e1
    style S4 fill:#e1f5ff
    style S5 fill:#fff4e1
    style S6 fill:#e1ffe1
    style S7 fill:#e1f5ff

Storage Details

Slot 0: Owner Address

Type: address (20 bytes)

Current Value: 0xc013Bb5798d34997adbeE84f3f3aF42d29d72e76

Purpose: Stores the address with exclusive administrative control over the contract. Modified only through ownership transfer functions.

Access Pattern:

  • Read: owner() view function
  • Write: setOwner(), transferOwnership() admin functions

Slot 1: Paused State

Type: bool (1 byte, packed)

Current Value: false

Purpose: Emergency pause flag. When true, allocation modification operations revert. Likely affects setBatchAllocations() and possibly other admin functions.

Access Pattern:

  • Read: paused() view function
  • Write: pause(), unpause() admin functions

Slot 2: Initialized State

Type: bool (1 byte, packed)

Current Value: true (set during constructor execution)

Purpose: Prevents re-initialization of contract state after deployment. Despite not being upgradeable, follows initializer pattern for setup protection.

Access Pattern:

  • Read: isInitialized() view function
  • Write: Constructor or initializer function (one-time)

Slot 3: Locked State

Type: bool (1 byte, packed)

Current Value: false

Purpose: Allocation finalization flag. When set to true, prevents any further allocation modifications. This is a one-way state transition—once locked, cannot be unlocked. Provides immutability guarantee for recipients once distribution data is finalized.

Access Pattern:

  • Read: Internal checks in allocation modification functions
  • Write: lockAllocations() admin function (irreversible)

Slot 4: Total Allocations

Type: uint256 (32 bytes)

Current Value: Unknown (sum of all allocation amounts)

Purpose: Maintains cumulative sum of all allocation amounts across all recipients. Updated when setBatchAllocations() modifies or adds allocations. Useful for tracking total distribution commitments.

Access Pattern:

  • Read: getTotalAllocations() view function
  • Write: setBatchAllocations() updates on allocation changes

Calculation:

totalAllocations = sum(allocations[recipient] for all recipients)


Slot 5+: Allocations Mapping

Type: mapping(address => uint256)

Storage Location: Slot 5 base, with keys hashed for actual storage positions

Purpose: Primary data structure mapping recipient addresses to their allocated token amounts. Standard Solidity mapping uses keccak256(key || slot) to compute storage position for each entry.

Access Pattern:

  • Read: balanceOf(address), getBalance(address) view functions
  • Write: setBatchAllocations() admin function

Storage Calculation:

// For address 'addr', allocation stored at:
slot = keccak256(abi.encode(addr, 5))

Example Entry:

Address: 0x9fbcc72a6bc74d0060e14fe8b8f4b7ccfa63ea03
Amount: 0x000000000000000000000000000000000000000000533ece4873e573470c0000
      = 404,000,000,000,000,000,000,000,000 wei


Slot 6: Recipients Array

Type: address[] (dynamic array)

Storage Location: Slot 6 stores array length, actual elements at keccak256(6) + index

Purpose: Ordered list of all recipient addresses that have received allocations. Allows iteration through all recipients using getAllocations() for count and getRecipient(index) for individual addresses.

Access Pattern:

  • Read: getAllocations() (length), getRecipient(index) (element access), getRecipientsCount() (length)
  • Write: setBatchAllocations() appends new recipients, removeRecipient() removes by index

Storage Calculation:

// Array length stored at slot 6
arrayLength = storage[6]

// Element at index i stored at:
slot = keccak256(6) + i

Growth Pattern:

  • Slot 6 value increments with each new unique recipient added
  • 20 batch allocation transactions suggest multiple recipients loaded
  • removeRecipient() likely uses swap-and-pop to avoid gaps

Slot 7+: Exchange Rates Mapping

Type: mapping(address => uint256)

Storage Location: Slot 7 base (estimated), with keys hashed for actual storage positions

Purpose: Stores exchange rates or conversion factors for different token addresses. Purpose unclear without verified source—may enable multi-asset distribution tracking or conversion calculations between allocation units and actual token amounts.

Access Pattern:

  • Read: getExchangeRate(address) view function
  • Write: Unknown setter function (not observed in documented transactions)

Storage Calculation:

// For token address 'token', rate stored at:
slot = keccak256(abi.encode(token, 7))

Unused Feature

No transactions have been observed setting exchange rates. This may be an unused feature, reserved for future functionality, or set during contract initialization with hardcoded values.


State Variable Packing

Solidity compiler automatically packs sequential variables smaller than 32 bytes into single storage slots when possible. Based on the estimated layout:

// Likely packed into Slot 1 (32 bytes total)
bool paused;       // 1 byte
bool initialized;  // 1 byte
bool locked;       // 1 byte
// Remaining 29 bytes unused or occupied by additional flags

This packing optimization reduces gas costs for storage operations on these frequently accessed state flags.


Storage Growth Analysis

Static Storage (Slots 0-4)

Fixed-size variables that occupy constant space regardless of contract usage:
- Control variables: owner, paused, initialized, locked
- Counter: totalAllocations

Estimated Gas Cost: ~20,000 gas per SSTORE on cold slots, 5,000 on warm slots


Dynamic Storage (Slots 5+)

Unbounded growth potential based on number of recipients:

Allocations Mapping:

  • Each new recipient adds one mapping entry
  • Storage cost: ~20,000 gas for new entry, 5,000 for updates

Recipients Array:

  • Each new recipient appends to array
  • Array length increment: 5,000 gas
  • New element storage: 20,000 gas

Worst Case Growth:

N recipients:
- Allocations mapping: N entries × 32 bytes each (sparse storage)
- Recipients array: 32 bytes (length) + N × 20 bytes (addresses)
- Exchange rates: M token addresses × 32 bytes (if used)


Storage Access Patterns

Read Operations (View Functions)

Gas Cost: ~100-2,100 gas depending on whether storage is warm or cold

Most frequent read patterns:
- balanceOf(address): Single mapping read
- getAllocations(): Single array length read
- getTotalAllocations(): Single uint256 read


Write Operations (Admin Functions)

setBatchAllocations(address[], uint256[]):

For N new recipients:
- N × mapping writes: N × 20,000 gas
- N × array appends: N × 20,000 gas
- Total accumulation updates: 5,000 gas
- Transaction observed: ~41,446 gas for 1 recipient

removeRecipient(uint256):

- Array element swap: 5,000 gas
- Array pop: 5,000 gas
- Mapping delete (optional): 5,000 gas refund


Storage Security Considerations

CONSIDERATION IMPACT MITIGATION
Unbounded array growth Gas costs increase with recipients count None observed; consider pagination in future versions
Mapping key collision Extremely low probability (2^256 keyspace) ☑ Inherent Solidity protection
Storage slot collision Only relevant for proxy contracts ☑ N/A (not upgradeable)
Lock permanence Irreversible state change △ No confirmation mechanism
Owner single point of failure Storage fully controlled by one address △ No multisig or timelock