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
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-
L-01 Low Incorrect Decimals Logical Error Resolved
Description
In
LeveragedTokenHelper, the logic used to determine whether a leveraged token is considered held multipliesbalanceOf(18 decimals) byexchangeRate(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.
-
L-02 Low bridgeFromPerp May Bridge Best Practices Resolved
Description
To bridge USDC from perps to EVM,
bridgeFromPerphas to be called, which:- Transfers USDC from perps to spot
- 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.
-
L-03 Low Total Assets Reduced When Bridging Logical Error Resolved
Description
According to the Hyperliquid docs:
A transfer from HyperCore to HyperEVM costs 200k gas at thebase 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
bridgeFromPerpandbridgeFromSpotfunctions 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
HyperCorewith HYPE, so that bridge fees are paid with HYPE instead of USDC.Resolution
Bounce Team: Resolved.
-
L-04 Low Exchange Rate Drop When Bridging Logical Error Resolved
Description
When bridging from Core to EVM, the
bridgeFromPerptransaction 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
totalAssetsin the same block as thebridgeFromPerptransaction, thehyperliquidUsdccall 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
bridgeFromPerptransaction 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
totalAssetsto normalize before proceeding with the automation flow.Alternatively, listen to the
BridgeFromPerpevent and create ano actionsdelay to prevent executing redeems before state settles.Resolution
Bounce Team: Resolved.
-
I-01 Informational Agent Activation Not Verified In setAgent Validation Resolved
Description
setAgentdoes not validate whether the providedagent_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_) == falsewould 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 thatagent_is eligible for API wallet addition on Hyperliquid Core.Resolution
Bounce Team: Resolved.
-
I-02 Informational Unsafe Integer Casting Best Practices Resolved
Description
Unsafe casting is used in
LeveragedToken.sol bridgeFromPerp()(uint64(amount_)) and inHyperliquidHandler.sol notionalUsdc()(uint16(marketId_)). These casts can silently truncate on overflow.Recommendation
Consider using
OpenZeppelin'sSafeCastlibrary to ensure safe conversions.Resolution
Bounce Team: The issue was resolved in PR#172.
-
I-03 Informational Complex Position Notional Value Calculations Informational Resolved
Description
The current
notionalUsdcfunction 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
AccountMarginSummarystruct has thentlPoswhich yields to the same value.Recommendation
Consider using the
AccountMarginSummary.ntlPosto avoid rounding errors when scaling the results.Resolution
Bounce Team: The issue was resolved in PR#170.
-
I-04 Informational Decimal Mismatch Math Resolved
Description
The function
getLeveragedTokenPositionDatacalculatesnetValue_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 decimalslt_.credit(): Returns LT token amount with 18 decimalslt_.exchangeRate(): Returns exchange rate with 18 decimalslt_.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.
-
I-05 Informational Multiple Documented Functions That Are Removed Documentation Resolved
Description
There are multiple functions that were removed from the code but are still referenced in the
README:
marginUsedAssetsperpUsdcAssetsspotBaseAssetsspotUsdcAssets
Recommendation
Update the README to reflect the new function names.
Resolution
Bounce Team: The issue was resolved in PR#168.
-
I-06 Informational Unused Imports Superfluous Code Resolved
Description
The
LeveragedTokenimportsHLConversionsandPrecompileLibbut these are never used.Recommendation
Remove unused imports.
Resolution
Bounce Team: The issue was resolved in PR#167.
Remediation Review
4 findings-
I-01 Informational Missing Event In State Changing Function Events Resolved
Description
The contract mutates state in
claimLiquidationPoints()by marking claims and appending to_userswithout emitting an event. This makes it harder to monitor, audit, or reconstruct claim history reliably, especially ifusers()becomes unusable due to size.Recommendation
Emit an event (e.g.,
event Claimed(address indexed user)) insideclaimLiquidationPointsand consider relying on events instead of keeping an ever-growing on-chain array.Resolution
Bounce Team: The issue was resolved in PR#177.
-
I-02 Informational Redemption Fee Invariant Can Be Broken Logical Error Acknowledged
Description
The
executeRedemptionFeeis bounded byminTransactionSize * 0.5at the time of setting, but loweringminTransactionSizelater can make the existingexecuteRedemptionFeeexceed 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.
-
I-03 Informational Inconsistent Base-Asset Balance Retrieval Best Practices Resolved
Description
getLeveragedTokenPositionDatareturns thebaseAssetContractBalanceasbaseAsset_.balanceOf(leveragedTokenAddress_). However, the LT contract exposes a helper functionbaseAssetBalance()that returns the actual base asset balance held by the LT contract.Recommendation
Consider using
lt_.baseAssetBalance()instead ofbaseAsset_.balanceOf(leveragedTokenAddress_)to ensure consistency with other functions in the contract that already rely onbaseAssetBalance()for retrieving the LT’s actual base asset balance.Resolution
Bounce Team: The issue was resolved in PR#175.
-
I-04 Informational Incorrect Note About Account Activation Documentation Resolved
Description
The
READMEcurrently mentions thatthe Smart Contract must be 'activated', which requires sendinga 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
READMEcomment if a lower amount is already used.Resolution
Bounce Team: The issue was resolved in PR#174.
No findings match.
More from Bounce Tech
-
Automation Layer and Contract Updates
53 findings1 high 53 findings: 1 high, 11 medium, 23 low, 18 informational -
Contract Updates
20 findings3 high 20 findings: 3 high, 4 medium, 3 low, 10 informational -
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.