Q2 2026 Security & Compliance Report67 incidents, $764M in losses, 88% from operational failures.
Get the report →

Audit name:

[SCA] Bullbit | Protocol V1 | Dec2025

Date:

Jan 22, 2026

Table of Content

→Introduction
→Audit Summary
→System Overview
→Potential Risks
→Findings
→Appendix 1. Definitions
→Appendix 2. Scope
→Appendix 3. Additional Valuables
→Disclaimer

Want a comprehensive audit report like this?

Introduction

We express our gratitude to the Bullbit team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.

A hybrid trading protocol with off-chain execution and on-chain settlement: the off-chain sequencer handles order matching and trading operations, while on-chain smart contracts serve as the secure treasury, managing assets and settling deposits/withdrawals through batched updates from the Verifier contract.

Document

NameSmart Contract Code Review and Security Analysis Report for Bullbit
Audited BySeher Saylik, Kornel Światłowski
Approved By
Websitehttps://bullbit.ai/→
Changelog06/01/2026 - Preliminary Report
22/01/2026 - Final Report
PlatformBase
LanguageSolidity
TagsTreasury, Signatures, Upgradable
Methodologyhttps://docs.hacken.io/methodologies/smart-contracts→

Review Scope

Repositoryhttps://github.com/bullbit/Protocol-V1→
Initial Commit7766cc0501b540660fa8845f23592eb7620266f4
Final Commit322258e4622b2430f1249853f2c0c013a038548f

Audit Summary

26Total Findings
19Resolved
5Accepted
2Mitigated

The system users should acknowledge all the risks summed up in the risks section of the report

Documentation quality

  • Documentation was provided; however, parts of it reference outdated features.

  • Technical description is not provided.

Code quality

  • The development environment was configured.

Test coverage

Code coverage of the project is 93.23% (branch coverage).

  • Basic user interactions were tested.

  • Edge cases including race conditions, out-of-order processing were covered.

  • Privileged functions were tested thoroughly.

System Overview

The Governance contract is the central administration contract that manages and configures all system module addresses and the Sequencer address. It stores references to all core contracts and provides a link setContracts() to link all contracts together, along with granular configuration functions for individual modules. It manages forced withdrawal parameters and cross-chain asset settings. The contract is upgradable using the UUPS pattern, with upgrade authorization restricted to the owner.

The Verifier contract is the entry point for the Sequencer to submit batched state updates from the off-chain execution layer to the on-chain settlement layer. It processes batched Pool updates approximately every 5 seconds, including cross-chain deposits, P/L updates, withdrawal requests with signature verification, and forced withdrawals from the InclusionQueue. It also processes batched Vault updates with USDC balance changes, withdrawal requests, and forced withdrawals. The contract maintains batch nonces for sequential processing and tracks last update timestamps to detect sequencer inactivity. It provides an escape hatch for processing expired queue items and enforces limits on forced withdrawals per batch. The contract is upgradable using the UUPS pattern, with upgrade authorization restricted to the owner.

The Pool contract manages user on-chain spot and margin trading balances for multiple tokens. Users can deposit tokens directly on-chain. The Verifier updates profit and loss through state changes and credits cross-chain deposits. WithdrawalManager and Verifier execute withdrawals. Users can initiate forced withdrawals when the sequencer is inactive. The contract is upgradable using the UUPS pattern, with upgrade authorization restricted to the owner.

The Vault contract manages user on-chain spot and margin trading balances for USDC. Users can deposit USDC directly, and the Verifier updates profit and loss through state changes. WithdrawalManager and Verifier can execute USDC withdrawals. Users can initiate forced withdrawals when the sequencer is inactive. The contract is upgradable using the UUPS pattern, with upgrade authorization restricted to the owner.

The WithdrawalManager contract handles EIP-712 signature-based authentication for withdrawal requests and dispatches them to Pool or Vault contracts. It verifies signatures for Pool on-chain withdrawals, Pool cross-chain withdrawals, and Vault withdrawals. The contract maintains nonces per user to prevent replay attacks and tracks cross-chain assets. It processes withdrawal requests on behalf of the Verifier during batch updates. The contract is upgradable using the UUPS pattern, with upgrade authorization restricted to the owner.

