ETHEREUM NETWORK

Earn on-chain yields from a decentralized protocol unstoppable, unchangeable, and autonomous.

Ankaa is a decentralized ecosystem that pays a fixed daily yield of 0.4% on your USDC deposits, secured by audited and immutable smart contracts on Ethereum.

Audited Smart ContractFully Decentralized

How It Works

Start earning predictable yields in three simple steps

01

Deposit USDC

Deposit USDC into the Ankaa Yield Pool on Ethereum network.

02

Earn 0.4% Daily Yield

Your yield compounds every second in real time.

03

Withdraw Your Earnings

Accumulated yield can be withdrawn at any time.

Why Choose Ankaa

Daily Fixed Yield

Earn a guaranteed 0.4% daily yield on your USDC deposits. Your yield compounds every second in real time.

Audited and Immutable Smart Contract

Our smart contract is publicly verified and immutable, ensuring complete transparency and security.

Fully Decentralized and Transparent

Built on Ethereum with complete decentralization. No central authority controls your funds.

Multi-Level Affiliate Rewards

Earn 20% from direct referrals and 5% from indirect ones up to 12 levels deep. Paid automatically in USDC.

On•chain Contract Code

💰

Daily yield

Earn daily yield forever.

🔒

Immutable

No one can change.

Decentralized

No one can stop.

