Skip to content

Functions

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

Function Selectors

SELECTOR FUNCTION SIGNATURE CATEGORY
N/A Doubler() Constructor
N/A fallback() Payable Entry Point
0x13af4035 enter() User
0x35c1d349 collectFees() Admin
0x13af40ba setOwner(address) Admin

Summary

CATEGORY NUM FUNCTIONS
Total Functions 5
User Functions 2 (enter, fallback)
Admin Functions 2 (collectFees, setOwner)
Constructor 1 (Doubler)
View Functions 0 (all state variables are public, auto-generated getters)

Constructor

Function: Doubler()

Constructor function executed once at contract Deployment to initialize the contract owner.

ATTRIBUTE VALUE
Selector N/A (constructor)
Parameters None
Access Public (called once at deployment)
Payable No
FLAG OBSERVATION
Simple owner initialization with no complex setup
Owner cannot be zero address (would revert in later modifier checks)
No events emitted, making deployment tracking difficult
STEP ACTION
1 Set owner to msg.sender (contract deployer)
VARIABLE CHANGE
owner Set to msg.sender (0x45ab6108cc41c20f416a98615aa8c349f02a275b)
function Doubler() {
    owner = msg.sender;
}

User Functions

Function: fallback()

Unnamed Fallback Function that executes when ETH is sent to the contract without function data. Delegates all calls to enter().

ATTRIBUTE VALUE
Selector N/A (fallback)
Parameters None (accepts msg.value)
Access Public, Payable
FLAG OBSERVATION
Simplifies UX - users can send ETH directly without function calls
No event emitted, making transaction tracking require full trace
All logic delegated to enter(), maintaining single source of truth
STEP ACTION
1 Call enter() function with current msg.value
function() {
    enter();
}

Function: enter()

Primary entry point for participants to deposit ETH and join the Ponzi Scheme queue. Handles deposits, fee extraction, balance tracking, and automatic payouts when conditions are met.

ATTRIBUTE VALUE
Selector 0x13af4035
Parameters None (uses msg.value and msg.sender)
Access Public, Payable
FLAG OBSERVATION
Minimum deposit 1 ETH enforced, but refund uses send() which can fail silently
First participant (idx=0) pays 100% in fees with no balance credit
send() for payouts can fail silently without reverting transaction
balance variable can desync from actual contract balance due to failed sends
No event emitted for deposits, queue additions, or payouts
Unbounded array growth - no limit on participants
CONDITION REQUIREMENT
Minimum Deposit msg.value >= 1 ether
Gas Available Sufficient gas for array expansion and potential payout
STEP ACTION
1 Check if msg.value < 1 ether - if true, refund via send() and return
2 Get current participants array length as idx
3 Expand participants array by 1
4 Store msg.sender as participants[idx].etherAddress
5 Store msg.value as participants[idx].amount
6 If idx != 0: Add msg.value / 10 to collectedFees and add msg.value to balance
7 Else (first participant): Add entire msg.value to collectedFees only
8 If balance > participants[payoutIdx].amount * 2: Calculate and send payout
9 Calculate transactionAmount = 2 * (participants[payoutIdx].amount - participants[payoutIdx].amount / 10)
10 Send transactionAmount to participants[payoutIdx].etherAddress via send()
11 Subtract participants[payoutIdx].amount * 2 from balance
12 Increment payoutIdx by 1
VARIABLE CHANGE
participants[idx].etherAddress Set to msg.sender
participants[idx].amount Set to msg.value
participants.length Incremented by 1
collectedFees Increased by msg.value / 10 (or msg.value if first participant)
balance Increased by msg.value (except first participant)
balance (on payout) Decreased by participants[payoutIdx].amount * 2
payoutIdx (on payout) Incremented by 1
CONDITION REVERT MESSAGE
Deposit < 1 ETH No revert - refund attempted via send()
Participant send() fails No revert - △ silent failure, balance still decremented
Refund send() fails No revert - △ silent failure, user loses funds
Out of gas EVM revert (no custom message)
flowchart TD
    Start([enter called with msg.value]) --> CheckMin{msg.value >= 1 ETH?}

    CheckMin -->|No| Refund[send msg.value back to sender]
    Refund --> RefundSuccess{send successful?}
    RefundSuccess -->|Yes| End1([Return - deposit rejected])
    RefundSuccess -->|No| End2([△ Return - user lost ETH])

    CheckMin -->|Yes| GetIdx[idx = participants.length]
    GetIdx --> ExpandArray[participants.length += 1]
    ExpandArray --> StoreAddress[participants idx etherAddress = msg.sender]
    StoreAddress --> StoreAmount[participants idx amount = msg.value]

    StoreAmount --> IsFirst{idx == 0?}
    IsFirst -->|Yes| AllFees[collectedFees += msg.value]
    IsFirst -->|No| TenPercent[collectedFees += msg.value/10<br/>balance += msg.value]

    AllFees --> CheckPayout{balance > participants payoutIdx amount * 2?}
    TenPercent --> CheckPayout

    CheckPayout -->|No| End3([Transaction complete - waiting in queue])

    CheckPayout -->|Yes| CalcPayout[transactionAmount = 2 * amount - amount/10]
    CalcPayout --> SendPayout[send transactionAmount to participants payoutIdx]
    SendPayout --> PaySuccess{send successful?}

    PaySuccess -->|Yes| UpdateBalance[balance -= participants payoutIdx amount * 2]
    PaySuccess -->|No| UpdateBalanceAnyway[☒ balance -= amount * 2 ANYWAY]

    UpdateBalance --> IncrementIdx[payoutIdx += 1]
    UpdateBalanceAnyway --> IncrementIdx
    IncrementIdx --> End4([Transaction complete - payout sent])

    style RefundSuccess fill:#fff4cc
    style PaySuccess fill:#fff4cc
    style End2 fill:#ffcccc
    style UpdateBalanceAnyway fill:#ffcccc