The InclusionQueue contract provides a decentralized forced withdrawal queue mechanism that allows users to bypass centralized control when the sequencer is inactive. It maintains separate queues for Pool and Vault withdrawals. Users can submit forced withdrawal requests, and the contract charges a fee in USDC and enforces minimum withdrawal amounts. The contract is upgradable using the UUPS pattern, with upgrade authorization restricted to the owner.

The Pausable contract provides pause and unpause functionality for emergency situations and is used as a base contract by all main system contracts. It allows the owner to toggle the pause state and provides modifiers for access control. The contract is inherited by Pool, Vault, Verifier, InclusionQueue, and WithdrawalManager.

Privileged roles

Each contract contains a combination of access control mechanisms — OwnableUpgradeable for onlyOwner access, and a custom implementation of access restricted to contract addresses stored in the contract’s storage variables. Each contract has the following privileged roles:

Governance

  • Owner

    • _authorizeUpgrade() - Authorizes contract upgrades via the UUPS pattern

    • setContracts() - Configures all system contract addresses and links them together

    • setQueueInfo() - Updates InclusionQueue contract addresses (governance, verifier, pool, vault)

    • setPoolInfo() - Updates Pool contract addresses (governance, verifier, withdrawal manager)

    • setVaultInfo() - Updates Vault contract addresses (governance, verifier, withdrawal manager)

    • setVerifierInfo() - Updates Verifier contract addresses (governance, sequencer, pool, vault, withdrawal manager, queue)

    • setVerifierForcedInfo() - Sets forced withdrawal parameters for Verifier (max forced per batch, forced deadline)

    • setWMInfo() - Updates WithdrawalManager contract addresses (governance, verifier, pool, vault)

    • setWMAssetCrossChain() - Configures which tokens are cross-chain assets in WithdrawalManager

    • setPoolForcedInfo() - Sets forced withdrawal parameters for Pool (sequencer inactivity threshold, force withdraw delay)

    • setVaultForcedInfo() - Sets forced withdrawal parameters for Vault (sequencer inactivity threshold, force withdraw delay)

Pool

  • Owner

    • _authorizeUpgrade() - Authorizes contract upgrades via the UUPS pattern

    • togglePause() - Pauses or unpauses contract operations

  • Governance

    • setInfo() - Updates contract addresses (governance, verifier, withdrawal manager)

    • setForcedInfo() - Sets forced withdrawal parameters (sequencer inactivity threshold, force withdraw delay)

  • Verifier

    • applyStateChanges() - Updates user P/L balances by applying positive or negative deltas per token

    • creditCrossChainDeposit() - Credits user balance for cross-chain deposits

    • executeOnChainWithdrawal() - Executes on-chain withdrawals by transferring tokens to users

  • WithdrawalManager

    • executeOnChainWithdrawal() - Executes on-chain withdrawals by transferring tokens to users

    • executeCrossChainWithdrawal() - Executes cross-chain withdrawals by subtracting balance and emitting an event

Vault

  • Owner

    • _authorizeUpgrade() - Authorizes contract upgrades via the UUPS pattern

    • togglePause() - Pauses or unpauses contract operations

  • Governance

    • setInfo() - Updates contract addresses (governance, verifier, withdrawal manager)

    • setForcedInfo() - Sets forced withdrawal parameters (sequencer inactivity threshold, force withdraw delay)

  • Verifier

    • applyStateChanges() - Updates LP P/L balances by applying positive or negative deltas

    • executeOnChainWithdrawal() - Executes LP withdrawals by transferring USDC to users

  • WithdrawalManager

    • executeOnChainWithdrawal() - Executes LP withdrawals by transferring USDC to users

Verifier

  • Owner

    • _authorizeUpgrade() - Authorizes contract upgrades via the UUPS pattern

    • togglePause() - Pauses or unpauses contract operations

  • Governance

    • setInfo() - Updates contract addresses (governance, sequencer, pool, vault, withdrawal manager, queue)

    • setForcedInfo() - Sets forced withdrawal parameters (max forced per batch, forced deadline)

  • Sequencer

    • submitPoolUpdateBatch() - Submits batched Pool updates, including cross-chain deposits, P/L updates, withdrawal requests, and forced withdrawals

    • submitVaultUpdateBatch() - Submits batched Vault update,s including LP P/L updates, withdrawal requests, and forced withdrawal

WithdrawalManager

  • Owner

    • _authorizeUpgrade() - Authorizes contract upgrades via the UUPS pattern

    • togglePause() - Pauses or unpauses contract operations

  • Governance

    • setInfo() - Updates contract addresses (governance, verifier, pool, vault)

    • setAssetCrossChain() - Configures which tokens are cross-chain assets

  • Verifier

    • processOnChainPoolWithdrawal() - Verifies EIP-712 signature and processes on-chain Pool withdrawals

    • processCrossChainPoolWithdrawal() - Verifies EIP-712 signature and processes cross-chain Pool withdrawals

    • processVaultWithdrawal() - Verifies EIP-712 signature and processes Vault withdrawals

InclusionQueue

  • Owner

    • _authorizeUpgrade() - Authorizes contract upgrades via the UUPS pattern

    • togglePause() - Pauses or unpauses contract operations

    • withdrawFee() - Withdraws collected USDC fees from the contract

    • setAmount() - Updates the minimum withdrawal amount and fee amount

    • withdrawToken() - Withdraws any ERC20 token from the contract

    • withdrawMain() - Withdraws native tokens from the contract

  • Governance

    • setInfo() - Updates contract addresses (governance, verifier, pool, vault)

  • Verifier

    • setPoolQueueIndex() - Updates the Pool queue processing index after processing requests

    • setVaultQueueIndex() - Updates the Vault queue processing index after processing requests

Potential Risks

Off-Chain Trust Assumption in Cross-Chain Withdrawals: When processCrossChainPoolWithdrawal() is executed, the user's on-chain balance in Pool.balances[user][token] is immediately decremented, but no actual token transfer occurs — the function only emits a CrossChainWithdrawal event containing the destination address. Users must fully trust that an off-chain bridge system will observe this event and deliver the equivalent tokens to the specified destination chain. If the off-chain bridge system fails, is compromised, experiences downtime, or never processes the withdrawal event, the user's balance is permanently lost with no on-chain recourse or refund mechanism.

Unrestricted Sequencer Authority Over User Balances: The sequencer has unilateral authority to modify any user's balance via submitPoolUpdateBatch() through arbitrary P/L deltas (applyStateChanges()) and unverified cross-chain deposit credits (creditCrossChainDeposit()), with no on-chain proof or validation mechanism. A compromised or malicious sequencer can drain user funds by crediting fake deposits to an attacker address and withdrawing real tokens, or by applying negative deltas to victim accounts. Users must fully trust the sequencer's integrity, as the escape hatch mechanisms can be circumvented by a minimally active sequencer applying small P/L updates to cancel pending forced withdrawal requests.

Dynamic Array Iteration Gas Limit Risks: The project iterates over large dynamic arrays, which leads to excessive gas costs, risking denial of service due to out-of-gas errors, directly impacting contract usability and reliability.

External Calls in Iteration Risks: Making external calls within loops increases the risk of gas exhaustion, potentially leading to failed transactions and reduced contract reliability, especially when processing large datasets.

Owner's Unrestricted State Modification: The absence of restrictions on state variable modifications by the owner leads to arbitrary changes, affecting contract integrity and user trust, especially during critical operations like minting phases.

Absence of Time-lock Mechanisms for Critical Operations: Without time-locks on critical operations, there is no buffer to review or revert potentially harmful actions, increasing the risk of rapid exploitation and irreversible changes.

Single Points of Failure and Control: The project is fully or partially centralized, introducing single points of failure and control. This centralization can lead to vulnerabilities in decision-making and operational processes, making the system more susceptible to targeted attacks or manipulation.

Administrative Key Control Risks: The digital contract architecture relies on administrative keys for critical operations. Centralized control over these keys presents a significant security risk, as compromise or misuse can lead to unauthorized actions or loss of funds.

Single Entity Upgrade Authority: The token ecosystem grants a single entity the authority to implement upgrades or changes. This centralization of power risks unilateral decisions that may not align with the community or stakeholders' interests, undermining trust and security.

Flexibility and Risk in Contract Upgrades: The project's contracts are upgradable, allowing the administrator to update the contract logic at any time. While this provides flexibility in addressing issues and evolving the project, it also introduces risks if upgrade processes are not properly managed or secured, potentially allowing for unauthorized changes that could compromise the project's integrity and security.

Absence of Upgrade Window Constraints: The contract suite allows for immediate upgrades without a mandatory review or waiting period, increasing the risk of rapid deployment of malicious or flawed code, potentially compromising the system's integrity and user assets.

Non-Refundable Queue Fee: Forced withdrawal requests are always consumed from the queue regardless of execution outcome, meaning users lose their queue fee even if the withdrawal fails (due to contract insolvency, transfer errors, or blacklisting) or executes with a partial amount less than requested. The client stated that this is an intentional design trade-off to prevent head-of-line blocking and ensure queue liveness for all users.

Findings

F-2026-1450finalizeForceWithdrawal Silently Burns User Balance When Pool Has Insufficient Token Balance
Status
fixed
Severity

High
F-2025-1443Blacklisted Token Recipient Permanently Blocks FIFO Forced Withdrawals
Status
fixed
Severity

High
F-2025-1446Fee-on-Transfer / Rebasing Tokens Break Accounting
Status
fixed
Severity

Medium
F-2025-1444Uncoordinated Escape Hatch Mechanisms Cause Permanent forcedWithdrawalRequests Lock When InclusionQueue Executes First
Status
fixed
Severity

Medium
F-2025-1444Uninitialized minWithdrawAmount Allows Zero-Amount Forced Withdrawal Requests To Block Queue Processing
Status
fixed
Severity

Medium
F-2025-1449InclusionQueue Forced Withdrawal Mechanism Limited to USDC
Status
accepted
Severity

Medium
F-2025-1448Forced Withdrawal Flow Is Not Fully Censorship-Resistant
Status
fixed
Severity

Medium
F-2025-1448Lack of Limits and Delay in Forced Withdrawal Parameter Updates
Status
fixed
Severity

Medium
F-2026-1452Pending Force Withdrawal Requests Removed On Balance Update
Status
fixed
Severity

Medium
F-2026-1450Inconsistent Asset Accounting Between Internal Balances and Contract Holdings
Status
accepted
Severity

Medium
Code
―
Title
Status
Severity
F-2026-1450finalizeForceWithdrawal Silently Burns User Balance When Pool Has Insufficient Token Balance
fixed

High
F-2025-1443Blacklisted Token Recipient Permanently Blocks FIFO Forced Withdrawals
fixed

High
F-2025-1446Fee-on-Transfer / Rebasing Tokens Break Accounting
fixed

Medium
F-2025-1444Uncoordinated Escape Hatch Mechanisms Cause Permanent forcedWithdrawalRequests Lock When InclusionQueue Executes First
fixed

Medium
F-2025-1444Uninitialized minWithdrawAmount Allows Zero-Amount Forced Withdrawal Requests To Block Queue Processing
fixed

Medium
F-2025-1449InclusionQueue Forced Withdrawal Mechanism Limited to USDC
accepted

Medium
F-2025-1448Forced Withdrawal Flow Is Not Fully Censorship-Resistant
fixed

Medium
F-2025-1448Lack of Limits and Delay in Forced Withdrawal Parameter Updates
fixed

Medium
F-2026-1452Pending Force Withdrawal Requests Removed On Balance Update
fixed

Medium
F-2026-1450Inconsistent Asset Accounting Between Internal Balances and Contract Holdings
accepted

Medium
1-10 of 26 findings

Identify vulnerabilities in your smart contracts.

Appendix 1. Definitions

Severities

When auditing smart contracts, Hacken is using a risk-based approach that considers Likelihood, Impact, Exploitability and Complexity metrics to evaluate findings and score severities.

Reference on how risk scoring is done is available through the repository in our Github organization:

Severity

Description

Critical
Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.

High
High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.

Medium
Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.

Low
Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
  • Severity

    Critical

    Description

    Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.

    Severity

    High

    Description

    High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.

    Severity

    Medium

    Description

    Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.

    Severity

    Low

    Description

    Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.

Potential Risks

The "Potential Risks" section identifies issues that are not direct security vulnerabilities but could still affect the project’s performance, reliability, or user trust. These risks arise from design choices, architectural decisions, or operational practices that, while not immediately exploitable, may lead to problems under certain conditions. Additionally, potential risks can impact the quality of the audit itself, as they may involve external factors or components beyond the scope of the audit, leading to incomplete assessments or oversight of key areas. This section aims to provide a broader perspective on factors that could affect the project's long-term security, functionality, and the comprehensiveness of the audit findings.

Appendix 2. Scope

The scope of the project includes the following smart contracts from the provided repository:

Scope Details

