# _safeMint Will Fail Due To An Edge Case In Calculating tokenId Using The _generateNewTokenId Function

XDEFI's _generateNewTokenId reuses totalSupply() after burns, so a new lock can collide with an existing tokenId and _safeMint reverts.

- Payout: Audit contest
- Severity: medium
- Program: XDEFI
- Found by: leastwood
- Disclosed: 2022-02-10
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-01-xdefi-findings/issues/17
- Library page: https://bugbountylibrary.com/r/c4%3A2022-01-xdefi%3AM-01
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error

**Target:** Solidity NFT distribution contract (XDEFIDistribution)
**Root cause:** tokenId is composed of points in the upper 128 bits and totalSupply()+1 in the lower 128 bits; burning NFTs via merge() lowers totalSupply(), so a later lock with matching points and supply index regenerates an already-minted tokenId.
**Steps:**
1. Lock position A with amount_/duration_ producing points P at supply N, minting tokenId = (P<<128)+N+1.
2. Wait for expiry, then merge() unlocked NFTs to burn them, reducing totalSupply().
3. Lock again with the same amount_/duration_ so points_ = P and totalSupply()+1 equals the earlier index.
4. _generateNewTokenId returns the already-used tokenId; _safeMint reverts on existing token.
**How found:** Code reading of _generateNewTokenId and merge(): noticed totalSupply() is used as the uniqueness component while merge() burns NFTs, making the supply non-monotonic.
**Insight:** When a tokenId or ID is derived from totalSupply(), check every path that burns or transfers supply — burns make the counter non-monotonic and enable ID collisions; prefer a dedicated monotonically increasing mint counter.
