# YearnTokenAdapter’s wrap can become stuck as it uses one step approval for an arbitrary underlying

YearnTokenAdapter.wrap uses one-step safeApprove on arbitrary underlying tokens; USDT-style tokens revert when leftover allowance exists, permanently bricking wrap.

- Payout: Audit contest
- Severity: medium
- Program: Alchemix
- Found by: hyh
- Disclosed: 2022-07-18
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2022-05-alchemix#m-08
- Library page: https://bugbountylibrary.com/r/c4%3A2022-05-alchemix%3AM-08
- Tags: area:web3 domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:dos

**Target:** YearnTokenAdapter.wrap (Solidity adapter contract)
**Root cause:** wrap() calls safeApprove(underlyingToken, token, amount) without first resetting allowance to zero; tokens like USDT revert on non-zero-to-non-zero approval, and partial vault fills leave residual allowance.
**Steps:**
1. Call `wrap(amount, recipient)` with a USDT-like underlying; it does `TokenUtils.safeTransferFrom(...)` then `TokenUtils.safeApprove(underlyingToken, token, amount)`.
2. If the yearn vault accepts only part of the amount, the approval is not fully consumed, leaving positive allowance.
3. Next `wrap()` call executes `safeApprove(underlyingToken, token, amount)` with positive existing allowance, which reverts on USDT-style tokens.
4. wrap becomes permanently unavailable for that token.
**How found:** Code review of wrap()'s single-step approval combined with knowledge of weird-ERC20 approval-race protections (USDT) and the fact that yearn vaults can partially fill deposits, leaving residual allowance.
**Insight:** When a contract approves an arbitrary, caller- or config-supplied token, assume USDT-style zero-first approval semantics; partial fills of vault deposits leave residual allowance that turns the next approve into a permanent revert.

## Proof of concept (copied exactly from the original report)

**wrap() one-step safeApprove of arbitrary underlying**

```
function wrap(uint256 amount, address recipient) external override returns (uint256) {
 TokenUtils.safeTransferFrom(underlyingToken, msg.sender, address(this), amount);
 TokenUtils.safeApprove(underlyingToken, token, amount);
```

