# Fixed Term Teller tokens can be created with an expiry in the past

BondFixedTermTeller's create() checks raw expiry against block.timestamp, but tokenId rounds expiry down to the day, letting users mint already-expired bonds redeemable instantly.

- Payout: Contest reward
- Severity: high
- Program: Bond
- Credited: obront
- Disclosed: 2022-11-05
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2022-11-bond-judging/issues/34
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-11-bond%3AH-1
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** BondFixedTermTeller.create() / getTokenId()
**Root cause:** create() validates expiry_ < block.timestamp on the raw timestamp, but getTokenId() floors the expiry to the nearest day, so a future timestamp within the current day maps to a tokenId whose effective expiry is earlier that day (already past).
**Steps:**
1. Note current time e.g. 1668546000 (3pm); latest expired tokenId expiry is 1668524400 (9am same day).
2. Call `create()` with `expiry_ = block.timestamp + 1` (e.g. 1668546001), passing the `expiry_ < block.timestamp` check.
3. getTokenId() floors expiry to the day boundary, yielding the tokenId whose expiry is 9am that morning.
4. Redeem the freshly minted bond tokens immediately since their effective expiry is in the past.
**How found:** Code reading: noticed create() checks raw expiry_ against block.timestamp while getTokenId() rounds timestamps down to the day, creating a mismatch between the validated value and the effective expiry.
**Exploitation:** Attacker mints bond tokens with a same-day future timestamp that rounds down to an already-expired tokenId, then redeems instantly. In a protocol that stakes assets to mint discounted bonds assuming future expiry, the attacker cycles stake→mint→redeem→dump for arbitrage and drains protocol token value.
**Escalation:** Repeated stake/mint/redeem cycles compound arbitrage returns and can tank the integrating protocol's token.
**Insight:** When a validated timestamp is later floored/rounded to derive an ID or key, check the rounded value against the constraint, not the raw input—rounding can move a 'future' value into the past.
