Functions
DISCLAIMER // NFA // DYOR
This analysis is based on decompiled bytecode and 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 | |
|---|---|
| Proxy Address | 0x2a9848c39fff51eb184326d65f1238cc36764069 (etherscan) |
| Implementation Address | 0xe528d428c188a80c4824aad89211d292f9a62d77 (etherscan) |
| Network | Ethereum Mainnet |
| Analysis Date | 2025-12-14 |
Function Summary
| CATEGORY | NUM FUNCTIONS |
|---|---|
| Initialization | 1 |
| User Functions | 5 |
| Operator Functions | 8 |
| Admin Functions | 12+ |
| View Functions | 15+ |
Initialization Function
initialize(address tokenAddress)
One-time initialization of the contract with the external token address.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:243-258 |
| Parameters | tokenAddress |
| Access | Anyone (but only works once) |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Can only be called once (prevents re-initialization) |
| ☑ | First caller becomes admin |
| △ | No protection on who can initialize (should be called immediately after deployment) |
| △ | Token address cannot be changed after initialization |
| STEP | ACTION |
|---|---|
| 1 | Validate not already initialized |
| 2 | Validate token address is not zero |
| 3 | Store token address |
| 4 | Set initialized flag to 1 |
| 5 | Set caller as admin |
| 6 | Initialize pause states to 0 |
| 7 | Set minimum balance requirement to 0 |
| 8 | Set snapshot interval to 3600 (1 hour) |
| 9 | Set last snapshot time to block.timestamp |
| 10 | Emit initialization event |
| VARIABLE | CHANGE |
|---|---|
tokenAddress |
Set to provided token address |
initialized |
Set to 1 |
adminAddress |
Set to msg.sender |
minBalance |
Set to 0 |
snapshotInterval |
Set to 3600 |
lastSnapshotTime |
Set to block.timestamp |
| CONDITION | REVERT MESSAGE |
|---|---|
| Already initialized | "Already initialized" |
| Token address is zero | "Invalid XCL token" |
User Functions
register()
Registers the caller to participate in rewards distribution based on their token holdings.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:431-458 |
| Selector | N/A (decompiled) |
| Access | External - Any eligible address |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Registration is a one-time action per address |
| ☑ | Initial reward debt prevents claiming past rewards |
| △ | User's token balance is snapshot at registration time |
| △ | External token balance query could fail if token contract is malicious |
| CONDITION | REQUIREMENT |
|---|---|
| Contract state | Not paused |
| Registration | Must be open |
| Caller status | Not blacklisted |
| Caller status | Not already registered |
| Token balance | >= minimum requirement |
| STEP | ACTION |
|---|---|
| 1 | Check all preconditions |
| 2 | Query caller's token balance from external contract |
| 3 | Mark caller as registered |
| 4 | Add caller to participants array |
| 5 | Store caller's index in mapping |
| 6 | Increment participantCount |
| 7 | Store caller's balance snapshot |
| 8 | Record registration timestamp |
| 9 | Add balance to total tracked |
| 10 | Calculate initial reward debt |
| VARIABLE | CHANGE |
|---|---|
isRegistered[caller] |
Set to 1 |
participantIndex[caller] |
Set to index in array |
participants |
Caller added to array |
participantCount |
Incremented |
userBalance[caller] |
Set to token balance |
lastUpdate[caller] |
Set to block.timestamp |
totalTrackedBalance |
Increased by caller's balance |
rewardDebt[caller] |
Set to balance * rewardsPerShare / 10^18 |
| CONDITION | REVERT MESSAGE |
|---|---|
| Contract paused | "Contract is paused" |
| Registration closed | "Registration closed" |
| Caller blacklisted | "Address blacklisted" |
| Already registered | "Already registered" |
| Insufficient balance | "Insufficient XCL balance" |
| External call fails | Depends on token contract |
flowchart TD
Start([register called])
Start --> Check1{Paused?}
Check1 -->|Yes| Revert1[Revert: Contract is paused]
Check1 -->|No| Check2{Registration Open?}
Check2 -->|No| Revert2[Revert: Registration closed]
Check2 -->|Yes| Check3{Blacklisted?}
Check3 -->|Yes| Revert3[Revert: Address blacklisted]
Check3 -->|No| Check4{Already Registered?}
Check4 -->|Yes| Revert4[Revert: Already registered]
Check4 -->|No| ExtCall[Query Token Balance]
ExtCall --> Check5{Balance >= Minimum?}
Check5 -->|No| Revert5[Revert: Insufficient balance]
Check5 -->|Yes| Process[Register User]
Process --> UpdateState[Update Storage]
UpdateState --> CalcDebt[Calculate Reward Debt]
CalcDebt --> EmitEvent[Emit Registration Event]
EmitEvent --> Success([Registration Complete])
style Start fill:#e1f5ff
style Success fill:#e1ffe1
style Revert1 fill:#ffe1e1
style Revert2 fill:#ffe1e1
style Revert3 fill:#ffe1e1
style Revert4 fill:#ffe1e1
style Revert5 fill:#ffe1e1
claimRewards()
Allows registered users to claim their accumulated ETH rewards.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:568-599 |
| Access | External - Registered users |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Uses rewards-per-share accounting |
| ☑ | Multiple safety checks ensure solvency |
| △ | ETH transfer uses raw call |
| △ | State updated after external call (reentrancy risk) |
| △ | Caller must be able to receive ETH |
| CONDITION | REQUIREMENT |
|---|---|
| Contract state | Not paused |
| Caller status | Not blacklisted |
| Caller rewards | Not individually paused |
| Caller status | Is registered |
| Pending rewards | > 0 |
| Contract balance | Sufficient ETH |
| Reward pool | Sufficient balance |
| STEP | ACTION |
|---|---|
| 1 | Validate all preconditions |
| 2 | Calculate pending rewards |
| 3 | Decrease reward pool by pending amount |
| 4 | Increase totalClaimed[caller] |
| 5 | Increase totalDistributed |
| 6 | Update reward debt |
| 7 | Transfer ETH to caller |
| 8 | Emit claim event |
| VARIABLE | CHANGE |
|---|---|
rewardPool |
Decreased by claimed amount |
totalClaimed[caller] |
Increased by claimed amount |
totalDistributed |
Increased by claimed amount |
rewardDebt[caller] |
Updated to current rewardsPerShare * balance / 10^18 |
| CONDITION | REVERT MESSAGE |
|---|---|
| Contract paused | "Contract is paused" |
| Caller blacklisted | "Address blacklisted" |
| Rewards paused | "Rewards paused for this address" |
| Not registered | "Not registered" |
| No rewards | "No rewards to claim" |
| Low ETH balance | "Insufficient contract balance" |
| Low reward pool | "Insufficient reward pool" |
| Transfer failed | "ETH transfer failed" |
sequenceDiagram
participant User
participant Contract
User->>Contract: claimRewards()
Note over Contract: Validate preconditions
alt Any check fails
Contract-->>User: Revert with error
else All checks pass
Contract->>Contract: Calculate pending rewards
Note over Contract: balance * rewardsPerShare / 1e18<br/>minus rewardDebt
Contract->>Contract: Update state
Contract->>User: Transfer ETH
alt Transfer succeeds
Contract->>Contract: Emit claim event
Contract-->>User: Return amount claimed
else Transfer fails
Contract-->>User: Revert: ETH transfer failed
end
end
deregister()
Allows users to unregister from the rewards system, claiming any pending rewards first.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:640-678 |
| Access | External - Registered users |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Attempts to claim rewards automatically |
| ☑ | Does not revert if reward claim fails (allows exit) |
| ☑ | Uses array compaction to avoid gaps |
| ☑ | Fully clears user state |
| △ | No cooldown or penalty for deregistering |
| CONDITION | REQUIREMENT |
|---|---|
| Contract state | Not paused |
| Caller status | Is registered |
| STEP | ACTION |
|---|---|
| 1 | Check preconditions |
| 2 | If pending rewards exist and sufficient funds, claim them |
| 3 | Decrease total tracked balance by user's balance |
| 4 | If user is not last in array, compact array |
| 5 | Remove last array element |
| 6 | Clear all user state |
| 7 | Decrement participantCount |
| 8 | Emit deregistration event |
| VARIABLE | CHANGE |
|---|---|
totalTrackedBalance |
Decreased by user's balance |
participants |
User removed, array compacted |
participantCount |
Decremented |
isRegistered[caller] |
Cleared |
userBalance[caller] |
Cleared |
lastUpdate[caller] |
Cleared |
rewardDebt[caller] |
Cleared |
participantIndex[caller] |
Cleared |
| CONDITION | REVERT MESSAGE |
|---|---|
| Contract paused | "Contract is paused" |
| Not registered | "Not registered" |
| ETH transfer fails | "ETH transfer failed" (during reward claim) |
flowchart TD
Start([deregister called])
Start --> Check1{Paused?}
Check1 -->|Yes| Revert1[Revert: Contract is paused]
Check1 -->|No| Check2{Registered?}
Check2 -->|No| Revert2[Revert: Not registered]
Check2 -->|Yes| Check3{Has Pending Rewards?}
Check3 -->|Yes| Check4{Sufficient Funds?}
Check4 -->|Yes| ClaimRewards[Claim Pending Rewards]
Check4 -->|No| SkipClaim[Skip Reward Claim]
Check3 -->|No| SkipClaim
ClaimRewards --> UpdateTotal[Decrease Total Balance]
SkipClaim --> UpdateTotal
UpdateTotal --> Check5{Last in Array?}
Check5 -->|Yes| RemoveLast[Remove from Array End]
Check5 -->|No| Compact[Move Last Participant<br/>to User's Position]
Compact --> RemoveLast
RemoveLast --> ClearState[Clear User State]
ClearState --> DecrementCount[Decrement Participant Count]
DecrementCount --> EmitEvent[Emit Deregistration Event]
EmitEvent --> Success([Deregistration Complete])
style Start fill:#e1f5ff
style Success fill:#e1ffe1
style Revert1 fill:#ffe1e1
style Revert2 fill:#ffe1e1
updateBalanceSnapshot()
Allows users to manually update their token balance snapshot, claiming any pending rewards first.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:601-638 |
| Access | External - Registered users |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Rate-limited by snapshot interval |
| ☑ | Automatically claims pending rewards from old balance |
| △ | Essential for users whose token balance changes |
| △ | Relies on external token contract being accurate |
| CONDITION | REQUIREMENT |
|---|---|
| Contract state | Not paused |
| Caller status | Is registered |
| Time elapsed | Snapshot interval elapsed since last update |
| STEP | ACTION |
|---|---|
| 1 | Check preconditions |
| 2 | Query current token balance from external contract |
| 3 | If balance changed from stored balance |
| 4 | - If pending rewards exist, claim them |
| 5 | - Update stored balance to new balance |
| 6 | - Update timestamp |
| 7 | - Adjust total tracked balance |
| 8 | - Recalculate reward debt |
| CONDITION | REVERT MESSAGE |
|---|---|
| Contract paused | "Contract is paused" |
| Not registered | "Not registered" |
| Too soon | "Snapshot interval not elapsed" |
| External call fails | Depends on token contract |
canRegister(address sender)
Checks if an address is eligible to register and returns status message.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:375-395 |
| Access | Public (view) |
| CONDITION | REQUIREMENT |
|---|---|
| Contract state | Not paused |
| Registration | Is open |
| Address status | Not blacklisted |
| Address status | Not already registered |
| Token balance | >= minimum requirement |
| TYPE | DESCRIPTION |
|---|---|
bool |
Can register |
| Multiple strings | Status/error description |
Operator Functions
depositRewards()
Deposits ETH into the reward pool for distribution to participants.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:210-221 |
| Access | Admin or Operator |
| Payable | Yes |
| FLAG | OBSERVATION |
|---|---|
| ☑ | ETH sent is immediately available for distribution |
| ☑ | Rewards distributed proportionally to token holdings |
| △ | If no participants, ETH accumulates without updating rewardsPerShare |
| △ | No cap on deposit amount |
| △ | Critical function for system operation |
| STEP | ACTION |
|---|---|
| 1 | Validate caller has permission |
| 2 | Validate ETH amount > 0 |
| 3 | Add ETH to reward pool |
| 4 | If participants exist, increase rewardsPerShare |
| 5 | Emit deposit event |
| VARIABLE | CHANGE |
|---|---|
rewardPool |
Increased by msg.value |
rewardsPerShare |
Increased by (10^18 * depositAmount) / totalTrackedBalance |
sequenceDiagram
participant Operator
participant Contract
Operator->>Contract: depositRewards() + ETH
Note over Contract: Check operator permission
alt Not operator
Contract-->>Operator: Revert: Not operator
else Is operator
alt Amount <= 0
Contract-->>Operator: Revert: Amount must be positive
else Amount > 0
Contract->>Contract: Increase reward pool
alt Participants exist
Contract->>Contract: Calculate rewards per share
Contract->>Contract: Increase rewardsPerShare
else No participants
Note over Contract: ETH stored but not<br/>distributed yet
end
Contract->>Contract: Emit deposit event
Contract-->>Operator: Success
end
end
pause() / unpause()
Controls global pause state of the contract.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:137-153 |
| Access | Admin or Operator |
| FUNCTION | ACTION |
|---|---|
pause() |
Sets paused flag to 1 |
unpause() |
Sets paused flag to 0 |
openRegistration() / closeRegistration()
Controls whether new users can register.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:155-181 |
| Access | Admin or Operator |
blacklistAddress(address)
Adds an address to the blacklist, preventing registration and claims.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:273-284 |
| Access | Admin or Operator |
| FLAG | OBSERVATION |
|---|---|
| △ | Does not automatically deregister if user already registered |
| △ | Prevents future registrations and claims |
| △ | Separate from pause functionality (address-specific) |
pauseUserRewards(address)
Pauses rewards for a specific registered user.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:286-297 |
| Access | Admin or Operator |
forceDeregister(address, bool)
Forcibly deregisters a user, optionally claiming their rewards first.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:680-727 |
| Access | Admin or Operator |
| FLAG | OBSERVATION |
|---|---|
| △ | Admin/operator can remove users without their consent |
| ☑ | Can choose whether to claim user's rewards first |
| △ | Clears pause status when removing |
globalBalanceSnapshot()
Updates token balance snapshots for all registered participants.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:491-529 |
| Access | Anyone |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Can be called by anyone (public utility function) |
| ☑ | Rate-limited by interval to prevent spam |
| △ | Gas cost scales linearly with participant count |
| △ | Critical for maintaining accurate distribution |
flowchart TD
Start([globalSnapshot called])
Start --> Check1{Interval Elapsed?}
Check1 -->|No| Revert1[Revert: Interval not elapsed]
Check1 -->|Yes| Init[Initialize: idx=0, sum=0]
Init --> Loop{idx < participants.length?}
Loop -->|No| UpdateTotal[Update Total Tracked Balance]
Loop -->|Yes| QueryBalance[Query participant balance]
QueryBalance --> Check2{Balance Changed?}
Check2 -->|No| AddToSum[Add old balance to sum]
Check2 -->|Yes| UpdateUser[Update user balance]
UpdateUser --> EmitUserEvent[Emit balance change event]
EmitUserEvent --> AddNewToSum[Add new balance to sum]
AddToSum --> Increment[idx++]
AddNewToSum --> Increment
Increment --> Loop
UpdateTotal --> UpdateTime[Update last snapshot time]
UpdateTime --> EmitGlobal[Emit global snapshot event]
EmitGlobal --> Success([Snapshot Complete])
style Start fill:#e1f5ff
style Success fill:#e1ffe1
style Revert1 fill:#ffe1e1
Admin Functions
addOperator(address newOperator)
Grants operator role to a new address.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:260-271 |
| Access | Admin only |
| CONDITION | REQUIREMENT |
|---|---|
| Caller | Is admin |
| Address | Not zero |
| Status | Not already an operator |
queueAdminTransfer(address) / acceptAdminTransfer()
Two-step admin transfer with 24-hour timelock.
| ATTRIBUTE | VALUE |
|---|---|
| Access | Admin (queue) / Pending Admin (accept) |
| Timelock | 24 hours |
| FLAG | OBSERVATION |
|---|---|
| ☑ | Two-step process prevents accidental transfers |
| ☑ | Pending admin must actively accept |
| ☑ | 24-hour delay provides warning |
queueOperatorRemoval(address) / executeOperatorRemoval(address, uint256)
Queues and executes operator removal with 24-hour timelock.
| ATTRIBUTE | VALUE |
|---|---|
| Access | Admin only |
| Timelock | 24 hours |
queueParameterChange(uint256) / executeParameterChange(uint256, uint256)
Queues and executes minimum balance requirement change with timelock.
| ATTRIBUTE | VALUE |
|---|---|
| Access | Admin only |
| Timelock | 24 hours |
queueEmergencyWithdrawal(...) / executeEmergencyWithdrawal(...)
Queues and executes emergency withdrawal of ETH or ERC20 tokens.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:350-360 (queue), 531-566 (execute) |
| Access | Admin only |
| Timelock | 24 hours |
| FLAG | OBSERVATION |
|---|---|
| △ | Critical emergency function to recover funds |
| △ | Can withdraw any ERC20 token (including the tracked token) |
| △ | Can withdraw ETH from reward pool |
| ☑ | 24-hour timelock provides warning period |
| △ | Could be used to drain the contract |
manualBalanceUpdate(address, uint256)
Allows admin to manually set a user's tracked token balance.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:362-373 |
| Access | Admin only |
| FLAG | OBSERVATION |
|---|---|
| △ | Powerful admin function that can manipulate reward distribution |
| △ | Bypasses external token contract query |
| △ | Could be used to fix discrepancies or manipulate system |
| △ | No validation that new balance matches actual token balance |
cancelQueuedAction(bytes32)
Cancels a queued timelock action.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:192-200 |
| Access | Admin only |
removeFromBlacklist(address)
Removes an address from the blacklist.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:233-241 |
| Access | Admin only |
unpauseUserRewards(address)
Unpauses rewards for a specific user.
| ATTRIBUTE | VALUE |
|---|---|
| Location | Implementation:223-231 |
| Access | Admin only |
View Functions
Contract State Functions
| FUNCTION | RETURNS | PURPOSE |
|---|---|---|
initialized() |
bool |
Whether contract has been initialized |
admin() |
address |
Current admin address |
pendingAdmin() |
address |
Pending admin during transfer |
paused() |
bool |
Whether contract is globally paused |
rewardPool() |
uint256 |
Total ETH available for rewards |
totalDistributed() |
uint256 |
Cumulative ETH distributed |
participantCount() |
uint256 |
Number of registered participants |
User State Functions
| FUNCTION | RETURNS | PURPOSE |
|---|---|---|
isRegistered(address) |
bool |
Whether address is registered |
blacklisted(address) |
bool |
Whether address is blacklisted |
operators(address) |
bool |
Whether address has operator role |
totalClaimed(address) |
uint256 |
Total rewards claimed by address |
participants(uint256) |
address |
Participant at specific index |
Statistics Functions
| FUNCTION | RETURNS | PURPOSE |
|---|---|---|
getStats() |
Multiple | Comprehensive contract statistics |
pendingRewards(address) |
uint256 |
Pending reward amount for address |
userStats(address) |
Multiple | Share percentage, tracked balance, current balance |
getParticipants(uint256, uint256) |
address[], uint256[] |
Paginated list of participants and balances |