<!-- canonical: https://0xsimao.com/findings/symmio-staking-and-vesting-double-spending-attack-vesting -->

# Double spending attack in the Vesting contract

Medium · Sherlock · Staking · 7th March 2025

Finding M-5 of the Symmio, Staking and Vesting competition.

- Protocol: https://audits.sherlock.xyz/contests/838
- Codebase: https://github.com/0xsimao/2025-03-symm-io-stacking/tree/d7cf7fc96af1c25b53a7b500a98b411cd018c0d3
- Source: https://github.com/sherlock-audit/2025-03-symm-io-stacking-judging/issues/650

---

### Summary

The function resetVestingPlans() is called by an administrator account and resets vesting plans for a list of users, with the corresponding amount provided as input. The function calls _resetVestingPlans(), where it checks whether the given amount is greater than or equal to the claimed amount for the user. After that, it calls resetAmount() from LibVestingPlan. In this function, the state is updated, the new amount is recorded, and claimedAmount is set to 0.



### Root Cause

The issue here is that this can lead to double spending. Even though the user executing the request is trusted, they cannot know whether another transaction has been executed before theirs, in which the user whose vesting plan is being reset has withdrawn their locked amount by paying a penalty fee. If this happens, the user will be able to claim the same amount again after the reset, which would harm other users who might not be able to claim their rewards.

https://github.com/sherlock-audit/2025-03-symm-io-stacking/blob/main/token/contracts/vesting/Vesting.sol#L222-L237


### Internal Pre-conditions

None

### External Pre-conditions

None

### Attack Path

1. Trusted user sends a transaction for executes resetVestingPlans()
2. Regular user subject of this reset sends a transaction that is executed before the first one and claim their locked tokens as they pay a penalty
3. After the reset the user  is able to claim the tokens up to amount again

### Impact

Loss of funds for the protocol and for the users

### PoC

_No response_

### Mitigation

_No response_

---

Related findings:

- [SpiritVestingFactory::createSpiritVestingContract overwrites the spiritVestings mapping for the same recipient](https://0xsimao.com/findings/spirit-protocol-vesting-overwrites-vestings-recipient): Spirit Protocol
- [SpiritVestingFactory::setTreasury() could be 2 factor, as well as the AccessControl OZ contract used in the codebase](https://0xsimao.com/findings/spirit-protocol-vesting-well-access-control): Spirit Protocol
