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

Variables

SLOT VARIABLE NAME TYPE CURRENT VALUE PURPOSE
0 participants.length uint256 321 (0x141) Total number of participants in queue
1 payoutIdx uint256 118 (0x76) Index of next participant to be paid
2 collectedFees uint256 0 Accumulated owner fees awaiting withdrawal
3 balance uint256 27,444,740,000,000,000,000 Wei (~27.44 ETH) Internal balance tracker for payout logic
4 owner address 0x45ab6108cc41c20f416a98615aa8c349f02a275b Contract owner (fee recipient)
keccak256(0) + offset participants[N] Participant struct Dynamic Participant data stored in array

Note on Current ETH Balance: The actual contract balance is 32.435530000000000000 ETH, which is 4.99 ETH higher than the internal balance State Variable. This discrepancy suggests failed send() calls or direct ETH transfers that didn't update the internal accounting.


Storage Layout Diagram

graph TB
    subgraph Fixed["Fixed Storage Slots 0-4"]
        S0["Slot 0: participants.length<br/>Value: 321<br/>Total participants ever joined"]
        S1["Slot 1: payoutIdx<br/>Value: 118<br/>Next participant to be paid"]
        S2["Slot 2: collectedFees<br/>Value: 0 ETH<br/>Owner fees withdrawn"]
        S3["Slot 3: balance<br/>Value: ~27.44 ETH<br/>△ Desynced from actual balance"]
        S4["Slot 4: owner<br/>Value: 0x45ab...275b<br/>Contract owner"]
    end

    subgraph Dynamic["Dynamic Storage - Participant Array"]
        ArrayBase["Base: keccak256 0<br/>Slot: 0x290d...a8"]
        P0["participants 0<br/>Slot: keccak256 0 + 0*2"]
        P1["participants 1<br/>Slot: keccak256 0 + 1*2"]
        P117["participants 117<br/>Slot: keccak256 0 + 117*2<br/>△ Last paid participant"]
        P118["participants 118<br/>Slot: keccak256 0 + 118*2<br/>☒ Next in queue stuck"]
        Dots["..."]
        P320["participants 320<br/>Slot: keccak256 0 + 320*2<br/>☒ Last participant stuck"]
    end

    ArrayBase --> P0
    P0 --> P1
    P1 --> Dots
    Dots --> P117
    P117 --> P118
    P118 --> Dots2["..."]
    Dots2 --> P320

    S0 -.->|points to| ArrayBase
    S1 -.->|points to| P118

    style S0 fill:#e1f5ff
    style S1 fill:#fff4e1
    style S2 fill:#e1ffe1
    style S3 fill:#ffe1e1
    style S4 fill:#ffe1f0
    style P117 fill:#e1ffe1
    style P118 fill:#ffe1e1
    style P320 fill:#ffe1e1

Detailed Variable Analysis

Slot 0: participants.length

Type: uint256 (dynamic array length)

Current Value: 321 (0x141)

Purpose: Stores the total number of participants who have ever entered the Ponzi queue. This value only increments and never decreases.

Verification Command:

cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D 0
# Returns: 0x0000000000000000000000000000000000000000000000000000000000000141 (321)

Significance:

  • With 321 total participants and only 118 paid out, 203 participants remain stuck in the queue
  • Array is unbounded - no maximum participant limit exists in code
  • Large arrays can cause high gas costs for iteration (though this contract doesn't iterate)

Slot 1: payoutIdx

Type: uint256

Current Value: 118 (0x76)

Purpose: Index of the next participant in line to receive a payout. When balance > participants[payoutIdx].amount * 2, this participant gets paid and payoutIdx increments.

Verification Command:

cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D 1
# Returns: 0x0000000000000000000000000000000000000000000000000000000000000076 (118)

Significance:

  • Current State: Participant at index 118 is next in line
  • Queue Status: 203 participants (indices 118-320) are waiting for payouts
  • Stuck Forever: Without new deposits, payoutIdx will never increment again

Slot 2: collectedFees

Type: uint256

Current Value: 0

Purpose: Accumulates 10% fee from each participant deposit. Owner can withdraw this via collectFees().

Verification Command:

cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D 2
# Returns: 0x0000000000000000000000000000000000000000000000000000000000000000 (0)

Significance:

  • Zero Balance: All fees have been withdrawn by the owner
  • Historical Fees: Approximately 32.1 ETH total fees were collected (10% of ~321 ETH deposited)
  • Owner Profit: Owner has extracted all accumulated fees

Slot 3: balance

Type: uint256

Current Value: 27,444,740,000,000,000,000 Wei (~27.44 ETH)

Purpose: Internal accounting variable that tracks the notional balance available for payouts. This should represent deposits minus payouts, but is desynced from reality.

Verification Command:

cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D 3
# Returns: 0x00000000000000000000000000000000000000000000000182a8a4884a804000 (~27.44 ETH)

# Compare to actual balance:
cast balance 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D
# Returns: 32435530000000000000 (32.44 ETH)

Significance:

  • Accounting Error: 4.99 ETH discrepancy between internal balance and actual ETH balance
  • Possible Causes:
  • Failed send() calls that didn't revert but still decremented balance
  • Direct ETH transfers to contract that bypassed enter() function
  • Rounding errors in integer division (unlikely to be this large)
  • Impact: Contract logic uses internal balance, not actual ETH balance, for payout decisions

Slot 4: owner

Type: address

Current Value: 0x45ab6108cc41c20f416a98615aa8c349f02a275b

Purpose: Stores the address authorized to call collectFees() and setOwner().

Verification Command:

cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D 4
# Returns: 0x00000000000000000000000045ab6108cc41c20f416a98615aa8c349f02a275b

Significance:

  • Same address as contract deployer (never transferred)
  • Controls all accumulated fees
  • Can transfer ownership to any address (including 0x0)

Dynamic Storage: participants Array

Storage Layout for Structs

Each Participant struct occupies 2 storage slots:

struct Participant {
    address etherAddress;  // 20 bytes - Slot N
    uint amount;           // 32 bytes - Slot N+1
}

Storage Location Calculation:

Base slot for array data: keccak256(0)

Participant at index i:
- etherAddress: keccak256(0) + (i * 2) + 0
- amount: keccak256(0) + (i * 2) + 1

Example Participant Storage Queries

Retrieve Participant 0 (First Participant):

# Calculate base slot
cast keccak 0x0000000000000000000000000000000000000000000000000000000000000000
# Returns: 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563

# Get participant[0].etherAddress
cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563

# Get participant[0].amount (base slot + 1)
cast to-dec 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564
cast storage 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D <result from above>

Retrieve Participant 118 (Next to be Paid):

# Calculate slot: keccak256(0) + (118 * 2) = base + 236
# Participant 118 etherAddress slot
cast --to-dec 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563
# Add 236 to decimal result, convert back to hex

# Or use public getter function (easier):
cast call 0xfD2487cc0e5dCe97F08BE1Bc8eF1DCE8D5988B4D "participants(uint256)" 118

Current Participant Queue Status

INDEX RANGE COUNT STATUS NOTES
0 - 117 118 ☑ Paid Out Successfully received 180% payout
118 - 320 203 ☒ Stuck Forever Insufficient balance for any payout

Next Required Payout:

To pay participant 118, the contract needs:

balance > participants[118].amount * 2

Since the contract balance is frozen (no new deposits) and payoutIdx is stuck at 118, the remaining 203 participants will never be paid.


Storage Access Patterns

Read Operations

All state variables are marked public, generating automatic View Functions:

// Auto-generated getters
function participants(uint256 index) public view returns (address, uint256);
function payoutIdx() public view returns (uint256);
function collectedFees() public view returns (uint256);
function balance() public view returns (uint256);
function owner() public view returns (address);

Write Operations

FUNCTION WRITES TO
enter() participants[idx], participants.length, collectedFees, balance, payoutIdx (conditional)
collectFees() collectedFees
setOwner() owner

Storage Security Observations

OBSERVATION SEVERITY NOTES
Unbounded array growth △ Medium No DoS risk since contract doesn't iterate, but increases storage costs
Balance desynchronization ☒ High Internal balance doesn't match actual ETH balance (4.99 ETH difference)
No overflow protection △ Medium Solidity 0.2.0 has no SafeMath - arithmetic can overflow/underflow
Direct storage access ☑ Low No proxy pattern - storage layout is stable and predictable
No emergency pause ☒ High No way to stop deposits or rescue funds if bug discovered