Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · October 2025

Contract Updates

for Bounce Tech

Guardian's review of Contract Updates for Bounce Tech, published October 2025. The report records 20 findings across 2 review rounds, including 3 high and 4 medium.

Published
Review window
October 6 to 17, 2025
Rounds
Main Review, Remediation Review
Language
Solidity
Chains
Hyperliquid, Offchain
Sector
Derivatives
  • 0 Critical
  • 3 High
  • 4 Medium
  • 3 Low
  • 10 Informational

20 acknowledged

Scope

Findings 20

Main Review

11 findings · October 6 to 13, 2025
  1. H-01 High Referral Rebates Credited To Agent Wallet Logical Error Acknowledged
    Location
    src/LeveragedToken.sol:353
    Round
    Main Review

    Description

    When agents execute prepared redemptions, the contract calls donateRebates passing msg.sender as the user. In the executeRedemptions path, msg.sender is the agent, not the end-user whose redemption is being processed. As a result, referral rebates are attributed to the agent rather than the redeemer, diverting rebates to agents and denying them to users.

    Recommendation

    In the executeRedemptions flow, pass the current user address being processed to donateRebates instead of msg.sender.

    As the redeem is called by user, update payRedemptionFee to accept a beneficiary argument and, in executeRedemptions, call donateRebates(user, redemptionFee_) rather than donateRebates(msg.sender, redemptionFee_).

  2. M-01 Medium Redeemers capture post request profit Logical Error Acknowledged
    Location
    src/LeveragedToken.sol:208
    Round
    Main Review

    Description

    When users want to redeem their LT tokens, they have two paths: through direct redemption if the contract holds enough assets, or through a two-step redemption if the contract does not hold enough assets. In the latter case, users submit a redemption request, and agents later bridge funds from Core to the EVM to fulfill it.

    When users request redemption, they effectively give up their LT tokens, and the amount of assets they should receive upon fulfillment is the same as what they would have received if the redemption were executed immediately at the time of the request (assuming no loss occurs).

    However, in the current implementation, within executeRedemptions, each withdrawal request is processed in sequence, and the amount of assets is calculated based on the current exchange rate, which may include profits that accrued after the request was made. This means requesters can receive a portion of profits generated by other users after their redemption request.

    Recommendation

    Consider capping the redemption amount to what the user would have received if the request were fulfilled immediately at the time of request, preventing users from capturing profits that accrued after their redemption was made.

  3. M-02 Medium Inability To Deregister API Wallets From Core Logical Error Acknowledged
    Location
    src/LeveragedToken.sol:269
    Round
    Main Review

    Description

    Leveraged token owner can add agent wallets, by calling CoreWriterLib.addApiWallet with agent address and name. It also stores the agent wallet in EVM state.

    However, removeAgent only removes agent from EVM side, but will remain registered in HyperCore and continue to trade on behalf of the LeveragedToken contract. Be aware that agents are not exactly removed on hyperliquid, only replaced (named and unnamed), with a default expiration time of 180 days.

    In case there is a malicious agent detected, owner can only prevent EVM interactions, but won't be able to stop Core trading.

    Recommendation

    Make sure to call CoreWriterLib.addApiWallet with agent address and the same agent name used during the addAgent flow.

    Be aware that if the api wallet already expired, calling CoreWriterLib.addApiWallet will register again, so a call to HL API {"type":"extraAgents","user":"0x**"} must be used to verify if the agent expired, so it's not re-added.

    Alternatively, add a onlyOwner function that only calls CoreWriterLib.addApiWallet to replace agent wallets in Core.

  4. M-03 Medium Adding Agent May Silently Fail Logical Error Acknowledged
    Location
    src/LeveragedToken.sol:269
    Round
    Main Review

    Description

    The addAgent function will execute CoreWriterLib.addApiWallet to add a new agent address in HyperCore. However, the EVM transaction will always succeed even if agent is never added in core. This may happen if:

    • There are more than 3 named agents. According to HL docs An account can have 1 unnamed approved wallet and up to 3 named ones. Therefore, the 4th named agent will never be added to core.
    • Agent wallet is already an agent for another user
    • Agent wallet is an active HL user.

    Recommendation

    Consider refactoring the agent logic, merging both addAgent and removeAgent in one single owner function, passing the agent address and index. If index is 0, it will replace the unnamed agent, else it will calculate the agent name with given index.

    Storage wise, this implementation will only require the isAgent (to verify onlyAgents access control) and agentCreatedAt (to prevent reusing an agent wallet).

    Additionally, double check the agent being added, and use the API /info endpoint with {"type":"extraAgents", "user":"<ltAddress>"} to confirm and {"type":"userRole", "user":"<agentToAdd>"} to validate the agent availability.

  5. L-01 Low Superfluous Storage Read Superfluous Code Acknowledged
    Location
    src/LeveragedToken.sol:64
    Round
    Main Review

    Description

    The whenNotMintPaused modifier fetches contract storage but the storage variable $ is never used.

    Recommendation

    Remove the storage read.

  6. L-02 Low Missing Execution Redemption Fee Validation Configuration Acknowledged
    Location
    src/LeveragedToken.sol:214
    Round
    Main Review

    Description

    The execution redemption fee is a flat fee added to the two step redemption flow to cover extra gas costs involved to bring back funds to HyperEVM.

    Protocol owner uses setExecuteRedemptionFee to set this fee. However, there is no max value enforced, specially comparing it to the minimum transaction size, so redemptions will not be executed as baseAmount_ < redemptionFee_.

    Recommendation

    Consider enforcing the new setExecuteRedemptionFee value is not above (or even close) to the min transaction size.

  7. L-03 Low Unnecessary Totaly Supply Check Gas Optimization Acknowledged
    Location
    src/LeveragedToken.sol:294
    Round
    Main Review

    Description

    The exchangeRate function calculates the current ratio between assets and LT supply. If totalSupply is 0, then the exchange rate is hardcoded to 1e18, to avoid division by zero.

    However, the current leveraged token deployment will mint a minimum amount and sends LTs to the dead address. Therefore, totalSupply will never be 0 after this initial mint and it will only waste gas for users.

    Recommendation

    Remove the zero total supply check. To mint the initial protocol supply, use the initialize function instead with a simpler mint flow that calls _checkpoint and mints the LTs 1:1 to the dead address.

  8. I-01 Informational Redundant balance check in executeRedemptions Best Practices Acknowledged
    Location
    src/LeveragedToken.sol:215
    Round
    Main Review

    Description

    Inside executeRedemptions, the contract first verifies that the available balance covers the full redemption amount:

    if (baseAssetBalance() < baseAmount_) continue;
    

    However, a few lines later, it performs another check:

    if (baseAsset().balanceOf(address(this)) < afterFees) continue;
    

    This second condition is redundant, since afterFees_ is always less than baseAmount_. If the first check passes, the second will necessarily pass as well, making it unnecessary.

    Recommendation

    Remove the redundant balance check as keeping only the baseAssetBalance() < baseAmount_ condition is sufficient to ensure redemption safety.

  9. I-02 Informational Redundant User Credit Check Superfluous Code Acknowledged
    Location
    src/LeveragedToken.sol:211
    Round
    Main Review

    Description

    During executeRedemptions, the function first fetches user credit and assigns to ltAmount_: uint256 ltAmount_ = userCredit(user_); However, it later compares the user credit to the ltAmount_: if ($.userCredit[user_] < ltAmount_) continue; This extra check is redundant, as the ltAmount_ == $.userCredit[user_]

    Recommendation

    Remove the following check: if ($.userCredit[user_] < ltAmount_) continue;

  10. I-03 Informational Unused error Best Practices Acknowledged
    Location
    src/interfaces/ILeveragedToken.sol:16
    Round
    Main Review

    Description

    Unused NotEnoughBaseAsset error in src/interfaces/ILeveragedToken.sol.

    Recommendation

    Remove the unused error.

  11. I-04 Informational Misleading Function Param Name Informational Acknowledged
    Location
    src/LeveragedToken.sol:344-372
    Round
    Main Review

    Description

    The _baseAmount param is normally used as the base asset amount, like in the mint function or the assigned to the returned value of ltToBaseAmount. The contract will later use this base amount to calculate fees.

    However, the same param name is used in the fee payment functions. Although the amount is in base asset terms, it may lead to errors if its treated as the user amount instead of fees.

    Recommendation

    Consider changing the param name to differentiate with user base asset amounts and fee amounts.

Remediation Review

9 findings · October 15 to 17, 2025
  1. H-01 High Protocol Wide Deadlock DoS Acknowledged
    Location
    src/LeveragedToken.sol:303
    Round
    Remediation Review

    Description

    The _checkpoint function unconditionally computes and pays streaming fees from the on-chain baseAsset via _payFees. If the on-chain baseAsset balance is insufficient (because most AUM sits on Hyperliquid), fee transfers revert.

    Since bridgeIn also calls _checkpoint first, agents cannot replenish the on-chain balance to make fee payment succeed. This creates a protocol-wide deadlock: mint, redeem (both paths), executeRedemptions, bridgeIn/out, and checkpoint all revert until someone donates baseAsset directly to the contract.

    Although team already mentioned that they will keep 10% assets in the LeveragedToken contract, a malicious user can redeem the exact amount of LT tokens and leave the contract empty, DoS'ing next calls to _checkpoint.

    Recommendation

    Consider adding a max redeem function, to prevent users from redeeming 100% of the base asset balance in the contract.

  2. H-02 High Agent Wallets Spot Transfers Not Supported Logical Error Acknowledged
    Location
    src/LeveragedToken.sol
    Round
    Remediation Review

    Description

    Agent wallets will be able to bridge funds in/out of HyperEVM, execute redemptions and manually perform a checkpoint. On the HyperCore side, these agents will swap base assets to USDC, transfer between spot and perps, and open positions for the leveraged token contract.

    However, agent wallets are not allowed to perform usd_class_transfer actions, to transfer USDC between spot and perps, as they are meant to be used only to trigger order actions on behalf of the main user.

    Due to the fact that funds can't never reach the perps balance, the LeveragedToken lacks the functionality to open perpetual positions. Spot transfer are meant to be called by the main account, in this case the LeveragedToken, but there is no permissioned function to trigger this action from the EVM side.

    Recommendation

    Consider adding a onlyAgents protected function that allows these wallets to perform usd_class_transfer actions on behalf of the parent account: CoreWriterLib.transferUsdClass(amount_, toPerp_); Keep in mind that amount_ has EVM units, so there is no need to do any conversion.

  3. M-01 Medium Unnamed agent can't be set Configuration Acknowledged
    Location
    src/LeveragedToken.sol:252
    Round
    Remediation Review

    Description

    setAgent allows the owner to set agents for the LT contract, it allows to set 3 agents from index 0 to 2, each has a derived name of symbol_AGENT_idx. On the other hand, HyperLiquid allows the addition of an unnamed agent which acts as the "default" agent. With the current implementation, it is not possible to set an unnamed agent using setAgent function as it always sets the agent name to symbol_AGENT_idx. This limits the LT contract to only have 3 agents, and the unnamed agent can't be set.

    Recommendation

    Consider refactoring the setAgent function to have a "reserved" index for the unnamed agent, for example, using index 0 for the unnamed agent and allowing named agents to be set from index 1 to 3. This way, the LT contract can accommodate both named and unnamed agents effectively.

  4. I-01 Informational Magic Values For Agent Slots Best Practices Acknowledged
    Location
    src/interfaces/ILeveragedToken.sol:56
    Round
    Remediation Review

    Description

    The _AGENT_SLOTS is declared as a constant with a value of 3. However, this constant is not used in other parts of the code and instead uses the hardcoded integer 3 (i.e. address[3] memory or address[3] agents).

    Recommendation

    Consider using the constant instead of magic values.

  5. I-02 Informational Superfluous Validations Superfluous Code Acknowledged
    Location
    src/LeveragedToken.sol:362
    Round
    Remediation Review

    Description

    The _addCredit function validates that the amount is greater than zero. However, this function is only called by prepareRedeem, which already validates non zero ltAmount_, so _addCredit validation is not necessary. Additionally, _addCredit checks for zero address on user, but this is always msg.sender.

    Similarly, _removeCredit verifies that user credit is greater or equal to the amount to remove, but _removeCredit is only called by executeRedemptions and ltAmount_ is equal to the user credit. Therefore, _removeCredit validation will never fail.

    Recommendation

    Remove the unnecessary validations.

  6. I-03 Informational Leverage Truncated To Integer Informational Acknowledged
    Location
    src/Factory.sol:90
    Round
    Remediation Review

    Description

    Name and symbol format leverage using targetLeverage_ / 1e18 with integer division, dropping fractional parts (e.g., 1.5x shows as 1x). This can mislead users about the product they hold.

    Recommendation

    Be aware to only deploy leveraged tokens without fractional leverage amounts.

  7. I-04 Informational Unused Events Informational Acknowledged
    Location
    src/interfaces/IGlobalStorage.sol:40-42
    Round
    Remediation Review

    Description

    IGlobalStorage declares an event SetPerpOwnerHoldsFundsThreshold but it's never used. Additionally the SetReferreeRebate event is duplicated.

    Recommendation

    Remove duplicate and unused events.

  8. I-05 Informational Referee Typo Informational Acknowledged
    Location
    src/GlobalStorage.sol:46
    Round
    Remediation Review

    Description

    The parameter and state names use referree instead of referee (e.g., referreeRebate). This typo also appears on SetReferreeRebate event and setReferreeRebate function.

    Recommendation

    Fix typos.

  9. I-06 Informational Leveraged Token Setup In HyperCore Informational Acknowledged
    Location
    src/LeveragedToken.sol
    Round
    Remediation Review

    Description

    The following steps should be considered when deploying a leveraged token on hyperliquid:

    • When LT contract is deployed, it's not activated. To do so, a small amount of USDC needs to be transferred to the LT address on Core. Any action done from the EVM side (i.e. adding an API wallet) will revert until the account is activated.
    • Be aware that raw actions sent from EVM are only treated as requests. When executed in HyperCore, these might never succeed. Therefore, contract can't assume that actions like transferring from spot to perps or adding agent wallets, will succeed in HyperCore.
    • Bridging USDT from HyperCore to HyperEMV requires a HYPE fee, so the LeveragedToken contract must hold some HYPE for this action.

    Recommendation

    Be sure to use the the /info endpoint with type: userRole in order to verify that the leveraged token has role:user and it's activated, after sending some USDC in spot. Additionally, make sure to fund the LeveragedToken with HYPE to be able to pay for the bridge fee.

More from Bounce Tech

  1. USDC Updates

    14 findings 14 findings: 4 low, 10 informational
  2. Automation Layer and Contract Updates

    53 findings1 high 53 findings: 1 high, 11 medium, 23 low, 18 informational
  3. Leveraged Token Protocol

    35 findings3 high 35 findings: 3 high, 9 medium, 13 low, 10 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote