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
Scope
Findings 20
Main Review
11 findings · October 6 to 13, 2025-
H-01 High Referral Rebates Credited To Agent Wallet Logical Error Acknowledged
Description
When agents execute prepared redemptions, the contract calls
donateRebatespassingmsg.senderas the user. In theexecuteRedemptionspath,msg.senderis 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
executeRedemptionsflow, pass the current user address being processed todonateRebatesinstead ofmsg.sender.As the
redeemis called by user, updatepayRedemptionFeeto accept a beneficiary argument and, inexecuteRedemptions, calldonateRebates(user, redemptionFee_)rather thandonateRebates(msg.sender, redemptionFee_). -
M-01 Medium Redeemers capture post request profit Logical Error Acknowledged
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.
-
M-02 Medium Inability To Deregister API Wallets From Core Logical Error Acknowledged
Description
Leveraged token owner can add agent wallets, by calling
CoreWriterLib.addApiWalletwith agent address and name. It also stores the agent wallet in EVM state.However,
removeAgentonly 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.addApiWalletwith agent address and the same agent name used during theaddAgentflow.Be aware that if the api wallet already expired, calling
CoreWriterLib.addApiWalletwill 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
onlyOwnerfunction that only callsCoreWriterLib.addApiWalletto replace agent wallets in Core. -
M-03 Medium Adding Agent May Silently Fail Logical Error Acknowledged
Description
The
addAgentfunction will executeCoreWriterLib.addApiWalletto 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
addAgentandremoveAgentin 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 verifyonlyAgentsaccess control) andagentCreatedAt(to prevent reusing an agent wallet).Additionally, double check the agent being added, and use the API
/infoendpoint with{"type":"extraAgents", "user":"<ltAddress>"}to confirm and{"type":"userRole", "user":"<agentToAdd>"}to validate the agent availability. - There are more than 3 named agents. According to HL docs
-
L-01 Low Superfluous Storage Read Superfluous Code Acknowledged
Description
The
whenNotMintPausedmodifier fetches contract storage but the storage variable$is never used.Recommendation
Remove the storage read.
-
L-02 Low Missing Execution Redemption Fee Validation Configuration Acknowledged
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
setExecuteRedemptionFeeto set this fee. However, there is no max value enforced, specially comparing it to the minimum transaction size, so redemptions will not be executed asbaseAmount_ < redemptionFee_.Recommendation
Consider enforcing the new
setExecuteRedemptionFeevalue is not above (or even close) to the min transaction size. -
L-03 Low Unnecessary Totaly Supply Check Gas Optimization Acknowledged
Description
The
exchangeRatefunction calculates the current ratio between assets and LT supply. IftotalSupplyis 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,
totalSupplywill 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
_checkpointand mints the LTs 1:1 to the dead address. -
I-01 Informational Redundant balance check in
executeRedemptionsBest Practices AcknowledgedDescription
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 thanbaseAmount_. 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. -
I-02 Informational Redundant User Credit Check Superfluous Code Acknowledged
Description
During
executeRedemptions, the function first fetches user credit and assigns toltAmount_:uint256 ltAmount_ = userCredit(user_);However, it later compares the user credit to theltAmount_:if ($.userCredit[user_] < ltAmount_) continue;This extra check is redundant, as theltAmount_ == $.userCredit[user_]Recommendation
Remove the following check:
if ($.userCredit[user_] < ltAmount_) continue; -
I-03 Informational Unused error Best Practices Acknowledged
Description
Unused
NotEnoughBaseAsseterror insrc/interfaces/ILeveragedToken.sol.Recommendation
Remove the unused error.
-
I-04 Informational Misleading Function Param Name Informational Acknowledged
Description
The
_baseAmountparam is normally used as the base asset amount, like in themintfunction or the assigned to the returned value ofltToBaseAmount. 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-
H-01 High Protocol Wide Deadlock DoS Acknowledged
Description
The
_checkpointfunction unconditionally computes and pays streaming fees from the on-chain baseAsset via_payFees. If the on-chainbaseAssetbalance is insufficient (because most AUM sits on Hyperliquid), fee transfers revert.Since
bridgeInalso calls_checkpointfirst, 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
LeveragedTokencontract, 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.
-
H-02 High Agent Wallets Spot Transfers Not Supported Logical Error Acknowledged
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_transferactions, 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
LeveragedTokenlacks the functionality to open perpetual positions. Spot transfer are meant to be called by the main account, in this case theLeveragedToken, but there is no permissioned function to trigger this action from the EVM side.Recommendation
Consider adding a
onlyAgentsprotected function that allows these wallets to performusd_class_transferactions on behalf of the parent account:CoreWriterLib.transferUsdClass(amount_, toPerp_);Keep in mind thatamount_has EVM units, so there is no need to do any conversion. -
M-01 Medium Unnamed agent can't be set Configuration Acknowledged
Description
setAgentallows 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 ofsymbol_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 usingsetAgentfunction as it always sets the agent name tosymbol_AGENT_idx. This limits the LT contract to only have 3 agents, and the unnamed agent can't be set.Recommendation
Consider refactoring the
setAgentfunction to have a "reserved" index for the unnamed agent, for example, using index0for the unnamed agent and allowing named agents to be set from index1to3. This way, the LT contract can accommodate both named and unnamed agents effectively. -
I-01 Informational Magic Values For Agent Slots Best Practices Acknowledged
Description
The
_AGENT_SLOTSis 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] memoryoraddress[3] agents).Recommendation
Consider using the constant instead of magic values.
-
I-02 Informational Superfluous Validations Superfluous Code Acknowledged
Description
The
_addCreditfunction validates that the amount is greater than zero. However, this function is only called byprepareRedeem, which already validates non zeroltAmount_, so_addCreditvalidation is not necessary. Additionally,_addCreditchecks for zero address on user, but this is alwaysmsg.sender.Similarly,
_removeCreditverifies that user credit is greater or equal to the amount to remove, but_removeCreditis only called byexecuteRedemptionsandltAmount_is equal to the user credit. Therefore,_removeCreditvalidation will never fail.Recommendation
Remove the unnecessary validations.
-
I-03 Informational Leverage Truncated To Integer Informational Acknowledged
Description
Name and symbol format leverage using
targetLeverage_ / 1e18with 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.
-
I-04 Informational Unused Events Informational Acknowledged
Description
IGlobalStoragedeclares an eventSetPerpOwnerHoldsFundsThresholdbut it's never used. Additionally theSetReferreeRebateevent is duplicated.Recommendation
Remove duplicate and unused events.
-
I-05 Informational Referee Typo Informational Acknowledged
Description
The parameter and state names use
referreeinstead ofreferee(e.g.,referreeRebate). This typo also appears onSetReferreeRebateevent andsetReferreeRebatefunction.Recommendation
Fix typos.
-
I-06 Informational Leveraged Token Setup In HyperCore Informational Acknowledged
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
LeveragedTokencontract must hold some HYPE for this action.
Recommendation
Be sure to use the the
/infoendpoint withtype: userRolein order to verify that the leveraged token hasrole:userand it'sactivated, after sending some USDC in spot. Additionally, make sure to fund theLeveragedTokenwith HYPE to be able to pay for the bridge fee.
No findings match.
More from Bounce Tech
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.