# ASDRouter didn’t call ASDUSDC.approve() to to grant permission for crocSwapAddress to spend their ASDUSDC

ASDRouter.lzCompose deposits USDC into ASDUSDC then swaps to NOTE but never approves crocSwapAddress, so the swap always reverts and the router is unusable.

- Payout: Audit contest
- Severity: medium
- Program: Canto
- Found by: 0xpiken
- Disclosed: 2024-05-03
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2024-03-canto#m-02
- Library page: https://bugbountylibrary.com/r/c4%3A2024-03-canto%3AM-02
- Tags: area:web3 domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:misconfiguration

**Target:** ASDRouter.lzCompose cross-chain receive/swap flow
**Root cause:** ASDRouter calls crocSwapAddress.swap() to convert ASDUSDC to NOTE without first calling ASDUSDC.approve(crocSwapAddress, amount), leaving allowance at zero.
**Steps:**
1. Receive wrapped USDC via lzCompose and call ASDUSDC.deposit() to mint ASDUSDC to the router.
2. Call _swapOFTForNote() which invokes crocSwapAddress.swap(...) with ASDUSDC as base token.
3. Observe ASDUSDC.allowance(address(this), crocSwapAddress) == 0 while balance is nonzero; swap reverts.
4. Confirm on fork (block 8977374, chainId 7700) that dex.swap reverts until usdc.approve(dex, amount) is set.
**How found:** Code review of the lzCompose deposit/swap/deposit sequence; noticed the swap step lacked an approve call, then instrumented _swapOFTForNote with console logs showing balance>0 and allowance==0, and validated on a Canto fork that crocSwapDex.swap reverts without allowance.
**Insight:** When auditing router/compose flows that custody tokens then call an external DEX, verify each token transfer path has a matching approve to the exact spender; instrumenting allowance vs balance in tests quickly exposes missing approvals.