Repositoryhttps://github.com/bullbit/Protocol-V1→
Initial Commit7766cc0501b540660fa8845f23592eb7620266f4
Final Commit322258e4622b2430f1249853f2c0c013a038548f
WhitepaperN/A
Requirementshttps://docs.google.com/document/d/1LU-hAbZboob9CCYRedHQWi4wshr0okDLHA_fE6MlHwI/edit?usp=sharing→
Technical Requirementshttps://docs.google.com/document/d/1LU-hAbZboob9CCYRedHQWi4wshr0okDLHA_fE6MlHwI/edit?usp=sharing→

Assets in Scope

.
Governance.sol - . › Governance.sol
InclusionQueue.sol - . › InclusionQueue.sol
Pausable.sol - . › Pausable.sol
Pool.sol - . › Pool.sol
Vault.sol - . › Vault.sol
Verifier.sol - . › Verifier.sol
WithdrawalManager.sol - . › WithdrawalManager.sol

Appendix 3. Additional Valuables

Verification of System Invariants

During the audit of BullBit / Protocol V1, Hacken followed its methodology by performing fuzz-testing on the project's main functions. Foundry →, a tool used for fuzz-testing, was employed to check how the protocol behaves under various inputs. Due to the complex and dynamic interactions within the protocol, unexpected edge cases might arise. Therefore, it was important to use fuzz-testing to ensure that several system invariants hold true in all situations.

Fuzz-testing allows the input of many random data points into the system, helping to identify issues that regular testing might miss. A specific Echidna fuzzing suite was prepared for this task, and throughout the assessment, 10 invariants were tested over 70000 runs. This thorough testing ensured that the system works correctly even with unexpected or unusual inputs.

Invariant

Test Result

Run Count

Queue indices should only ever increase, never exceed totalPassed10k
User nonces should only ever be >= 0 (start at 0)Passed10k
Processed index should never exceed total requestsPassed10k
Total deposits should be >= total withdrawals trackedPassed10k
Queue request count should be consistentPassed10k
Only the wallet owner can access assets via signed requestPassed-
Overdue requests must be processable via escape hatchPassed-
Non-overdue requests should not be processedPassed-
Pool user internal balance sum will be lover/equal to the contract balancePassed10k
Vault user internal balance sum will be lover/equal to contract balancePassed10k
  • Invariant

    Queue indices should only ever increase, never exceed total

    Test Result

    Passed

    Run Count

    10k

    Invariant

    User nonces should only ever be >= 0 (start at 0)

    Test Result

    Passed

    Run Count

    10k

    Invariant

    Processed index should never exceed total requests

    Test Result

    Passed

    Run Count

    10k

    Invariant

    Total deposits should be >= total withdrawals tracked

    Test Result

    Passed

    Run Count

    10k

    Invariant

    Queue request count should be consistent

    Test Result

    Passed

    Run Count

    10k

    Invariant

    Only the wallet owner can access assets via signed request

    Test Result

    Passed

    Run Count

    -

    Invariant

    Overdue requests must be processable via escape hatch

    Test Result

    Passed

    Run Count

    -

    Invariant

    Non-overdue requests should not be processed

    Test Result

    Passed

    Run Count

    -

    Invariant

    Pool user internal balance sum will be lover/equal to the contract balance

    Test Result

    Passed

    Run Count

    10k

    Invariant

    Vault user internal balance sum will be lover/equal to contract balance

    Test Result

    Passed

    Run Count

    10k

Additional Recommendations

The smart contracts in the scope of this audit could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like ownership changes or proxy upgrades, as well as unexpected fund manipulations, including large withdrawals or minting events. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended.

To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:

  • Detecting changes to ownership or critical permissions.

  • Monitoring large or unexpected transactions and minting events.

  • Pausing operations when irregularities are identified.

These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations while maintaining smooth operations.

Frameworks and Methodologies

This security assessment was conducted in alignment with recognised penetration testing standards, methodologies and guidelines, including the NIST SP 800-115 – Technical Guide to Information Security Testing and Assessment →, and the Penetration Testing Execution Standard (PTES) →, These assets provide a structured foundation for planning, executing, and documenting technical evaluations such as vulnerability assessments, exploitation activities, and security code reviews. Hacken’s internal penetration testing methodology extends these principles to Web2 and Web3 environments to ensure consistency, repeatability, and verifiable outcomes.

Disclaimer