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

Security review · January 2026

USDC Updates

for Bounce Tech

Bounce engaged Guardian to review the security of their USDC Updates. From the 7th of January to the 9th of January, a team of 2 auditors reviewed the source code in scope.

Published
Review window
January 7 to 9, 2026
Rounds
Main Review, Remediation Review
Language
Solidity
Chains
Hyperliquid, Offchain
Sector
Derivatives
  • 0 Critical
  • 0 High
  • 0 Medium
  • 4 Low
  • 10 Informational

13 resolved · 1 acknowledged

Scope

Overview

Bounce engaged Guardian to review the security of their USDC Updates. From the 7th of January to the 9th of January, a team of 2 auditors reviewed the source code in scope.

Findings 14

Main Review

10 findings
  1. L-01 Low Incorrect Decimals Logical Error Resolved
    Location
    LeveragedTokenHelper.sol: 96-97
    Round
    Main Review

    Description

    In LeveragedTokenHelper, the logic used to determine whether a leveraged token is considered held multiplies balanceOf (18 decimals) by exchangeRate (18 decimals) and directly compares the result against _MIN_ASSET_VALUE_THRESHOLD = 1e6.

    This produces a value with 36 decimals, while the threshold is expressed in 6-decimal USDC units, making the comparison invalid.

    As a result, the onlyHeld_ filter can behave incorrectly (false positives or negatives) and the raw multiplication risks overflow and revert in view calls for large balances.

    Recommendation

    Normalize units before comparison by scaling the computed value to USDC decimals, following the same pattern used in LeveragedToken.ltToBaseAmount.

    Resolution

    Bounce Team: The issue was resolved in PR#171.

  2. L-02 Low bridgeFromPerp May Bridge Best Practices Resolved
    Location
    LeveragedToken.sol: 286-287
    Round
    Main Review

    Description

    To bridge USDC from perps to EVM, bridgeFromPerp has to be called, which:

    1. Transfers USDC from perps to spot
    2. Bridges USDC from spot to EVM

    However, if there is not enough USDC in perps, the transfer step fails, but the bridging happens anyway.

    This could result in an accidental spot-to-EVM bridge when the intent was to bridge from perps, which could happen more often if the protocol has low TVL.

    Recommendation

    Consider acknowledging and documenting this behavior, so agents are aware of it.

    Resolution

    Bounce Team: Resolved.

  3. L-03 Low Total Assets Reduced When Bridging Logical Error Resolved
    Location
    LeveragedToken.sol: 349
    Round
    Main Review

    Description

    According to the Hyperliquid docs: A transfer from HyperCore to HyperEVM costs 200k gas at the base gas price of the next HyperEVM block. If the account is only funded with USDC, these bridge fees will be charged in USDC tokens (around 0.0005 USDC per bridge according to internal testing).

    Consequently, two issues arise:

    • if automation script requires that 100% of the Core funds should be bridged to EVM, the bridge

    transaction will revert due to insufficient funds to pay for fees.

    • the bridgeFromPerp and bridgeFromSpot functions do not execute _checkpoint(), so the next

    action will calculate fees based on a slightly lower value of totalAssets.

    Recommendation

    Fund the leveraged token address in HyperCore with HYPE, so that bridge fees are paid with HYPE instead of USDC.

    Resolution

    Bounce Team: Resolved.

  4. L-04 Low Exchange Rate Drop When Bridging Logical Error Resolved
    Location
    LeveragedToken.sol: 284
    Round
    Main Review

    Description

    When bridging from Core to EVM, the bridgeFromPerp transaction will first reduce the Core balance, but the USDC EVM tokens will only be minted in the next EVM block.

    Therefore, when the RPC fetches totalAssets in the same block as the bridgeFromPerp transaction, the hyperliquidUsdc call will not detect the Core assets, and the USDC tokens are not yet minted to the LT contract, causing an unexpected exchange rate drop.

    Keep in mind that USDC is transferred/minted to the leveraged token contract one block after the bridgeFromPerp transaction is confirmed, but these transfers take place in the first position of the block, preventing invalid state if frontrunned.

    Recommendation

    Consider adding a short delay after bridging funds to EVM and allow totalAssets to normalize before proceeding with the automation flow.

    Alternatively, listen to the BridgeFromPerp event and create a no actions delay to prevent executing redeems before state settles.

    Resolution

    Bounce Team: Resolved.

  5. I-01 Informational Agent Activation Not Verified In setAgent Validation Resolved
    Location
    LeveragedToken.sol
    Round
    Main Review

    Description

    setAgent does not validate whether the provided agent_ is eligible to be added as an API wallet on Hyperliquid Core (e.g., whether the address is already activated on Core).

    This is intentional behavior: enforcing coreUserExists(agent_) == false would prevent setting/removing an agent if the address gets activated in Core outside of this contract.

    However, the lack of validation means the owner may set an address that cannot be added as an API wallet, causing addApiWallet(agent_) to revert/fail at the Core level and requiring manual operational checks.

    Recommendation

    Document this operational requirement: before calling setAgent, the admin should manually verify that agent_ is eligible for API wallet addition on Hyperliquid Core.

    Resolution

    Bounce Team: Resolved.

  6. I-02 Informational Unsafe Integer Casting Best Practices Resolved
    Location
    HyperliquidHandler.sol: 43
    Round
    Main Review

    Description

    Unsafe casting is used in LeveragedToken.sol bridgeFromPerp() (uint64(amount_)) and in HyperliquidHandler.sol notionalUsdc() (uint16(marketId_)). These casts can silently truncate on overflow.

    Recommendation

    Consider using OpenZeppelin's SafeCast library to ensure safe conversions.

    Resolution

    Bounce Team: The issue was resolved in PR#172.

  7. I-03 Informational Complex Position Notional Value Calculations Informational Resolved
    Location
    HyperliquidHandler.sol: 39
    Round
    Main Review

    Description

    The current notionalUsdc function calculates the LT's position notional value based on the position's size, mark price and performs some scaling based on token, size and price decimals.

    However, if every leveraged token only manages one hyperliquid position, the AccountMarginSummary struct has the ntlPos which yields to the same value.

    Recommendation

    Consider using the AccountMarginSummary.ntlPos to avoid rounding errors when scaling the results.

    Resolution

    Bounce Team: The issue was resolved in PR#170.

  8. I-04 Informational Decimal Mismatch Math Resolved
    Location
    LeveragedTokenHelper.sol: 176
    Round
    Main Review

    Description

    The function getLeveragedTokenPositionData calculates netValue_ by subtracting credit value from total assets. However, there is a decimal mismatch between the two operands:

    uint256 netValue_ = lt_.totalAssets() - lt_.credit().mul(lt_.exchangeRate());
    
    • lt_.totalAssets(): Returns USDC amount with 6 decimals
    • lt_.credit(): Returns LT token amount with 18 decimals
    • lt_.exchangeRate(): Returns exchange rate with 18 decimals
    • lt_.credit().mul(lt_.exchangeRate()): Results in 18 decimals

    Subtracting an 18-decimal value from a 6-decimal value causes an underflow. This will revert and break off-chain monitoring and automation.

    Recommendation

    Consider scaling the credit value down to 6 decimals before subtraction:

    uint256 creditValue_ = lt_.credit().mul(lt_.exchangeRate()).scaleTo(baseAssetDecimals_);
    uint256 netValue_ = lt_.totalAssets() - creditValue_;
    

    This ensures both operands use the same decimal precision (6 decimals) before arithmetic operations.

    Resolution

    Bounce Team: The issue was resolved in PR#169.

  9. I-05 Informational Multiple Documented Functions That Are Removed Documentation Resolved
    Location
    README.md
    Round
    Main Review

    Description

    There are multiple functions that were removed from the code but are still referenced in the

    README:

    1. marginUsedAssets
    2. perpUsdcAssets
    3. spotBaseAssets
    4. spotUsdcAssets

    Recommendation

    Update the README to reflect the new function names.

    Resolution

    Bounce Team: The issue was resolved in PR#168.

  10. I-06 Informational Unused Imports Superfluous Code Resolved
    Location
    LeveragedToken.sol: 10-11
    Round
    Main Review

    Description

    The LeveragedToken imports HLConversions and PrecompileLib but these are never used.

    Recommendation

    Remove unused imports.

    Resolution

    Bounce Team: The issue was resolved in PR#167.

Remediation Review

4 findings
  1. I-01 Informational Missing Event In State Changing Function Events Resolved
    Location
    LiquidationPoints.sol: 26
    Round
    Remediation Review

    Description

    The contract mutates state in claimLiquidationPoints() by marking claims and appending to _users without emitting an event. This makes it harder to monitor, audit, or reconstruct claim history reliably, especially if users() becomes unusable due to size.

    Recommendation

    Emit an event (e.g., event Claimed(address indexed user)) inside claimLiquidationPoints and consider relying on events instead of keeping an ever-growing on-chain array.

    Resolution

    Bounce Team: The issue was resolved in PR#177.

  2. I-02 Informational Redemption Fee Invariant Can Be Broken Logical Error Acknowledged
    Location
    GlobalStorage.sol: 160
    Round
    Remediation Review

    Description

    The executeRedemptionFee is bounded by minTransactionSize * 0.5 at the time of setting, but lowering minTransactionSize later can make the existing executeRedemptionFee exceed this intended bound. No continuous invariant enforcement exists.

    Recommendation

    Either enforce the invariant on both setters (revalidate and adjust or reject updates that violate the constraint) or compute the max allowed dynamically at use-time instead of set-time.

    Resolution

    Bounce Team: Acknowledged.

  3. I-03 Informational Inconsistent Base-Asset Balance Retrieval Best Practices Resolved
    Location
    LeveragedTokenHelper.sol: 249
    Round
    Remediation Review

    Description

    getLeveragedTokenPositionData returns the baseAssetContractBalance as baseAsset_.balanceOf(leveragedTokenAddress_). However, the LT contract exposes a helper function baseAssetBalance() that returns the actual base asset balance held by the LT contract.

    Recommendation

    Consider using lt_.baseAssetBalance() instead of baseAsset_.balanceOf(leveragedTokenAddress_) to ensure consistency with other functions in the contract that already rely on baseAssetBalance() for retrieving the LT’s actual base asset balance.

    Resolution

    Bounce Team: The issue was resolved in PR#175.

  4. I-04 Informational Incorrect Note About Account Activation Documentation Resolved
    Location
    README.md
    Round
    Remediation Review

    Description

    The README currently mentions that the Smart Contract must be 'activated', which requires sending a small amount of USDC to that contract on HyperCore. \$5 should be plenty.

    However, tha activation fee is 1 stablecoin unit, no matter how much is sent. An account can be activated in Hypercore by transferring 0.01 USDC, but will pay a 1 USDC fee to the network.

    Recommendation

    Lower the amount sent to activate the contract or fix the README comment if a lower amount is already used.

    Resolution

    Bounce Team: The issue was resolved in PR#174.

More from Bounce Tech

  1. Automation Layer and Contract Updates

    53 findings1 high 53 findings: 1 high, 11 medium, 23 low, 18 informational
  2. Contract Updates

    20 findings3 high 20 findings: 3 high, 4 medium, 3 low, 10 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