CertiK reviewed BEBE (Beyond Gaming Chain - ValidatorSmartContract) on 7 Mar 2025: 20 findings, 0 critical; 15 marked fixed, 5 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 1 | 0 | 1 |
| Medium | 0 | 0 | 3 |
| Low | 4 | 0 | 11 |
### ValidatorSmartContract In the contract `ValidatorSmartContract`, the role `RootValidator` has authority over the functions listed below. - approveValidator() - disapproveValidator() - revokeValidator() - addRootValidator() - removeRootValidator() - queueAction() - executeAction() - cancelAction() If multiple `RootValidator` accounts are compromised to meet multi-sign threshold, the hacker could exploit this authority to perform operations like approving, disapproving, or revoking on normal validators at will, adding or removing the root validator. The role `RootValidator` also has the authority to queue the execution of the following functions and schedule these functions to be executed after 48 hours. - executeEmergencyWithdraw() - changeState() If multiple `RootValidator` accounts are compromised to meet the multi-sign threshold, the hacker could exploit this authority to schedule operations like withdrawing all native tokens to the emergency wallet or changing the state of the contract.
Signed messages are not properly verified with the current chain ID and the address of the contract, thus allowing attackers to perform replay attacks across chains or contracts (from the old version to the new version). Especially if the team plans to deploy contracts on multiple chains or if the team has deployed multiple versions of contracts, the signature and message hash can be reused in the future.
Both the `revokeValidator` function and the `initiateWithdrawal` function will remove the validator from the active set; whichever transaction is mined first “wins.” The second transaction will check `!info.isActive` and will revert with a `NotActiveValidatorError()`. ```solidity function revokeValidator(address validator) external onlyAdmin nonZeroAddress(validator) whenNotPaused { ... if (!info.isActive) revert NotActiveValidatorError(); ... revokedValidators[validator] = true; ... delete validatorInfo[validator]; ... } function initiateWithdrawal() external whenNotPaused whenNotEmergency { if (revokedValidators[msg.sender]) revert RevokedValidatorError(); ... if (!info.isActive) revert NotActiveValidatorError(); ... delete validatorInfo[msg.sender]; ... } ``` If the `revokeValidator` is used to permanently remove a validator (for malicious behavior or policy violation), this could be an issue since the validator can see the admin’s transaction in the mempool, they can “front‐run” the admin by calling `initiateWithdrawal` with a higher gas price (or via a private/Flashbots/MEV transaction). Once the validator’s transaction is included first, `info.isActive` becomes false, so the subsequent `revokeValidator` from the admin will revert. As a result, the validator escapes without the “revoked” label and they could rejoin in the future if they want.
If there are two or more time-locked actions that depend on the contract’s state, the order in which they are executed can lead to different results. One action can invalidate the other if the state is changed first. Consider the following example: - Action A: `changeState(ContractState.ACTIVE)` - Action B: `executeEmergencyWithdraw()` When the contract’s `contractState` is `EMERGENCY`: - If Action B (withdraw) is executed first, it will succeed because the contract is still in an `EMERGENCY` state. - If Action A (change state to `ACTIVE`) is executed first, Action B will revert on its subsequent execution attempt because the contract would no longer be in an `EMERGENCY` state. In other words, the final outcome depends on the order in which these timelocked actions are executed—a detail not enforced on-chain.
In the contract's constructor, the initial validators are registered with a `stake` value of `0`. Normally, in a proof-of-stake (PoS) consensus mechanism, validators are selected for block production based on the proportion of their staked amount relative to the total staked amount. However, the current implementation assigns initial validators with no staked funds. ```solidity for (uint i = 0; i < length; i++) { ... validatorInfo[validator] = ValidatorInfo({ stake: 0, index: uint32(i), registeredTime: uint32(block.timestamp), activatedTime: uint32(block.timestamp), isActive: true, isWaiting: false, ownerAddress: validator }); } ``` This raises the concern of whether these initial validators are treated differently by the consensus algorithm. This may undermine the security assumptions of a PoS system and initial validators might have an unfair advantage over later participants who are required to meet the `requiredStake` condition when registering.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (1)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us