function enter() {
    if (msg.value < 1 ether) {
        msg.sender.send(msg.value);
        return;
    }

    // add a new participant to array
    uint idx = participants.length;
    participants.length += 1;
    participants[idx].etherAddress = msg.sender;
    participants[idx].amount = msg.value;

    // collect fees and update contract balance
    if (idx != 0) {
        collectedFees += msg.value / 10;
        balance += msg.value;
    }
    else {
        // first participant has no one above him,
        // so it goes all to fees
        collectedFees += msg.value;
    }

// if there are enough ether on the balance we can pay out to an earlier participant
    if (balance > participants[payoutIdx].amount * 2) {
        uint transactionAmount = 2 * (participants[payoutIdx].amount - participants[payoutIdx].amount / 10);
        participants[payoutIdx].etherAddress.send(transactionAmount);

        balance -= participants[payoutIdx].amount * 2;
        payoutIdx += 1;
    }
}

Admin Functions

Function: collectFees()

Allows the contract owner to withdraw accumulated fees to their address. Fees represent 10% of all participant deposits.

ATTRIBUTE VALUE
Selector 0x35c1d349
Parameters None
Access Owner only (via onlyowner modifier)
Payable No
FLAG OBSERVATION
Owner can withdraw fees at any time without restrictions
Uses send() which can fail silently if owner is contract with fallback
If send fails, collectedFees is still set to 0 - permanent loss
No event emitted for fee collection
Early exit if no fees to collect (gas optimization)
CONDITION REQUIREMENT
Caller Must be current owner address
Collected Fees Must be greater than 0 (otherwise early return)
STEP ACTION
1 Check if msg.sender == owner (via modifier)
2 If collectedFees == 0, return early
3 Send collectedFees amount to owner via send()
4 Set collectedFees = 0
VARIABLE CHANGE
collectedFees Set to 0
Owner ETH balance Increased by collectedFees amount (if send succeeds)
CONDITION REVERT MESSAGE
Caller is not owner No revert - modifier prevents function body execution
send() to owner fails No revert - △ collectedFees lost permanently
collectedFees == 0 No revert - early return
function collectFees() onlyowner {
    if (collectedFees == 0) return;

    owner.send(collectedFees);
    collectedFees = 0;
}

Function: setOwner(address)

Allows the current owner to transfer ownership to a new address. No timelock or multi-step confirmation process.

ATTRIBUTE VALUE
Selector 0x13af40ba
Parameters address _owner - new owner address
Access Owner only (via onlyowner modifier)
Payable No
FLAG OBSERVATION
Ownership can be transferred instantly without delay
No validation that _owner is not zero address
Ownership could be transferred to contract that can't call functions
No event emitted for ownership transfer
No two-step ownership transfer process
CONDITION REQUIREMENT
Caller Must be current owner address
New Owner None - any address accepted (including 0x0)
STEP ACTION
1 Check if msg.sender == owner (via modifier)
2 Set owner = _owner
VARIABLE CHANGE
owner Set to _owner parameter value
CONDITION REVERT MESSAGE
Caller is not owner No revert - modifier prevents function body execution
function setOwner(address _owner) onlyowner {
    owner = _owner;
}

Access Control Modifier

Modifier: onlyowner

The onlyowner Modifier restricts function access to the contract owner. However, it uses deprecated Solidity syntax that doesn't explicitly Revert on failure.

Code:

modifier onlyowner { if (msg.sender == owner) _ }

Behavior:

  • If msg.sender == owner: Execute function body (represented by _)
  • Else: Do nothing (function body is not executed, but transaction succeeds with no revert)

Security Concern: In modern Solidity, this modifier would use require(msg.sender == owner, "Not owner"); to explicitly revert unauthorized calls. The deprecated syntax silently succeeds without executing the function, which could be confusing for callers expecting a revert.


Public State Variables (Auto-Generated Getters)

While not explicit functions in the source code, Solidity automatically generates getter functions for all public State Variables:

FUNCTION RETURN TYPE PURPOSE
participants(uint256) (address, uint256) Returns etherAddress and amount for participant at given index
payoutIdx() uint256 Returns current payout queue position (currently 118)
collectedFees() uint256 Returns accumulated owner fees (currently 0)
balance() uint256 Returns internal balance tracker (currently ~27.44 ETH)
owner() address Returns current owner address

These getters are callable by anyone and cost only gas for the RPC call (no transaction fee when called off-chain).