Skip to content

Contracts: the TTL ceiling was copied from the SDK test harness, so balances live 180 days, not a year (regression of #341) #398

Description

@zachyo

Files: contracts/token/src/lib.rs lines 12 to 31, test at 1275; contracts/vesting/src/lib.rs lines 13 to 26, test at 780; unclamped extend_ttl sites at token 216, 262, 265, 294, 511, 514, 612, 615 and vesting 689

Issue: #341 correctly replaced the copy-pasted "52 weeks" expression with one named constant. The constant's ceiling is wrong:

/// ... (`max_entry_ttl` in the network config; 6,312,000 ledgers on mainnet).
const MAX_ENTRY_TTL_LEDGERS: u32 = 6_312_000;

const TTL_LEDGERS: u32 = {
    const YEAR_LEDGERS: u64 = 365 * 24 * 60 * 60 / 5;   // 6,307,200
    if YEAR_LEDGERS < MAX_ENTRY_TTL_LEDGERS as u64 { YEAR_LEDGERS as u32 }
    else { MAX_ENTRY_TTL_LEDGERS }
};

6,312,000 is not the mainnet value. It is the soroban-sdk test-environment default, at soroban-sdk-21.7.7/src/env.rs:514:

max_entry_ttl: 6_312_000,

The live setting, read from the STATE_ARCHIVAL config-setting ledger entry on 2026-07-30, is 3,110,400 on both testnet and mainnet (min_persistent_ttl differs between them — 120,960 on testnet, 2,073,600 on mainnet — but max_entry_ttl does not). So TTL_LEDGERS resolves to 6,307,200, which is 2.03x what either network will honour, and the clamp that was added to guard against exactly this can never fire.

This does not brick anything, and it is important to be precise about why. soroban-env-host treats the two durabilities differently (storage.rs:521-537): for persistent entries it silently lowers new_live_until to the network maximum, and only for temporary entries does it return Storage/InvalidAction. All eight unclamped token sites and the one vesting site write persistent entries, so they are quietly clamped.

The real consequences are:

Note the contract already reads the value at runtime in two places (token lib.rs:558, vesting lib.rs:644), so the correct pattern is present in both files and simply not used at the other nine sites.

Fix: Delete MAX_ENTRY_TTL_LEDGERS and clamp every site with env.storage().max_ttl() at call time, keeping TTL_LEDGERS only as a desired-duration hint. Rewrite the doc comment to say the effective window is whatever the network allows, currently about 180 days, and that holders relying on longer must interact within it. Replace the test with one asserting TTL_LEDGERS >= env.storage().max_ttl() so the clamp is what is exercised. Note the test harness will keep reporting 6,312,000, so any test that hardcodes a network figure will mislead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions