# Alkimiya SilicaPools: unsafe cast in collateralizedMint drains WBTC

Unsafe uint128 cast in Alkimiya SilicaPools collateralizedMint allowed minting huge shares, then redeemShort drained 1.14 WBTC via flash loan.

- Payout: Real-world hack
- Severity: high
- Program: Alkimiya_IO
- Credited: DeFiHackLabs
- Disclosed: 2025-03-28
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-03/Alkimiya_io_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250328-alkimiya-io
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:flash-loan vuln:precision-loss

**Target:** SilicaPools collateralizedMint / redeemShort
**Root cause:** collateralizedMint casts shares to uint128 without checking, so passing type(uint128).max+2 wraps to 1 share but mints a huge ERC1155 balance; redeemShort then pays out based on the inflated balance.
**Steps:**
1. Flash loan 1e9 WBTC from Morpho.
2. Approve SilicaPools and Morpho for WBTC.
3. Transfer 56125794 WBTC to SilicaPools.
4. Call `collateralizedMint(poolParams, bytes32(0), type(uint128).max+2, attacker, attacker)` with a malicious `index` contract returning decimals=31, shares=1, balance=0.
5. Receive ERC1155 shares; `safeTransferFrom` to address(1) with value type(uint128).max to set id.
6. Call `startPool`, `endPool`, `redeemShort` to redeem inflated short position for WBTC.
**How found:** Likely code review of the cast `uint128(shares)` in collateralizedMint and the redeemShort payout logic, noticing that a value > uint128 max wraps but the ERC1155 mint uses the original uint256.
**Exploitation:** Attacker flash-loaned WBTC, minted a huge short position via the unsafe cast, then redeemed it through redeemShort to extract 1.14015390 WBTC (~95.5K USD).
**Insight:** Look for unchecked downcasts (uint128/uint96) of user-supplied amounts that are later used in minting or accounting; test boundary values like type(uint128).max+2 to see if wrap-around creates a mismatch between stored balance and paid-out value.