1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.28;
3
4import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
5import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
6import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
7import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
8import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
9import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
10import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
11
12contract AnkaaProtocol is
13    Initializable,
14    ReentrancyGuardUpgradeable,
15    OwnableUpgradeable,
16    UUPSUpgradeable {
17    using SafeERC20 for IERC20;
18
19    // ============ Constants ============
20    uint256 public constant DAILY_YIELD_RATE = 40; // 0.4% daily = 40/10000 (12% monthly)
21    uint256 public constant RATE_DENOMINATOR = 10000;
22    uint256 public constant MAX_UPLINES = 12;
23    uint256 public constant Q128 = 2**128; // Fixed point precision multiplier for accurate calculations
24
25    // Commission rates by level (in basis points - 10000 = 100%)
26    // Initialized in initialize() function for upgrade safety
27    uint256[12] public COMMISSION_RATES;
28    uint256 public constant MIN_DEPOSIT = 1000; // 0.001 USDC minimum (6 decimals)
29    uint256 public constant MAX_DEPOSIT = 5000000 * 1e6; // 5M USDC maximum (6 decimals)
30    uint256 public constant SECONDS_IN_DAY = 86400;
31
32    // ============ Structures ============
33    struct UplineEntry {
34        address wallet;     // Upline's wallet address
35    }
36
37    struct UserAccount {
38        bool isRegistered;
39        bool isBase;                    // True if BASE user (without referrer)
40        address referrer;               // Direct referrer address
41        UplineEntry[] uplines;          // Array of uplines (maximum 12)
42        uint256 totalDeposited;
43        uint256 lastYieldClaim;
44        uint256 accumulatedYield;
45        uint256 directReferrals;
46        uint256 totalCommissionsEarned;
47        uint256 totalYieldEarned;           // Total yields claimed by user
48        uint256 depositTimestamp;
49        uint256 lastDepositAmount;          // Last deposit for yield calculation
50
51        // Growth accumulators for yield tracking
52        uint256 yieldGrowthInsideLastX128;      // Last yield growth snapshot when user claimed
53        uint256 liquidity;                      // User's active liquidity (deposit amount)
54
55        // Growth accumulators for commission tracking
56        uint256[12] commissionGrowthInsideLastX128;  // Last commission growth snapshot per level
57        uint256 baseCommissionGrowthInsideLastX128;  // Last BASE commission growth snapshot
58
59        uint256 lastCommissionClaim;            // Timestamp of last commission claim
60        uint256 accumulatedCommissions;         // Total commissions already claimed
61    }
62
63    struct DepositRecord {
64        uint256 amount;
65        uint256 timestamp;
66        uint256 yieldClaimed;
67    }
68
69    // ============ State Variables ============
70    IERC20 public depositToken;
71
72    mapping(address => UserAccount) public users;
73    mapping(address => DepositRecord[]) public userDeposits;
74    mapping(address => mapping(uint256 => bool)) private processedNonces;
75
76    address public baseUser;
77    address public authorizedBaseCreator;
78
79    uint256 public totalValueLocked;
80    uint256 public totalYieldPaid;
81    uint256 public totalCommissionsPaid;
82
83    // Global yield growth accumulator
84    uint256 public yieldGrowthGlobalX128;       // Global accumulated yield growth per liquidity unit
85    uint256 public lastYieldUpdate;             // Last timestamp when yield growth was updated
86
87    // Global commission growth accumulators (one per level)
88    uint256[12] public commissionGrowthGlobalX128;  // Global commission growth per level
89    uint256 public baseCommissionGrowthGlobalX128;  // Global BASE commission growth
90    uint256 public lastCommissionUpdate;            // Last timestamp when commission growth was updated
91
92    // Commission liquidity tracking - tracks how much liquidity each user has per level
93    mapping(address => uint256[12]) public userCommissionLiquidity;  // user -> level -> liquidity amount
94    mapping(address => uint256) public userBaseCommissionLiquidity;   // user -> base commission liquidity
95
96    // Total liquidity per level for commission calculations
97    uint256[12] public totalCommissionLiquidityByLevel;
98    uint256 public totalBaseCommissionLiquidity;
99
100    uint256 public version;
101
102    // ============ Events ============
103    event BaseUserRegistered(address indexed user, uint256 timestamp);
104    event UserRegistered(address indexed user, address indexed referrer, uint256 deposit);
105    event Deposited(address indexed user, uint256 amount, address indexed referrer);
106    event YieldClaimed(address indexed user, uint256 amount);
107    event CommissionPaid(address indexed recipient, uint256 amount, uint256 level);
108    event RemainderToBase(address indexed baseUser, uint256 amount, uint256 missingUplines);
109    event UplinesUpdated(address indexed user, uint256 uplinesCount);
110
111    // Auto-liquidation commission events
112    event CommissionClaimed(address indexed user, uint256 amount);
113    event CommissionLevelUpdated(address indexed user, uint256 level, uint256 dailyRate, uint256 liquidatedAmount);
114    event BaseCommissionUpdated(address indexed baseUser, uint256 dailyRate, uint256 liquidatedAmount);
115    event CommissionAutoLiquidated(address indexed user, uint256 level, uint256 liquidatedAmount, uint256 daysCovered);
116    event CommissionsClaimed(address indexed user, uint256 amount);
117    event YieldCapApplied(uint256 originalTimeElapsed, uint256 cappedTimeElapsed);
118    event ContractUpgraded(address indexed implementation, uint256 version);
119
120    // ============ Modifiers ============
121    modifier onlyAuthorizedBase() {
122        require(msg.sender == authorizedBaseCreator, "Not authorized to create BASE");
123        _;
124    }
125
126    modifier userExists(address _user) {
127        require(users[_user].isRegistered, "User does not exist");
128        _;
129    }
130
131    modifier validAmount(uint256 _amount) {
132        require(_amount >= MIN_DEPOSIT && _amount <= MAX_DEPOSIT, "Invalid amount");
133        _;
134    }
135
136    // ============ Constructor ============
137    /// @custom:oz-upgrades-unsafe-allow constructor
138    constructor() {
139        _disableInitializers();
140    }
141
142    /**
143     * @dev Initializes the contract (replaces constructor for upgradeable contracts)
144     * @param _depositToken USDC token address
145     * @param _authorizedBaseCreator Authorized BASE creator address
146     */
147    function initialize(
148        address _depositToken,
149        address _authorizedBaseCreator
150    ) public initializer {
151        require(_depositToken != address(0), "Invalid token");
152        require(_authorizedBaseCreator != address(0), "Invalid creator");
153
154        __ReentrancyGuard_init();
155        __Ownable_init(msg.sender);
156        __UUPSUpgradeable_init();
157
158        depositToken = IERC20(_depositToken);
159        authorizedBaseCreator = _authorizedBaseCreator;
160
161        // Initialize commission rates (upgrade safe)
162        COMMISSION_RATES[0] = 2000;  // Level 1: 20%
163        COMMISSION_RATES[1] = 500;   // Level 2: 5%
164        COMMISSION_RATES[2] = 500;   // Level 3: 5%
165        COMMISSION_RATES[3] = 500;   // Level 4: 5%
166        COMMISSION_RATES[4] = 500;   // Level 5: 5%
167        COMMISSION_RATES[5] = 500;   // Level 6: 5%
168        COMMISSION_RATES[6] = 500;   // Level 7: 5%
169        COMMISSION_RATES[7] = 500;   // Level 8: 5%
170        COMMISSION_RATES[8] = 500;   // Level 9: 5%
171        COMMISSION_RATES[9] = 500;   // Level 10: 5%
172        COMMISSION_RATES[10] = 500;  // Level 11: 5%
173        COMMISSION_RATES[11] = 500;  // Level 12: 5%
174
175        yieldGrowthGlobalX128 = 0;
176        lastYieldUpdate = block.timestamp;
177
178        for (uint256 i = 0; i < 12; i++) {
179            commissionGrowthGlobalX128[i] = 0;
180        }
181        baseCommissionGrowthGlobalX128 = 0;
182        lastCommissionUpdate = block.timestamp;
183
184        version = 1;
185    }
186
187    // ============ Registration Functions ============
188
189    /**
190     * @dev Registers BASE user (without referrer) with initial deposit
191     * @notice BASE user receives commissions when network doesn't have 12 uplines
192     * @param _baseUserAddress BASE user address
193     * @param _initialDeposit BASE user's initial deposit
194     */
195    function registerBaseUser(address _baseUserAddress, uint256 _initialDeposit)
196        external
197        onlyAuthorizedBase
198        nonReentrant
199        validAmount(_initialDeposit)
200    {
201        require(_baseUserAddress != address(0), "Invalid base user address");
202        require(!users[_baseUserAddress].isRegistered, "User already registered");
203        require(baseUser == address(0), "Base user already exists");
204
205        // Transfer initial deposit from authorized creator
206        depositToken.safeTransferFrom(msg.sender, address(this), _initialDeposit);
207
208        UserAccount storage user = users[_baseUserAddress];
209
210        // Update global yield growth before creating user
211        _updateYieldGrowth();
212
213        // Create BASE user with initial deposit
214        user.isRegistered = true;
215        user.isBase = true;
216        user.referrer = address(0);
217        // user.uplines remains empty for BASE
218        user.totalDeposited = _initialDeposit;
219        user.lastDepositAmount = _initialDeposit;
220        user.depositTimestamp = block.timestamp;
221        user.lastYieldClaim = block.timestamp;
222
223        // Initialize yield tracking
224        user.yieldGrowthInsideLastX128 = yieldGrowthGlobalX128;
225        user.liquidity = _initialDeposit;
226
227        user.lastCommissionClaim = block.timestamp;
228        user.accumulatedCommissions = 0;
229
230        // Register initial deposit
231        userDeposits[_baseUserAddress].push(DepositRecord({
232            amount: _initialDeposit,
233            timestamp: block.timestamp,
234            yieldClaimed: 0
235        }));
236
237        // Update TVL
238        totalValueLocked += _initialDeposit;
239
240        // Set as global BASE user
241        baseUser = _baseUserAddress;
242
243        emit BaseUserRegistered(_baseUserAddress, block.timestamp);
244        emit Deposited(_baseUserAddress, _initialDeposit, address(0));
245    }
246
247    /**
248     * @dev Registers user with referrer and makes initial deposit
249     * @param _amount Amount to deposit
250     * @param _referrer Referrer address
251     * @param _nonce Unique nonce to prevent replay attacks
252     */
253    function registerWithReferrer(
254        uint256 _amount,
255        address _referrer,
256        uint256 _nonce
257    ) external nonReentrant validAmount(_amount) {
258        require(!processedNonces[msg.sender][_nonce], "Nonce already used");
259        require(!users[msg.sender].isRegistered, "User already registered");
260        require(users[_referrer].isRegistered, "Referrer not registered");
261        require(_referrer != msg.sender, "Cannot refer yourself");
262        require(baseUser != address(0), "Base user not initialized");
263
264        processedNonces[msg.sender][_nonce] = true;
265
266        // Update global yield growth before creating user
267        _updateYieldGrowth();
268
269        // Transfer tokens to contract
270        depositToken.safeTransferFrom(msg.sender, address(this), _amount);
271
272        // Create user account
273        UserAccount storage newUser = users[msg.sender];
274        newUser.isRegistered = true;
275        newUser.isBase = false;
276        newUser.referrer = _referrer;
277        newUser.totalDeposited = _amount;
278        newUser.lastDepositAmount = _amount;
279        newUser.depositTimestamp = block.timestamp;
280        newUser.lastYieldClaim = block.timestamp;
281
282        // Initialize yield tracking
283        newUser.yieldGrowthInsideLastX128 = yieldGrowthGlobalX128;
284        newUser.liquidity = _amount;
285
286        newUser.lastCommissionClaim = block.timestamp;
287        newUser.accumulatedCommissions = 0;
288
289        // Build upline structure
290        _buildUplineStructure(msg.sender, _referrer);
291
292        // Register deposit
293        userDeposits[msg.sender].push(DepositRecord({
294            amount: _amount,
295            timestamp: block.timestamp,
296            yieldClaimed: 0
297        }));
298
299        // Update TVL
300        totalValueLocked += _amount;
301
302        // Increment referrer's referrals
303        users[_referrer].directReferrals++;
304
305        // Add commission liquidity for uplines
306        _addCommissionLiquidity(msg.sender, _amount);
307
308        emit UserRegistered(msg.sender, _referrer, _amount);
309        emit Deposited(msg.sender, _amount, _referrer);
310    }
311
312    /**
313     * @dev Makes additional deposit (for already registered users, including BASE)
314     */
315    function deposit(
316        uint256 _amount,
317        uint256 _nonce
318    ) external nonReentrant validAmount(_amount) userExists(msg.sender) {
319        require(!processedNonces[msg.sender][_nonce], "Nonce already used");
320
321        processedNonces[msg.sender][_nonce] = true;
322
323        UserAccount storage user = users[msg.sender];
324
325        // Update global yield growth and claim any pending yield before deposit
326        _updateYieldGrowth();
327
328        // Accumulate pending yield before changing liquidity
329        uint256 pendingYield = _getYieldOwed(yieldGrowthGlobalX128, user.yieldGrowthInsideLastX128, user.liquidity);
330        if (pendingYield > 0) {
331            user.accumulatedYield += pendingYield;
332            // NOTE: totalYieldPaid is incremented only in claimYield() to avoid double counting
333            // totalYieldPaid += pendingYield;
334        }
335
336        // Transfer tokens to contract
337        depositToken.safeTransferFrom(msg.sender, address(this), _amount);
338
339        // Update user data
340        user.totalDeposited += _amount;
341        user.lastDepositAmount = _amount;
342        user.depositTimestamp = block.timestamp;
343
344        // Update yield tracking with new liquidity
345        user.yieldGrowthInsideLastX128 = yieldGrowthGlobalX128;
346        user.liquidity = user.totalDeposited;
347
348        // Register deposit
349        userDeposits[msg.sender].push(DepositRecord({
350            amount: _amount,
351            timestamp: block.timestamp,
352            yieldClaimed: 0
353        }));
354
355        // Update TVL
356        totalValueLocked += _amount;
357
358        // Add commission liquidity for uplines
359        if (!user.isBase) {
360            _addCommissionLiquidity(msg.sender, _amount);
361        }
362
363        emit Deposited(msg.sender, _amount, user.referrer);
364    }
365
366    // ============ Yield Growth Functions ============
367
368    /**
369     * @dev Updates global yield growth accumulator using continuous compounding
370     * @notice Includes overflow protection by capping time elapsed to 365 days
371     */
372    function _updateYieldGrowth() private {
373        if (totalValueLocked > 0 && block.timestamp >= lastYieldUpdate) {
374            uint256 timeElapsed = block.timestamp - lastYieldUpdate;
375
376            // Protection against overflow: cap time elapsed to 365 days
377            // This prevents potential overflow if contract is paused for extended period
378            if (timeElapsed > 365 days) {
379                emit YieldCapApplied(timeElapsed, 365 days);
380                timeElapsed = 365 days;
381            }
382
383            // Calculate yield growth per second per liquidity unit
384            // DAILY_YIELD_RATE per day = DAILY_YIELD_RATE per SECONDS_IN_DAY seconds
385            uint256 yieldPerSecond = DAILY_YIELD_RATE * Q128 / (RATE_DENOMINATOR * SECONDS_IN_DAY);
386            uint256 yieldGrowthIncrease = yieldPerSecond * timeElapsed;
387
388            yieldGrowthGlobalX128 += yieldGrowthIncrease;
389        }
390
391        lastYieldUpdate = block.timestamp;
392    }
393
394    /**
395     * @dev Calculates yield owed using growth accumulator formula
396     * @param yieldGrowthInsideX128 Current global yield growth
397     * @param yieldGrowthInsideLastX128 User's last yield growth snapshot
398     * @param liquidity User's liquidity amount
399     */
400    function _getYieldOwed(
401        uint256 yieldGrowthInsideX128,
402        uint256 yieldGrowthInsideLastX128,
403        uint256 liquidity
404    ) private pure returns (uint256 yieldOwed) {
405        if (liquidity == 0) return 0;
406
407        // Calculate yield: (currentGrowth - lastGrowth) * liquidity / Q128
408        uint256 growthDifference = yieldGrowthInsideX128 - yieldGrowthInsideLastX128;
409        yieldOwed = (growthDifference * liquidity) / Q128;
410    }
411
412    // ============ Commission Growth Functions ============
413
414    /**
415     * @dev Updates global commission growth accumulators continuously (like yield growth)
416     * @notice Each level grows with its own commission rate (20%, 5%, 5%, 5%...)
417     * @notice BASE commissions are tracked per-level like regular uplines
418     */
419    function _updateCommissionGrowth() private {
420        if (block.timestamp > lastCommissionUpdate) {
421            uint256 timeElapsed = block.timestamp - lastCommissionUpdate;
422
423            // Update growth for each commission level
424            for (uint256 i = 0; i < MAX_UPLINES; i++) {
425                if (totalCommissionLiquidityByLevel[i] > 0) {
426                    // Calculate commission growth per second per liquidity unit for this level
427                    // Commission rate for this level * daily yield rate / seconds per day
428                    uint256 commissionPerSecond = (DAILY_YIELD_RATE * COMMISSION_RATES[i] * Q128) /
429                                                 (RATE_DENOMINATOR * RATE_DENOMINATOR * SECONDS_IN_DAY);
430                    uint256 commissionGrowthIncrease = commissionPerSecond * timeElapsed;
431
432                    commissionGrowthGlobalX128[i] += commissionGrowthIncrease;
433                }
434            }
435
436            lastCommissionUpdate = block.timestamp;
437        }
438    }
439
440    /**
441     * @dev Calculates commission owed for a specific level using growth accumulator formula
442     * @param _user The user claiming commissions
443     * @param _level The commission level (0-11 for levels 1-12)
444     */
445    function _getCommissionOwedByLevel(address _user, uint256 _level) private view returns (uint256 commissionOwed) {
446        require(_level < MAX_UPLINES, "Invalid level");
447        require(_user != address(0), "Invalid user address");
448
449        // Get user's commission liquidity for this level
450        uint256 userLiquidity = userCommissionLiquidity[_user][_level];
451
452        if (userLiquidity == 0) {
453            return 0; // User has no liquidity at this level
454        }
455
456        UserAccount storage user = users[_user];
457
458        // Get current and last growth for this level
459        uint256 currentGrowth = commissionGrowthGlobalX128[_level];
460
461        // Add time-based growth if liquidity exists for this level (SAME AS _calculatePendingCommissions)
462        uint256 timeElapsed = block.timestamp > lastCommissionUpdate ? block.timestamp - lastCommissionUpdate : 0;
463        if (totalCommissionLiquidityByLevel[_level] > 0 && timeElapsed > 0) {
464            uint256 commissionPerSecond = (DAILY_YIELD_RATE * COMMISSION_RATES[_level] * Q128) /
465                                         (RATE_DENOMINATOR * RATE_DENOMINATOR * SECONDS_IN_DAY);
466            uint256 commissionGrowthIncrease = commissionPerSecond * timeElapsed;
467            currentGrowth += commissionGrowthIncrease;
468        }
469
470        uint256 lastGrowth = user.commissionGrowthInsideLastX128[_level];
471
472        if (currentGrowth <= lastGrowth) {
473            return 0; // No growth since last claim
474        }
475
476        // Calculate commission owed: (growth_difference * liquidity) / Q128
477        uint256 growthDifference = currentGrowth - lastGrowth;
478        commissionOwed = (growthDifference * userLiquidity) / Q128;
479    }
480
481    // ============ Internal Functions ============
482
483    /**
484     * @dev Builds upline structure with truncation
485     */
486    function _buildUplineStructure(address _user, address _referrer) private {
487        UserAccount storage userAccount = users[_user];
488        UserAccount storage referrerAccount = users[_referrer];
489
490        // Clear existing uplines (security)
491        delete userAccount.uplines;
492
493        // If referrer has uplines, copy with truncation if necessary
494        uint256 uplinesToCopy = referrerAccount.uplines.length;
495
496        if (uplinesToCopy >= MAX_UPLINES - 1) {
497            // Truncation: take the last 11 uplines
498            uint256 startIndex = uplinesToCopy - (MAX_UPLINES - 1);
499            for (uint256 i = startIndex; i < uplinesToCopy; i++) {
500                userAccount.uplines.push(referrerAccount.uplines[i]);
501            }
502        } else {
503            // Copy all referrer's uplines
504            for (uint256 i = 0; i < uplinesToCopy; i++) {
505                userAccount.uplines.push(referrerAccount.uplines[i]);
506            }
507        }
508
509        // Add referrer as last upline
510        userAccount.uplines.push(UplineEntry({
511            wallet: _referrer
512        }));
513
514        emit UplinesUpdated(_user, userAccount.uplines.length);
515    }
516
517
518    /**
519     * @dev Adds commission liquidity for uplines when user makes a deposit
520     * @param _user The user who made the deposit
521     * @param _depositAmount The deposit amount that adds liquidity for uplines
522     */
523    function _addCommissionLiquidity(address _user, uint256 _depositAmount) private {
524        require(_user != address(0), "Invalid user address");
525        require(_depositAmount > 0, "Invalid deposit amount");
526
527        UserAccount storage userAccount = users[_user];
528        uint256 uplinesCount = userAccount.uplines.length;
529
530        // Update commission growth before changing liquidity
531        _updateCommissionGrowth();
532
533        // Process each level (0-11 = levels 1-12)
534        for (uint256 i = 0; i < MAX_UPLINES; i++) {
535            // Check if this level has an actual upline
536            address uplineAddress = address(0);
537
538            if (i < uplinesCount) {
539                uint256 uplineIndex = uplinesCount - 1 - i; // Reverse index (closest = level 0)
540                uplineAddress = userAccount.uplines[uplineIndex].wallet;
541            }
542
543            // If no upline at this level, BASE user receives commission for this level
544            if (uplineAddress == address(0)) {
545                uplineAddress = baseUser;
546            }
547
548            // Add liquidity for upline (or BASE) at this specific level
549            if (uplineAddress != address(0)) {
550                // First claim any pending commissions to avoid loss
551                uint256 pendingCommission = _getCommissionOwedByLevel(uplineAddress, i);
552                if (pendingCommission > 0) {
553                    users[uplineAddress].accumulatedCommissions += pendingCommission;
554                    // NOTE: totalCommissionsPaid is incremented only in claimCommissions() to avoid double counting
555                    // totalCommissionsPaid += pendingCommission;
556                }
557
558                // Update growth snapshot and add liquidity at this SPECIFIC level
559                users[uplineAddress].commissionGrowthInsideLastX128[i] = commissionGrowthGlobalX128[i];
560                userCommissionLiquidity[uplineAddress][i] += _depositAmount;
561                totalCommissionLiquidityByLevel[i] += _depositAmount;
562
563                emit CommissionLevelUpdated(uplineAddress, i + 1, 0, _depositAmount);
564            }
565        }
566    }
567
568    /**
569     * @dev Calculates pending yield using growth accumulator
570     * @notice Returns accumulated yield + current pending yield (preserves past earnings)
571     */
572    function _calculatePendingYield(address _user) private view returns (uint256) {
573        UserAccount storage user = users[_user];
574
575        // Always include accumulated yield from previous periods
576        uint256 totalPendingYield = user.accumulatedYield;
577
578        // If user has active liquidity, calculate additional pending yield
579        if (user.liquidity > 0) {
580            // Calculate current global yield growth with time elapsed
581            uint256 currentYieldGrowthX128 = yieldGrowthGlobalX128;
582            if (totalValueLocked > 0 && block.timestamp >= lastYieldUpdate) {
583                uint256 timeElapsed = block.timestamp - lastYieldUpdate;
584                uint256 yieldPerSecond = DAILY_YIELD_RATE * Q128 / (RATE_DENOMINATOR * SECONDS_IN_DAY);
585                uint256 yieldGrowthIncrease = yieldPerSecond * timeElapsed;
586                currentYieldGrowthX128 += yieldGrowthIncrease;
587            }
588
589            // Calculate yield owed from active liquidity
590            uint256 currentPendingYield = _getYieldOwed(currentYieldGrowthX128, user.yieldGrowthInsideLastX128, user.liquidity);
591            totalPendingYield += currentPendingYield;
592        }
593
594        return totalPendingYield;
595    }
596
597    /**
598     * @dev Calculates pending commissions using growth accumulator
599     * @notice Returns accumulated commissions + current pending commissions (preserves past earnings)
600     * @dev Includes time-based growth calculation for accurate pending amounts
601     */
602    function _calculatePendingCommissions(address _user) private view returns (uint256) {
603        require(_user != address(0), "Invalid user address");
604
605        UserAccount storage user = users[_user];
606
607        // Always include accumulated commissions from previous periods
608        uint256 totalPending = user.accumulatedCommissions;
609
610        // First calculate what the current growth would be if updated now
611        uint256 timeElapsed = block.timestamp > lastCommissionUpdate ? block.timestamp - lastCommissionUpdate : 0;
612
613        // Calculate commissions owed from each level (0-11 = levels 1-12)
614        // This includes both direct upline commissions and BASE commissions for missing levels
615        for (uint256 i = 0; i < MAX_UPLINES; i++) {
616            uint256 userLiquidity = userCommissionLiquidity[_user][i];
617            if (userLiquidity > 0) {
618                uint256 currentGrowth = commissionGrowthGlobalX128[i];
619
620                // Add time-based growth if liquidity exists for this level
621                if (totalCommissionLiquidityByLevel[i] > 0 && timeElapsed > 0) {
622                    uint256 commissionPerSecond = (DAILY_YIELD_RATE * COMMISSION_RATES[i] * Q128) /
623                                                 (RATE_DENOMINATOR * RATE_DENOMINATOR * SECONDS_IN_DAY);
624                    uint256 commissionGrowthIncrease = commissionPerSecond * timeElapsed;
625                    currentGrowth += commissionGrowthIncrease;
626                }
627
628                uint256 lastGrowth = user.commissionGrowthInsideLastX128[i];
629                if (currentGrowth > lastGrowth) {
630                    uint256 growthDifference = currentGrowth - lastGrowth;
631                    uint256 levelCommission = (growthDifference * userLiquidity) / Q128;
632                    totalPending += levelCommission;
633                }
634            }
635        }
636
637        return totalPending;
638    }
639
640    // ============ Public Functions ============
641
642    /**
643     * @dev Claims accumulated yield using growth accumulator
644     * @notice Claims all accumulated yield (from past deposits) + current pending yield
645     * @notice Follows CEI (Checks-Effects-Interactions) pattern for maximum security
646     */
647    function claimYield() external nonReentrant userExists(msg.sender) {
648        // Update global yield growth before claiming
649        _updateYieldGrowth();
650
651        UserAccount storage user = users[msg.sender];
652
653        // CHECKS: Calculate total claimable yield (accumulated + current pending)
654        uint256 totalClaimableYield = user.accumulatedYield;
655        uint256 currentPendingYield = 0;
656
657        if (user.liquidity > 0) {
658            currentPendingYield = _getYieldOwed(yieldGrowthGlobalX128, user.yieldGrowthInsideLastX128, user.liquidity);
659            totalClaimableYield += currentPendingYield;
660        }
661
662        require(totalClaimableYield > 0, "No yield to claim");
663
664        // EFFECTS: Update state before external interactions (CEI pattern)
665        if (user.liquidity > 0) {
666            user.yieldGrowthInsideLastX128 = yieldGrowthGlobalX128;
667        }
668
669        user.accumulatedYield = 0;
670        user.lastYieldClaim = block.timestamp;
671        user.totalYieldEarned += totalClaimableYield;
672        totalYieldPaid += totalClaimableYield;
673
674        // INTERACTIONS: External calls last (CEI pattern)
675        require(depositToken.balanceOf(address(this)) >= totalClaimableYield, "Insufficient balance");
676
677        depositToken.safeTransfer(msg.sender, totalClaimableYield);
678        emit YieldClaimed(msg.sender, totalClaimableYield);
679    }
680
681    /**
682     * @dev Claims accumulated commissions using growth accumulator
683     * @notice Claims all accumulated commissions + current pending commissions
684     * @notice Follows CEI (Checks-Effects-Interactions) pattern for maximum security
685     */
686    function claimCommissions() external nonReentrant userExists(msg.sender) {
687        // Update commission growth before calculating pending
688        _updateCommissionGrowth();
689
690        UserAccount storage user = users[msg.sender];
691
692        // CHECKS: Calculate total claimable commissions (accumulated + current pending)
693        uint256 previousAccumulated = user.accumulatedCommissions;
694        uint256 currentPending = 0;
695
696        // Calculate current pending from active liquidity (all levels including BASE)
697        for (uint256 i = 0; i < MAX_UPLINES; i++) {
698            uint256 userLiquidity = userCommissionLiquidity[msg.sender][i];
699            if (userLiquidity > 0) {
700                uint256 lastGrowth = user.commissionGrowthInsideLastX128[i];
701                if (commissionGrowthGlobalX128[i] > lastGrowth) {
702                    uint256 growthDifference = commissionGrowthGlobalX128[i] - lastGrowth;
703                    uint256 levelCommission = (growthDifference * userLiquidity) / Q128;
704                    currentPending += levelCommission;
705                }
706            }
707        }
708
709        uint256 totalClaimable = previousAccumulated + currentPending;
710
711        require(totalClaimable > 0, "No commissions to claim");
712
713        // EFFECTS: Update state before external interactions (CEI pattern)
714        // Update user's commission growth snapshots to current (reset pending calculation)
715        for (uint256 i = 0; i < MAX_UPLINES; i++) {
716            if (userCommissionLiquidity[msg.sender][i] > 0) {
717                user.commissionGrowthInsideLastX128[i] = commissionGrowthGlobalX128[i];
718            }
719        }
720
721        user.accumulatedCommissions = 0;
722        user.lastCommissionClaim = block.timestamp;
723        user.totalCommissionsEarned += totalClaimable;
724        totalCommissionsPaid += totalClaimable;
725
726        // INTERACTIONS: External calls last (CEI pattern)
727        require(depositToken.balanceOf(address(this)) >= totalClaimable, "Insufficient balance");
728
729        depositToken.safeTransfer(msg.sender, totalClaimable);
730        emit CommissionClaimed(msg.sender, totalClaimable);
731    }
732
733    // ============ View Functions ============
734
735    /**
736     * @dev Returns basic user information
737     */
738    function getUserInfo(address _user) external view returns (
739        bool isRegistered,
740        bool isBase,
741        uint256 totalDeposited,
742        uint256 pendingYield,
743        address referrer
744    ) {
745        UserAccount storage user = users[_user];
746
747        return (
748            user.isRegistered,
749            user.isBase,
750            user.totalDeposited,
751            _calculatePendingYield(_user),
752            user.referrer
753        );
754    }
755
756    /**
757     * @dev Returns user's network information
758     */
759    function getUserNetworkInfo(address _user) external view returns (
760        uint256 directReferrals,
761        uint256 uplinesCount,
762        uint256 totalCommissionsEarned
763    ) {
764        UserAccount storage user = users[_user];
765
766        return (
767            user.directReferrals,
768            user.uplines.length,
769            user.totalCommissionsEarned
770        );
771    }
772
773    /**
774     * @dev Returns user's yield information
775     */
776    function getUserYieldInfo(address _user) external view returns (
777        uint256 totalYieldEarned,
778        uint256 pendingYield,
779        uint256 accumulatedYield
780    ) {
781        UserAccount storage user = users[_user];
782
783        return (
784            user.totalYieldEarned,
785            _calculatePendingYield(_user),
786            user.accumulatedYield
787        );
788    }
789
790    /**
791     * @dev Returns user's commission breakdown by level (growth accumulator system)
792     * @notice BASE commissions are included in pendingCommissionsByLevel at their respective levels
793     */
794    function getUserCommissionBreakdown(address _user) external view returns (
795        uint256[12] memory pendingCommissionsByLevel,
796        uint256 totalPendingCommissions
797    ) {
798        require(_user != address(0), "Invalid user address");
799
800        // Calculate pending commissions for each level using growth accumulator
801        // This includes both direct upline commissions and BASE commissions for missing levels
802        for (uint256 i = 0; i < MAX_UPLINES; i++) {
803            pendingCommissionsByLevel[i] = _getCommissionOwedByLevel(_user, i);
804        }
805
806        // Calculate total pending
807        totalPendingCommissions = _calculatePendingCommissions(_user);
808
809        return (
810            pendingCommissionsByLevel,
811            totalPendingCommissions
812        );
813    }
814
815    /**
816     * @dev Returns user's pending commission for a specific level
817     */
818    function getUserCommissionByLevel(address _user, uint256 _level) external view returns (
819        uint256 pendingCommission
820    ) {
821        require(_level >= 1 && _level <= 12, "Invalid level");
822        uint256 levelIndex = _level - 1;
823
824        return _getCommissionOwedByLevel(_user, levelIndex);
825    }
826
827    /**
828     * @dev Returns total pending commissions for user (growth accumulator)
829     */
830    function getUserTotalPendingCommissions(address _user) external view returns (uint256) {
831        return _calculatePendingCommissions(_user);
832    }
833
834    /**
835     * @dev Returns user's commission history
836     */
837    function getUserCommissionHistory(address _user) external view returns (
838        uint256 accumulatedCommissions,
839        uint256 lastCommissionClaim,
840        uint256 totalCommissionsEarned
841    ) {
842        UserAccount storage user = users[_user];
843        return (
844            user.accumulatedCommissions,
845            user.lastCommissionClaim,
846            user.totalCommissionsEarned
847        );
848    }
849
850    /**
851     * @dev Returns user's uplines
852     */
853    function getUserUplines(address _user) external view returns (address[] memory wallets) {
854        UserAccount storage user = users[_user];
855        uint256 length = user.uplines.length;
856
857        wallets = new address[](length);
858
859        for (uint256 i = 0; i < length; i++) {
860            wallets[i] = user.uplines[i].wallet;
861        }
862
863        return wallets;
864    }
865
866    /**
867     * @dev Returns user's deposits
868     */
869    function getUserDeposits(address _user) external view returns (DepositRecord[] memory) {
870        return userDeposits[_user];
871    }
872
873    /**
874     * @dev Calculates pending yield (public)
875     */
876    function getPendingYield(address _user) external view returns (uint256) {
877        return _calculatePendingYield(_user);
878    }
879
880    /**
881     * @dev Calculates pending commissions (public)
882     */
883    function getPendingCommissions(address _user) external view returns (uint256) {
884        return _calculatePendingCommissions(_user);
885    }
886
887    /**
888     * @dev Returns available balance (active liquidity)
889     */
890    function getAvailableBalance(address _user) external view returns (uint256) {
891        UserAccount storage user = users[_user];
892        return user.totalDeposited;
893    }
894
895    /**
896     * @dev Returns user's accumulated yield (already earned, ready to claim)
897     */
898    function getAccumulatedYield(address _user) external view returns (uint256) {
899        return users[_user].accumulatedYield;
900    }
901
902    /**
903     * @dev Returns user's accumulated commissions (already earned, ready to claim)
904     */
905    function getAccumulatedCommissions(address _user) external view returns (uint256) {
906        return users[_user].accumulatedCommissions;
907    }
908
909    /**
910     * @dev Returns detailed yield breakdown (accumulated + pending from active liquidity)
911     */
912    function getYieldBreakdown(address _user) external view returns (
913        uint256 accumulated,
914        uint256 pendingFromActiveLiquidity,
915        uint256 total,
916        uint256 activeLiquidity
917    ) {
918        UserAccount storage user = users[_user];
919        accumulated = user.accumulatedYield;
920        activeLiquidity = user.liquidity;
921
922        if (activeLiquidity > 0) {
923            // Calculate current yield growth
924            uint256 currentYieldGrowthX128 = yieldGrowthGlobalX128;
925            if (totalValueLocked > 0 && block.timestamp >= lastYieldUpdate) {
926                uint256 timeElapsed = block.timestamp - lastYieldUpdate;
927                uint256 yieldPerSecond = DAILY_YIELD_RATE * Q128 / (RATE_DENOMINATOR * SECONDS_IN_DAY);
928                uint256 yieldGrowthIncrease = yieldPerSecond * timeElapsed;
929                currentYieldGrowthX128 += yieldGrowthIncrease;
930            }
931            pendingFromActiveLiquidity = _getYieldOwed(currentYieldGrowthX128, user.yieldGrowthInsideLastX128, activeLiquidity);
932        } else {
933            pendingFromActiveLiquidity = 0;
934        }
935
936        total = accumulated + pendingFromActiveLiquidity;
937    }
938
939    /**
940     * @dev Checks if address is the BASE user
941     */
942    function isBaseUser(address _user) external view returns (bool) {
943        return users[_user].isBase;
944    }
945
946    /**
947     * @dev Returns BASE user address
948     */
949    function getBaseUser() external view returns (address) {
950        return baseUser;
951    }
952
953    /**
954     * @dev Returns the current implementation address
955     * @notice Useful for users to verify they're on the latest version
956     */
957    function getImplementation() external view returns (address) {
958        return ERC1967Utils.getImplementation();
959    }
960
961    // ============ Admin Functions ============
962
963    /**
964     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract.
965     * Called by {upgradeToAndCall}.
966     * @notice Only the contract owner can authorize upgrades
967     */
968    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {
969        emit ContractUpgraded(newImplementation, ++version);
970    }
971
972    // ============ Storage Gap ============
973    /**
974     * @dev Reserve storage slots for future upgrades
975     * This prevents storage collisions when adding new state variables
976     */
977    uint256[50] private __gap;
978
979}

Contract Ownership Renounced

This contract is permanently locked. No one can modify or upgrade it.

Immutable Forever

What does this mean?

The "owner" is the only one who could update this contract. By renouncing ownership, the owner key was permanently destroyed.

No one can change it

Not the developers, not hackers, not anyone. The contract code will run exactly as written forever.

Your funds are safe

The rules are set in stone. Your deposits and earnings will always work exactly as the code defines.

Blockchain Proof

This is recorded on Ethereum blockchain and can be verified by anyone

Event RecordedOwnershipTransferred
Previous Owner0x1b31...5e74
New Owner
0x0000...0000NULL (No One)
Block Number23,914,923
DateDecember 1, 2025
Verify on Etherscan

Earn More with the Ankaa Affiliate Program

Receive 20% of the yield from your direct referrals and 5% from their referrals up to 12 levels deep. Commissions are paid automatically in USDC and can be withdrawn anytime.

20% commission on direct referrals
5% commission up to 12 levels deep
Passive income with instant and automatic USDC payment

Affiliate Network Visualization

Level 1: 20%
Level 2-12: 5%

Your referral network grows exponentially, creating a passive income stream from every level of your network.

Frequently Asked Questions

Join the decentralized revolution of predictable yields.