# UlyssesToken asset ID accounting error

UlyssesToken.removeAsset assigns a 0-based ID to the swapped-in last asset while addAsset uses 1-based IDs, corrupting asset accounting and allowing duplicate tokens.

- Payout: Contest reward
- Severity: high
- Program: Maia DAO Ecosystem
- Credited: 0xTheC0der
- Disclosed: 2023-09-18
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-05-maia-findings/issues/275
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-maia%3AH-25
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error

**Target:** UlyssesToken ERC4626 multi-token vault (Solidity)
**Root cause:** removeAsset sets assetId[lastAsset] = assetIndex (0-based) while addAsset and the constructor use 1-based IDs, so the swapped-in last asset gets an ID one too low.
**Steps:**
1. Deploy UlyssesToken with assets IDs 1,2,3,4.
2. Call `removeAsset(assets[0])`; last asset is reassigned ID=0 instead of 1.
3. Call `removeAsset(lastAsset)` -> underflow revert at L62.
4. Call `addAsset(lastAsset, weight)` -> does not revert (ID=0 treated as not added), token added twice with different weights.
**How found:** Code reading of addAsset/constructor (1-based IDs) versus removeAsset (0-based reassignment), spotting the off-by-one inconsistency; confirmed with Foundry tests.
**Exploitation:** No direct theft path described; broken accounting lets the same underlying token be registered twice with different weights, corrupting vault share/weight math and bricking removal of the last asset.
**Insight:** When a contract uses 1-based IDs to reserve 0 as 'unset', audit every reassignment site (swap-and-pop patterns) for a missing +1; a 0 ID silently re-enables the 'not added' branch.
