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.
How It Works
Start earning predictable yields in three simple steps
Deposit USDC
Deposit USDC into the Ankaa Yield Pool on Ethereum network.
Earn 0.4% Daily Yield
Your yield compounds every second in real time.
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}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
OwnershipTransferred23,914,923December 1, 2025Earn 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.
Affiliate Network Visualization
Your referral network grows exponentially, creating a passive income stream from every level of your network.