Reports

Smart Contract Security Assessment

Asymmetry - flash zap

The USDaf Flash Zapper Protocol is a capital-efficient leverage system that enables users to open, increase, and decrease leveraged positions in a Liquity-style lending protocol through a single transaction. By utilizing zero-fee crvUSD flash loans, users can leverage their collateral positions without needing to handle complex multi-step operations manually.

5
Issues
2
C/H/M
Period
Aug 04, 2025 - Aug 05, 2025
Auditors
Panda, Fedebianu

Review Summary

Protocol Overview

The USDaf Flash Zapper Protocol is a capital-efficient leverage system that enables users to open, increase, and decrease leveraged positions in a Liquity-style lending protocol through a single transaction. By utilizing zero-fee crvUSD flash loans, users can leverage their collateral positions without needing to handle complex multi-step operations manually.

Protocol
Asymmetry
Timeline
Aug 04, 2025 - Aug 05, 2025
Audit Team
Panda, Fedebianu

Audit Overview

Scope and Resources

Scope

This audit covers one smart contract totaling approximately 280 lines of code across two days of review.

Overall Assessment

Evaluation Matrix

access control
Good

No access control vulnerabilities identified in the audit findings.

mathematics

complexity
Average

Code shows some complexity issues with variable tracking across multi-step swap operations, but overall structure appears manageable.

libraries
Good

Appropriate use of libraries and valid intégration with existing protocols (Yearn, Curve).

decentralization

code stability
Good

Code wasn't updated during the audit.

documentation
Good

Some typos in comments noted, but no major documentation gaps highlighted in the audit.

monitoring

testing
Low

High-severity bugs that should have been caught during testing suggest inadequate test coverage for edge cases and amount calculations.

Key Findings

Findings Summary

0
Critical
2
High
0
Medium
1
Low
2
Informational
0
Gas
H-1 Finding

H-1: WBTC18 swap uses wrong amount variable

High

Summary:

An incorrect variable is used in the final swap operation of the _swap_to function in wbtc18.vy, causing the swap to use the original input amount instead of the intermediate swap result.

Description:

In wbtc18.vy, the _swap_to function uses the variable amount instead of amount_out in the final swap operation:

File: wbtc18.vy
177:     amount_out = curve_tricrypto.swap(
178:         TBTC_INDEX_TRICRYPTO,
179:         CRVUSD_INDEX_TRICRYPTO,
180:         amount,
181:         TRICRYPTO_POOL,
182:         msg.sender,
183:     )

The swap chain should be: WBTC18 → WBTC → tBTC → crvUSD, where each step uses the output from the previous step as input.

Impact:

High.

  • Transaction failure: If the tBTC amount received from the WBTC→tBTC swap is less than the original WBTC18 input amount, the final tBTC→crvUSD swap will attempt to use more tBTC than available, causing the transaction to revert.
  • Funds stuck on contract: If the tBTC amount received is greater than the original WBTC18 input amount, only a portion of the tBTC will be swapped, leaving the remainder trapped on the contract and resulting in a loss for the user.

Recommendation:

Change line 180 in src/exchanges/wbtc18.vy

- 180 amount,
+ 180 amount_out

Developer Response:

Fixed in: 2224877.

H-2 Finding

H-2: ysyBOLD `_swap_to()` does not use `YEARN_BOLD_ZAPPER.zapOut()` returned amount

High

Summary:

ysyBOLD _swap_to() does not properly handle the returned amount from the zap out operation. Since ysyBOLD is a staked version of yBOLD that accumulates compounding rewards over time, the conversion ratio is not 1:1 (see docs). The function incorrectly uses the original input amount instead of the actual amount received from the zap, leading to stuck funds.

Description:

In ysybold.vy contract, _swap_to() calls YEARN_BOLD_ZAPPER.zapOut() but does not use the returned amount. The subsequent swap use the original amount instead of the actual amount received from the zap operation, leading to incorrect swap amount.

Impact:

High. The function will use incorrect amount for subsequent swap and user interest accrued from ysyBOLD will be stuck in the contract.

Recommendation:

Update _swap_to() to properly handle the returned amount from YEARN_BOLD_ZAPPER.zapOut():

def _swap_to(amount: uint256, min_amount: uint256) -> uint256:
    # Pull st-yBOLD
    extcall YEARN_STAKED_BOLD.transferFrom(msg.sender, self, amount, default_return_value=True)

    # st-yBOLD --> BOLD
    amount_out: uint256 = extcall YEARN_BOLD_ZAPPER.zapOut(amount, self, 0)  # maxLoss is 0

    # BOLD --> USDC
    amount_out = curve_stableswap.swap(
        BOLD_INDEX_BOLD_USDC_CURVE_POOL,
        USDC_INDEX_BOLD_USDC_CURVE_POOL,
        amount_out,
        USDC_BOLD_CURVE_POOL,
        self,
    )

    # USDC --> crvUSD
    amount_out = curve_stableswap.swap(
        USDC_INDEX_USDC_CRVUSD_CURVE_POOL,
        CRVUSD_INDEX_USDC_CRVUSD_CURVE_POOL,
        amount_out,
        USDC_CRVUSD_CURVE_POOL,
        msg.sender,
    )

    assert amount_out >= min_amount, "slippage rekt you"
    return amount_out

Developer Response:

Fixxed in: d38611

L-1 Finding

L-1: Missing slippage protection in swap operations can lead to loss of funds

Low

Summary:

The zapper contract performs multiple swap operations during leverage up/down without proper slippage protection. Most swap calls use the default min_out = 0 parameter, making users vulnerable to unlimited slippage and potential MEV attacks.

Description:

swap() has an optional min_out parameter for slippage protection that defaults to 0.

However, 5 out of 6 swap calls in the leverage operations do not provide any slippage protection:

  • Line 205: collateral_amount: uint256 = (self.swap(flash_loan_amount, True, True) + initial_collateral_amount)
  • Line 223: self.swap(usdaf_amount, False, False)
  • Line 283: collateral_amount: uint256 = self.swap(flash_loan_amount, True, True)
  • Line 289: self.swap(usdaf_amount, False, False)
  • Line 360: self.swap(collateral_amount, True, False)

Only one swap call has slippage protection:

  • Line 354: usdaf_amount: uint256 = self.swap(flash_loan_amount, False, True, min_usdaf_amount)

Even if, as stated here, the flash loan provider will revert if the amount returned is not enough, users can still lost their leftovers.

Impact:

High. Users are still vulnerable to sandwich attacks. Any amount of slippage is accepted, potentially causing losses up to their leftovers after repaying the loan.

Recommendation:

Add slippage protection to all swap operations.

Developer Response:

Acknowledged. The frontend should do a correct calculation for the txn to not get sandwiched.

I-2 Finding

I-2: `DECIMALS_DIFF` should be initialized dynamically

Informational

Description:

FlashZapper currently uses a hardcoded DECIMALS_DIFF constant of 10 for all wrapped collateral tokens. Using a hardcoded value assumes all wrapped tokens follow the same decimal pattern, which may not be true for other tokens that will be added in the future.

Impact:

Informational.

Recommendation:

Initialize DECIMALS_DIFF dynamically in the constructor based on the actual decimal differences between the unwrapped and wrapped tokens:

unwrapped_decimals: uint256 = staticcall UNWRAPPED_COLLATERAL_TOKEN.decimals()
wrapped_decimals: uint256 = staticcall COLLATERAL_TOKEN.decimals()
DECIMALS_DIFF: uint256 = wrapped_decimals - unwrapped_decimals

This would make the contract more flexible and robust for supporting different wrapped token configurations.

Developer Response:

Acknowledged.

Methodology

Severity Classification

Critical

Immediate threat to user funds or protocol integrity

Direct loss of funds, protocol compromise

High

Significant security risk requiring urgent attention

Potential fund loss, major functionality disruption

Medium

Important issue that should be addressed

Limited fund risk, functionality concerns

Low

Minor issue with minimal impact

Best practice violations, minor inefficiencies

Gas

Findings that improve gas efficiency

Increased transaction costs

Informational

Code quality and best practice recommendations

Reduced maintainability and readability