You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If release_funds() fails mid-loop because one recipient's token transfer traps, the entire Soroban transaction rolls back — but the contract retains no record of which payouts were completed before the failure, so a re-attempt starts from index zero and risks double-payment to recipients earlier in the list. A checkpoint mechanism must record the index of the last successfully transferred payout so that a recovery entry point can resume from the correct position.
Technical Context
contracts/split/src/lib.rs — release_funds() payout loop and a new resume_payout(invoice_id: u64, from_index: u32)#[contractimpl] entry point; contracts/split/src/storage.rs — add DataKey::PayoutCheckpoint(u64) storing a u32 index of the last confirmed transfer; contracts/split/src/types.rs — add InvoiceStatus::PayoutInProgress intermediate state; contracts/split/src/auth.rs — resume_payout() requires auth from the invoice creator or the platform admin; contracts/split/src/events.rs — emit payout_checkpoint_saved(invoice_id, index, recipient) after each successful transfer().
Acceptance Criteria
After each successful token::Client::transfer() in release_funds(), DataKey::PayoutCheckpoint(invoice_id) is updated with the current 0-based recipient index before proceeding to the next
resume_payout(invoice_id, from_index) reads the stored checkpoint, returns Error::CheckpointMismatch if from_index does not match the stored value, and otherwise continues the transfer loop from that position
Attempting to pay a recipient at an index strictly less than the stored checkpoint value returns Error::AlreadyPaid without executing a transfer
Once all recipients are paid, DataKey::PayoutCheckpoint is deleted and invoice status transitions from PayoutInProgress to Settled
A test in contracts/split/src/tests/checkpoint_tests.rs simulates a trap on recipient index 2 of 5, then calls resume_payout(invoice_id, 2) and asserts that only recipients at indices 2, 3, and 4 receive funds in the recovery run
All CI checks (cargo build --target wasm32-unknown-unknown, cargo test, cargo clippy) pass and the branch has no merge conflicts
Description
If
release_funds()fails mid-loop because one recipient's token transfer traps, the entire Soroban transaction rolls back — but the contract retains no record of which payouts were completed before the failure, so a re-attempt starts from index zero and risks double-payment to recipients earlier in the list. A checkpoint mechanism must record the index of the last successfully transferred payout so that a recovery entry point can resume from the correct position.Technical Context
contracts/split/src/lib.rs—release_funds()payout loop and a newresume_payout(invoice_id: u64, from_index: u32)#[contractimpl]entry point;contracts/split/src/storage.rs— addDataKey::PayoutCheckpoint(u64)storing au32index of the last confirmed transfer;contracts/split/src/types.rs— addInvoiceStatus::PayoutInProgressintermediate state;contracts/split/src/auth.rs—resume_payout()requires auth from the invoice creator or the platform admin;contracts/split/src/events.rs— emitpayout_checkpoint_saved(invoice_id, index, recipient)after each successfultransfer().Acceptance Criteria
token::Client::transfer()inrelease_funds(),DataKey::PayoutCheckpoint(invoice_id)is updated with the current 0-based recipient index before proceeding to the nextresume_payout(invoice_id, from_index)reads the stored checkpoint, returnsError::CheckpointMismatchiffrom_indexdoes not match the stored value, and otherwise continues the transfer loop from that positionError::AlreadyPaidwithout executing a transferDataKey::PayoutCheckpointis deleted and invoice status transitions fromPayoutInProgresstoSettledcontracts/split/src/tests/checkpoint_tests.rssimulates a trap on recipient index 2 of 5, then callsresume_payout(invoice_id, 2)and asserts that only recipients at indices 2, 3, and 4 receive funds in the recovery run