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,
payoutIdxwill 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 decrementedbalance - 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:
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 |