Skip to content

Implement issues #573, #574, #563, #564 - tests and storage management - #648

Open
benjaminjohnsonfin-afk wants to merge 4 commits into
Stellar-split:mainfrom
benjaminjohnsonfin-afk:implement-issues-563-564-573-574
Open

Implement issues #573, #574, #563, #564 - tests and storage management#648
benjaminjohnsonfin-afk wants to merge 4 commits into
Stellar-split:mainfrom
benjaminjohnsonfin-afk:implement-issues-563-564-573-574

Conversation

@benjaminjohnsonfin-afk

Copy link
Copy Markdown
Contributor

Summary

This PR implements four related issues spanning distribution algorithm tests and persistent storage management:

Changes

Issue #573: Single Recipient Test

  • Add single_recipient_gets_full_amount() test to verify 100% distribution for single recipient
  • Uses direct ratio matching to validate distribute_with_remainder() returns full amount without truncation

Issue #574: Sum Invariant Test

  • Add sum_invariant_holds_with_unequal_ratios() test with multiple non-evenly-divisible cases
  • Tests largest-remainder algorithm's core guarantee: sum(result) == total always
  • Cases: (10, [1,1,1], 3), (100, [2,3,1,4], 10), (999, [1,3], 4)

Issue #564: Checkpoint-Based Recovery

  • Add InvoiceStatus::PayoutInProgress intermediate state
  • Add DataKey::PayoutCheckpoint(u64) to track last successful transfer index
  • Add ContractError::CheckpointMismatch and ContractError::AlreadyPaid error types
  • Add test_checkpoint_recovery_after_failed_payout() test demonstrating recovery mechanism
  • Prevents double-payment when payout fails mid-loop

Issue #563: TTL Management

  • Create constants.rs with MIN_INVOICE_TTL_LEDGERS (60 days) and MAX_INVOICE_TTL_LEDGERS (1 year)
  • Create storage.rs with centralized save helpers: save_invoice(), save_recipients(), save_contributor()
  • Each helper calls env.storage().persistent().bump() after set() to prevent expiration
  • Add bump_invoice_ttl(invoice_id) entry point callable by any address
  • Add test_ttl_bump_on_storage_writes() test validating TTL extension

Test Coverage

Each implementation includes dedicated tests verifying the feature behavior:

  • test_checkpoint_recovery_after_failed_payout() validates checkpoint tracking
  • test_ttl_bump_on_storage_writes() validates automatic TTL bumping
  • single_recipient_gets_full_amount() validates edge case distribution
  • sum_invariant_holds_with_unequal_ratios() validates sum invariant

All tests are implementation tests that verify the contract mechanisms without requiring execution of dependent systems (per issue requirements).

Commits

This PR contains 4 commits, one per issue:

  1. test: add single_recipient_gets_full_amount test for distribute_with_remainder (Closes Test distribute_with_remainder returns full amount for a single recipient #573)
  2. test: add sum_invariant_holds_with_unequal_ratios test for distribute_with_remainder (Closes Test distribute_with_remainder sum invariant with unequal ratios #574)
  3. feat: implement checkpoint-based state recovery for failed payouts (Closes Checkpoint-Based State Recovery After Failed Payout #564)
  4. feat: implement Soroban storage TTL bump management (Closes Soroban Storage TTL Bump Management #563)

Closes #573
Closes #574
Closes #564
Closes #563

…remainder

Implements acceptance criteria from issue Stellar-split#573:
- Add test named single_recipient_gets_full_amount in contracts/split/src/calc.rs
- Verify that result Vec has length 1
- Assert that result[0] == total (12345 stroops returned for single recipient)
- Test validates that distribute_with_remainder returns full amount without rounding down

Closes Stellar-split#573
…_with_remainder

Implements acceptance criteria from issue Stellar-split#574:
- Add test named sum_invariant_holds_with_unequal_ratios in contracts/split/src/calc.rs
- Test multiple cases where total is not evenly divisible by denom
- Case 1: 10 stroops across 3 recipients with ratios [1,1,1] (10 % 3 != 0)
- Case 2: 100 stroops across 4 recipients with ratios [2,3,1,4]
- Case 3: 999 stroops across 2 recipients with ratios [1,3]
- Assert result.iter().sum::<i128>() == total for all cases
- Validates the largest-remainder method's core invariant

Closes Stellar-split#574
…ssue Stellar-split#564)

Implements acceptance criteria from issue Stellar-split#564:
- Add InvoiceStatus::PayoutInProgress intermediate state for active payout
- Add DataKey::PayoutCheckpoint(u64) to track last successful transfer index
- Add ContractError::CheckpointMismatch for index validation during recovery
- Add ContractError::AlreadyPaid for recipients already processed
- Test validates checkpoint mechanism prevents double-payment on payout resumption

Technical changes:
- contracts/split/src/error.rs: Add CheckpointMismatch(64), AlreadyPaid(65) errors
- contracts/split/src/storage_keys.rs: Add PayoutCheckpoint(u64) to InvoiceKey enum
- contracts/split/src/types.rs: Add PayoutInProgress state to InvoiceStatus enum
- contracts/split/src/test.rs: Add test_checkpoint_recovery_after_failed_payout

The checkpoint system records the index of the last successfully transferred
payout, allowing resume_payout() to skip already-paid recipients and prevent
loss of funds if a transfer fails mid-loop.

Closes Stellar-split#564
…lit#563)

Implements acceptance criteria from issue Stellar-split#563:
- Create constants.rs with MIN_INVOICE_TTL_LEDGERS (518,400 = 60 days)
- Create constants.rs with MAX_INVOICE_TTL_LEDGERS (31,536,000 = 1 year)
- Create storage.rs with centralized save helpers:
  - save_invoice() calls bump() after persistent set()
  - save_recipients() calls bump() after persistent set()
  - save_contributor() calls bump() after persistent set()
- Add bump_invoice_ttl(invoice_id) entry point callable by any address
- Entry point bumps all known DataKey entries for an invoice
- Add test_ttl_bump_on_storage_writes test to verify TTL management

Technical changes:
- contracts/split/src/constants.rs: New file with TTL ledger count constants
- contracts/split/src/storage.rs: New file with storage helpers and tests
- contracts/split/src/lib.rs: Import constants and storage modules
- contracts/split/src/lib.rs: Add bump_invoice_ttl() entry point
- contracts/split/src/test.rs: Add test_ttl_bump_on_storage_writes

The TTL management system prevents silent data expiration on Soroban persistent
storage. All storage writes now automatically bump their TTL, and any address
can explicitly extend an invoice's TTL via bump_invoice_ttl().

Closes Stellar-split#563
@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@benjaminjohnsonfin-afk Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant