Skip to content

[CO-07] Fix off-by-one expiry in loyalty_token approve() causing immediate silent expiration #285

Description

@Leothosine

Problem

contracts/loyalty_token/src/lib.rs has inconsistent boundary conditions between approve() and get_allowance():

  • approve() rejects only if expiration_ledger < env.ledger().sequence() (strict less-than)
  • get_allowance() returns 0 when env.ledger().sequence() > d.expiration_ledger (strict greater-than)

A caller who sets expiration_ledger = current_sequence + 1 gets approval valid for only ~5 seconds (one ledger). There is no minimum validity window enforced, no maximum cap, and the semantics of expiration_ledger are undocumented - making it easy for integrators to create approvals that expire before they can be used.

Proposed Solution

  1. Add MIN_APPROVAL_VALIDITY_LEDGERS: u32 = 17280 constant (~24 hours at 5s/ledger)
  2. Change approve() to require expiration_ledger >= env.ledger().sequence() + MIN_APPROVAL_VALIDITY_LEDGERS
  3. Add a MAX_APPROVAL_VALIDITY_LEDGERS cap (~1 year) to prevent indefinite approvals
  4. Document in code comments: expiration_ledger is the LAST ledger at which the approval is valid (inclusive)

Acceptance Criteria

  • Approving with expiration_ledger < current + MIN_APPROVAL_VALIDITY_LEDGERS panics with "expiration too soon"
  • A valid approval is usable up to and including expiration_ledger
  • A valid approval returns 0 on expiration_ledger + 1
  • Unit test matrix covers: too-short expiry (panics), minimum valid expiry, at-expiry-ledger (valid), past-expiry (returns 0)
  • Code comment documents the inclusive expiry semantics

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions