This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.
// Reconstructed from bytecodefunctionreRegister()externalnonReentrantwhenRegistrationOpen{require(isRegistered[msg.sender],"Not registered");require(!participants[msg.sender].isActive,"Already active");// Calculate cooldown end timeuint256cooldownEnd=participants[msg.sender].lastCheckAt+cooldownPeriod;require(block.timestamp>=cooldownEnd,"Cooldown period not expired");// Re-validate balanceuint256balance=IERC20(sentToken).balanceOf(msg.sender);require(balance>=minimumBalanceThreshold,"Balance below threshold");// Re-validate Hive eligibilityrequire(IHive(hiveContract).checkRegistrationEligibility(msg.sender),"Not eligible");// Update participant statusparticipants[msg.sender].isActive=true;participants[msg.sender].lastCheckAt=block.timestamp;participants[msg.sender].reRegistrationCount++;// Add history entryparticipantHistory[msg.sender].push(HistoryEntry({timestamp:block.timestamp,wasActive:true,eventType:EventType.ReRegistered
}));totalRegistrations++;totalActiveMembers++;emitReRegistered(msg.sender,block.timestamp,cooldownEnd);}
Admin Functions
Function: setRegistrationOpen(bool)
Toggles whether new registrations and re-registrations are permitted. Owner-only control.
ATTRIBUTE
VALUE
Selector
0x30067bc8
Parameters
bool _isOpen
Access
Owner only
Modifiers
onlyOwner
FLAG
OBSERVATION
△
Owner can disable registration at any time
△
No time lock or warning period
☑
Emits event for transparency
STEP
ACTION
1
Verify caller is owner
2
Update registrationOpen flag
3
Emit RegistrationStatusChanged(_isOpen) event
VARIABLE
CHANGE
registrationOpen
Update to _isOpen value
CONDITION
REVERT MESSAGE
msg.sender != owner
"Ownable: caller is not the owner"
// Reconstructed from bytecodefunctionsetRegistrationOpen(bool_isOpen)externalonlyOwner{registrationOpen=_isOpen;emitRegistrationStatusChanged(_isOpen);}
Function: updateContracts(address,address)
Updates the external contract addresses for SENT token and Hive eligibility verification. Owner-only control.
ATTRIBUTE
VALUE
Selector
0xe692c49f
Parameters
address _sentToken, address _hiveContract
Access
Owner only
Modifiers
onlyOwner
FLAG
OBSERVATION
△
Owner can swap dependencies at any time
△
No validation that addresses are contracts
△
No time lock or migration period
☒
Changing contracts could break existing registrations
CONDITION
REQUIREMENT
Valid Addresses
Both addresses should be non-zero (likely validated)
// Reconstructed from bytecodefunctionupdateContracts(address_sentToken,address_hiveContract)externalonlyOwner{require(_sentToken!=address(0),"Invalid SENT token address");require(_hiveContract!=address(0),"Invalid Hive contract address");sentToken=_sentToken;hiveContract=_hiveContract;emitContractsUpdated(_sentToken,_hiveContract);}
Updates configuration parameters for minimum balance threshold, balance check percentage, and cooldown period. Owner-only control with bounds validation.
// Reconstructed from bytecodefunctionupdateConfiguration(uint256_minBalance,uint256_checkPercentage,uint256_cooldown)externalonlyOwner{if(_minBalance==0)revertInvalidConfiguration();if(_checkPercentage<1||_checkPercentage>100)revertInvalidConfiguration();if(_cooldown>2592000)revertInvalidConfiguration();// Max 30 daysminimumBalanceThreshold=_minBalance;balanceCheckPercentage=_checkPercentage;cooldownPeriod=_cooldown;emitConfigurationUpdated(_minBalance,_checkPercentage,_cooldown);}
Function: runDailyCheck()
Iterates through all registered addresses to verify their SENT token balances still meet the threshold. Deactivates members who fall below the required balance. Owner-only control.
ATTRIBUTE
VALUE
Selector
0xde508c8c
Parameters
None
Access
Owner only
Modifiers
onlyOwner, nonReentrant
FLAG
OBSERVATION
△
Reverts if more than 100 participants (must use batch version)
△
Gas cost scales linearly with participant count
☑
Uses staticcall to read balances (no state changes in SENT contract)
// Reconstructed from bytecodefunctionrunDailyCheck()externalonlyOwnernonReentrant{uint256length=registeredAddresses.length;require(length<=100,"Too many participants: use runDailyCheckBatch()");uint256activeCount=0;uint256requiredBalance=(minimumBalanceThreshold*balanceCheckPercentage)/100;for(uint256i=0;i<length;i++){addressparticipant=registeredAddresses[i];// Skip if already inactiveif(!participants[participant].isActive)continue;// Check balanceuint256balance=IERC20(sentToken).balanceOf(participant);if(balance>=requiredBalance){activeCount++;participants[participant].lastCheckAt=block.timestamp;}else{// Deactivate memberparticipants[participant].isActive=false;participants[participant].lastCheckAt=block.timestamp;totalActiveMembers--;// Add history entryparticipantHistory[participant].push(HistoryEntry({timestamp:block.timestamp,wasActive:false,eventType:EventType.BalanceCheckFailed
}));}}lastDailyCheckTime=block.timestamp;emitDailyCheckCompleted(length,activeCount,block.timestamp);}
Function: runDailyCheckBatch(uint256,uint256)
Batch version of daily check that processes a specified range of registered addresses. Enables processing of large registries without hitting gas limits. Owner-only control.
ATTRIBUTE
VALUE
Selector
0x2a65daa5
Parameters
uint256 _startIndex, uint256 _endIndex
Access
Owner only
Modifiers
onlyOwner, nonReentrant
FLAG
OBSERVATION
☑
Allows chunked processing to avoid gas limits
△
Batch size limited to 100 addresses per call
△
Requires owner to track progress across multiple transactions
☑
Validates index bounds to prevent out-of-range errors
CONDITION
REQUIREMENT
Valid Range
_startIndex < _endIndex
Within Bounds
_endIndex <= registeredAddresses.length
Batch Size
_endIndex - _startIndex <= 100
STEP
ACTION
1
Verify caller is owner
2
Validate _startIndex < _endIndex
3
Validate _endIndex <= array length
4
Validate batch size <= 100
5
For each address in range [_startIndex, _endIndex):
5a
Skip if already inactive
5b
Read SENT balance via staticcall
5c
Calculate required balance
5d
If balance < required: deactivate and record history
6
Update lastDailyCheckTime if this is the final batch
// Reconstructed from bytecodefunctionrunDailyCheckBatch(uint256_startIndex,uint256_endIndex)externalonlyOwnernonReentrant{require(_startIndex<_endIndex,"Invalid range");require(_endIndex<=registeredAddresses.length,"Index out of bounds");require(_endIndex-_startIndex<=100,"Batch size exceeds limit");uint256activeCount=0;uint256requiredBalance=(minimumBalanceThreshold*balanceCheckPercentage)/100;for(uint256i=_startIndex;i<_endIndex;i++){addressparticipant=registeredAddresses[i];if(!participants[participant].isActive)continue;uint256balance=IERC20(sentToken).balanceOf(participant);if(balance>=requiredBalance){activeCount++;participants[participant].lastCheckAt=block.timestamp;}else{participants[participant].isActive=false;participants[participant].lastCheckAt=block.timestamp;totalActiveMembers--;participantHistory[participant].push(HistoryEntry({timestamp:block.timestamp,wasActive:false,eventType:EventType.BalanceCheckFailed
}));}}emitDailyCheckBatchCompleted(_startIndex,_endIndex,activeCount);}
Function: batchRegister(address[])
Registers multiple addresses in a single transaction. Owner-only function likely used for initial setup or migration. Bypasses normal registration checks.
ATTRIBUTE
VALUE
Selector
0x1d88bbe5
Parameters
address[] _addresses
Access
Owner only
Modifiers
onlyOwner, nonReentrant
FLAG
OBSERVATION
△
Owner can add addresses without balance or eligibility checks
△
Bypasses normal registration requirements
☒
Could be used to add ineligible addresses
△
Batch size likely limited to avoid gas limits
CONDITION
REQUIREMENT
Non-empty Array
_addresses.length > 0
Valid Addresses
All addresses should be non-zero
STEP
ACTION
1
Verify caller is owner
2
For each address in array:
2a
Skip if already registered
2b
Set isRegistered = true
2c
Add to registeredAddresses array
2d
Create participant record
2e
Increment counters
3
Emit BatchRegistered event with count
VARIABLE
CHANGE
isRegistered[addr]
Set to true for each address
registeredAddresses
Append each new address
participants[addr]
Create records for each address
totalUniqueParticipants
Increment for each new registration
totalRegistrations
Increment for each registration
totalActiveMembers
Increment for each registration
CONDITION
REVERT MESSAGE
msg.sender != owner
"Ownable: caller is not the owner"
_addresses.length == 0
"Empty array"
// Reconstructed from bytecodefunctionbatchRegister(address[]calldata_addresses)externalonlyOwnernonReentrant{require(_addresses.length>0,"Empty array");uint256registered=0;for(uint256i=0;i<_addresses.length;i++){addressaddr=_addresses[i];// Skip if already registeredif(isRegistered[addr])continue;isRegistered[addr]=true;registeredAddresses.push(addr);participants[addr]=Participant({registeredAt:block.timestamp,lastCheckAt:block.timestamp,isActive:true,reRegistrationCount:0});totalUniqueParticipants++;totalRegistrations++;totalActiveMembers++;registered++;}emitBatchRegistered(registered,block.timestamp);}
Function: transferOwnership(address)
Transfers ownership of the contract to a new address. OpenZeppelin Ownable pattern implementation.
// Reconstructed from bytecodefunctiontransferOwnership(addressnewOwner)publicvirtualonlyOwner{require(newOwner!=address(0),"Ownable: new owner is the zero address");_transferOwnership(newOwner);}function_transferOwnership(addressnewOwner)internalvirtual{addressoldOwner=owner();// Update owner in EIP-1967 admin slotStorageSlot.getAddressSlot(ADMIN_SLOT).value=newOwner;emitOwnershipTransferred(oldOwner,newOwner);}
Function: renounceOwnership()
Permanently removes ownership, making the contract ownerless. All owner-only functions become permanently inaccessible. OpenZeppelin Ownable pattern implementation.
// Reconstructed from bytecodefunctionrenounceOwnership()publicvirtualonlyOwner{_transferOwnership(address(0));}
View Functions
Function: isRegistered(address)
Returns whether an address has ever registered, regardless of current active status.
ATTRIBUTE
VALUE
Selector
0xc3c5a547
Parameters
address _addr
Access
Public view
Returns
bool
// Reconstructed from bytecodefunctionisRegistered(address_addr)externalviewreturns(bool){returnisRegistered[_addr];}
Function: isActiveMember(address)
Returns whether an address is currently an active member (registered and passed recent balance checks).
ATTRIBUTE
VALUE
Selector
0x45ecd02f
Parameters
address _addr
Access
Public view
Returns
bool
// Reconstructed from bytecodefunctionisActiveMember(address_addr)externalviewreturns(bool){returnisRegistered[_addr]&&participants[_addr].isActive;}
Function: getParticipant(address)
Returns the full participant record for an address including registration timestamp, last check time, active status, and re-registration count.
ATTRIBUTE
VALUE
Selector
0x7143059f
Parameters
address _addr
Access
Public view
Returns
Participant memory
// Reconstructed from bytecodefunctiongetParticipant(address_addr)externalviewreturns(uint256registeredAt,uint256lastCheckAt,boolisActive,uint256reRegistrationCount){Participantmemoryp=participants[_addr];return(p.registeredAt,p.lastCheckAt,p.isActive,p.reRegistrationCount);}
Function: getParticipantHistory(address)
Returns the complete history array for a participant showing all status changes over time.
ATTRIBUTE
VALUE
Selector
0x78a03c47
Parameters
address _addr
Access
Public view
Returns
HistoryEntry[] memory
// Reconstructed from bytecodefunctiongetParticipantHistory(address_addr)externalviewreturns(HistoryEntry[]memory){returnparticipantHistory[_addr];}
Function: getGlobalStats()
Returns global statistics about the registry including total unique participants, total registrations, active members, and historical member count.
ATTRIBUTE
VALUE
Selector
0x6b4169c3
Parameters
None
Access
Public view
Returns
(uint256, uint256, uint256, uint256)
// Reconstructed from bytecodefunctiongetGlobalStats()externalviewreturns(uint256uniqueParticipants,uint256totalRegistrations,uint256activeMembers,uint256historicalMembers){return(totalUniqueParticipants,totalRegistrations,totalActiveMembers,totalHistoricalMembers
);}
Function: sentToken()
Returns the address of the SENT token contract used for balance verification.
ATTRIBUTE
VALUE
Selector
0x444cef88
Parameters
None
Access
Public view
Returns
address
// Reconstructed from bytecodefunctionsentToken()externalviewreturns(address){returnsentToken;}
Function: minimumBalanceThreshold()
Returns the current minimum SENT token balance required for registration and active membership.
ATTRIBUTE
VALUE
Selector
0x4bb8ed66
Parameters
None
Access
Public view
Returns
uint256
// Reconstructed from bytecodefunctionminimumBalanceThreshold()externalviewreturns(uint256){returnminimumBalanceThreshold;}
Function: registrationOpen()
Returns whether new registrations and re-registrations are currently permitted.
ATTRIBUTE
VALUE
Selector
0x736f88cd
Parameters
None
Access
Public view
Returns
bool
// Reconstructed from bytecodefunctionregistrationOpen()externalviewreturns(bool){returnregistrationOpen;}
Function: owner()
Returns the current owner address from EIP-1967 admin slot. OpenZeppelin Ownable pattern implementation.
ATTRIBUTE
VALUE
Selector
0x8da5cb5b
Parameters
None
Access
Public view
Returns
address
// Reconstructed from bytecodefunctionowner()publicviewvirtualreturns(address){returnStorageSlot.getAddressSlot(ADMIN_SLOT).value;}
Upgrade Functions
Function: proxiableUUID()
Returns the EIP-1967 implementation slot identifier to verify UUPS compatibility. Part of ERC-1822 standard.
ATTRIBUTE
VALUE
Selector
0x52d1902d
Parameters
None
Access
Public view
Returns
bytes32
// Reconstructed from bytecodefunctionproxiableUUID()externalviewreturns(bytes32){return0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;}
Function: upgradeToAndCall(address,bytes)
Upgrades the implementation contract to a new address and optionally calls a function on the new implementation. Owner-only control with no time lock.
ATTRIBUTE
VALUE
Selector
0x4f1ef286
Parameters
address newImplementation, bytes calldata data
Access
Owner only
Modifiers
onlyOwner
FLAG
OBSERVATION
△
Owner can upgrade immediately without warning
△
No time lock or community approval
☑
Validates new implementation has proxiableUUID()
☒
Could introduce malicious logic
CONDITION
REQUIREMENT
Valid Implementation
newImplementation must implement proxiableUUID()
Owner Authorization
Caller must be current owner
STEP
ACTION
1
Verify caller is owner
2
Validate new implementation supports UUPS (proxiableUUID check)
3
Update implementation address in EIP-1967 slot
4
If data.length > 0: delegatecall to new implementation with data
5
Emit Upgraded(newImplementation) event
VARIABLE
CHANGE
Implementation slot (0x360894a1...)
Update to newImplementation
CONDITION
REVERT MESSAGE
msg.sender != owner
"Ownable: caller is not the owner"
Invalid implementation
"ERC1967: new implementation is not UUPS"
Delegatecall fails
Propagate error from new implementation
// Reconstructed from bytecodefunctionupgradeToAndCall(addressnewImplementation,bytesmemorydata
)externalpayableonlyOwner{// Validate new implementationtryIERC1822Proxiable(newImplementation).proxiableUUID()returns(bytes32slot){require(slot==0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,"ERC1967: new implementation is not UUPS");}catch{revert("ERC1967: new implementation is not UUPS");}// Update implementation slotStorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value=newImplementation;// Call initialization function if data providedif(data.length>0){(boolsuccess,bytesmemoryreturndata)=newImplementation.delegatecall(data);require(success,"Upgrade call failed");}emitUpgraded(newImplementation);}