As of 25 Oct 2026, BEBE has 3 audit reports by 1 security firm on Trustblock; the latest is by CertiK, dated 19 Mar 2025. Those reports list no open critical findings.
Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Be Chain - ValidatorSmartContract | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Beyond Gaming Chain - ValidatorSmartContract | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| BEBE | Security firm: CertiK | Date: | Scope: Not recorded (see the report) | 1 critical finding, fixed | Open · skynet.certik.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 1 |
| High | 1 | 0 | 3 |
| Medium | 0 | 0 | 8 |
| Low | 11 | 0 | 25 |
### 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.
Open the reportSigned 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.
Open the reportBoth 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.
Open the reportIf 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.
Open the reportIn 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.
Open the reportThe function `claimIncome()` allows users to claim their percentage of mined tokens the day after they are mined. However, the timestamp at which a user is able to be claimed will differ, and those who stake in a block whose `block.timestamp` is a multiple of `SECONDS_PER_DAY` will be able to claim before any other users. In general, the lower the value of the timestamp of the block when staking modulo `SECONDS_PER_DAY`, the sooner they will be able to claim tokens. This creates an unfair advantage as users who stake at a more advantageous `block.timestamp` will be able to sell tokens before other users.
Open the reportThe lines cited above set variables to their default value. The keyword `delete` can be used in their place as it more closely matches the semantics of the logic.
Open the reportThe function `importStake()` can only be called by the owner and is designed to be able to add users stakes without the need for the user to transfer tokens or call `stake()`. However, this function will update the stored `totalPower` for the input `stakeTime` date, which in some cases can cause rewards to be misallocated. If the `stakeTime` is for a date that has not yet been mined and no stakes are made for that date when it is reached, then it will cause `_incomePools[incomeDay].totalPower` to be nonzero, so that the function `confirmAddIncomePool()` will not compute the total power for that income day and thus allocate funds based only on the stakes added via `importStake()`, when it should also include the previous stakes in the calculation. If the `stakeTime` is for a date where some users have already claimed their income, then it will add to the `totalPower`, however, as some amount was already claimed this will cause more than the `poolAmount` to be allocated so that the last users will be unable to claim all tokens they are entitled to. In addition, there are other cases where inputs may provide incorrect rewards.
Open the report### BEBEStakeManager The function `importStake()` does not perform the following checks: - It does not check `_stakeEnabled` is true. This may be by design as it is a privileged function, however, we would like to ensure it aligns with the design intent. - It does not check if `_userActiveStake[staker[i]]` is already staked. This can override a users stake unexpectedly. - It does not check that the input `staker[i]` is not `address(0)`. If it is set to the zero address accidentally it will cause income to be unable to be claimed. - It does not check that the `amount[i]` are greater than or equal to the `MINIMUM_STAKE` and less than or equal to the `MAXIMUM_STAKE`. This can allow any amount to be staked. - It does not check the length of the `staker`, `stakeTime`, and `amount` arrays are the same. If the lengths differ, then the inputs are likely incorrect and may cause incorrect values to be set.
Open the reportFunctions that update state variables should emit relevant events as notifications.
Open the reportWhen calculating the percentage of the `poolAmount` that a users should be given in a day it rounds down. As such, after all users claim for that day, the entire `poolAmount` will not be claimed, so that the remaining amount will be locked in the contract.
Open the reportThe `MINIMUM_STAKE` and `MAXIMUM_STAKE` parameters were removed so that users can now stake any amount of tokens. This can lead to greater rounding errors for users as there can be a larger difference between users stakes. However, there are 15 decimals of precision used so we give this a *minor* severity. For example if a user staked only 10 tokens and another user staked 1e16 tokens so that the total power is 1e16+10. Then the calculated income for the user who staked 10 tokens (so that instance.amount=10): ```solidity income = (((instance.amount * DECIMAL_SCALER) / _incomePools[day].totalPower) * _incomePools[day].poolAmount) / DECIMAL_SCALER; ``` Will be (((10 * 1e15) / (1e16+10)) * _incomePools[day].poolAmount) / 1e15 However (10 * 1e15) / (1e16+10) = 1e16/(1e16+10) will round down to zero so that this amount will be equal to 0. In addition, this allows a user to accidentally stake a small amount, in which case they would need to stake with a different address if they wish to correct their mistake. Note that at the end stages of mining, there will a small amount of tokens mined, that will not be able to be distributed as income. In the extreme case when `mine()` is called it will mine only 1 token. Unless there is a single user, then this will not be able to be claimed by any user.
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="bebe"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something 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