Skip to content

test(contract): property tests for time-delayed multi-sig emergency withdrawal - #670

Open
Samaro1 wants to merge 6 commits into
FinChippay:mainfrom
Samaro1:test/emergency-withdrawal-property
Open

test(contract): property tests for time-delayed multi-sig emergency withdrawal#670
Samaro1 wants to merge 6 commits into
FinChippay:mainfrom
Samaro1:test/emergency-withdrawal-property

Conversation

@Samaro1

@Samaro1 Samaro1 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

closes #628

What was implemented

Adds contracts/finchippay-contract/tests/emergency_withdrawal_property.rs — a property-based test suite (proptest + the Soroban test Env) proving the time-delayed, multi-sig-gated emergency withdrawal (initiate → approve → execute/cancel) cannot be executed early, double-executed, or executed after cancellation, even under adversarial ordering.

Coverage (12 tests, 10 property-driven scenarios)

  1. Activation-ledger arithmetic / off-by-one — over 200 randomized initiation ledgers, asserts activation_ledger == initiated_at + EMERGENCY_WITHDRAWAL_DELAY (pinned to 17_280, never DELAY±1) and that the ledger before activation is still gated.
  2. Dual-gate property — randomized (threshold, approval-count, ledger-offset ∈ {activation−1, activation, activation+1}) proves execution succeeds iff both the threshold is met and the activation ledger is reached; any single missed gate keeps the withdrawal pending with zero transfer.
  3. Exact activation boundary — executing at activation−1 reverts with the exact EmergencyWithdrawalNotReady error and zero transfer; executing at activation succeeds and transfers exactly once; post-execution execute/approve revert with “not pending” and never double-transfer.
  4. Threshold met exactly at activation — the approval that reaches the threshold at exactly activation_ledger auto-executes.
  5. Approval deduplication — duplicate approvals always revert with the exact error and never inflate approvals.len().
  6. Cancellation irreversibility — a cancelled withdrawal can never be approved, executed, or re-cancelled (even far past activation and after the threshold was fully met), and never transfers funds.
  7. Duplicate initiation — independent withdrawals with incrementing, non-colliding IDs and fully isolated approval state.
  8. Authorization — only the legacy admin can initiate/cancel (Unauthorized for any other signer); non-signers cannot approve (NotAdminSigner).
  9. Mid-flight threshold rotation — the stored threshold is snapshotted at initiation: lowering the global threshold doesn’t weaken a pending withdrawal; a new withdrawal picks up the new global threshold.
  10. Mid-flight signer-set rotation — removed signers lose the ability to approve while already-recorded approvals persist and remain sufficient.
  11. Model-based adversarial state-machine property — exact in-test reference Model mirrors the contract’s check ordering and auto-execution rules; randomized sequences of {advance ledger, approve (valid/duplicate/stranger), execute, cancel} are replayed against both model and contract across 40 cases, asserting the on-chain status/approvals/funds-match the model and every revert carries the exact expected panic message.

Verification

  • cargo fmt --check — new file is clean (repo has pre-existing formatting drift in untouched files).
  • cargo clippy -- -D warnings — new file contributes 0 warnings (pre-existing lib.rs violations are unrelated and untouched).
  • cargo test — full workspace green, including the 12 new tests.

Principles upheld

  • Deterministic fixed ledgers via the Soroban test Env (env.ledger().with_mut).
  • Exact error assertions against the contract’s panic payloads, mapped to ContractError::NotAdminSigner/EmergencyWithdrawalNotReady/Unauthorized, etc.
  • EMERGENCY_WITHDRAWAL_DELAY is intentionally hard-coded (not imported) so a change in the contract constant must be caught by the tests.

Samaro1 and others added 6 commits August 17, 2026 15:34
Replace the plain transaction list with a lightweight windowed virtualizer
so accounts with thousands of payments render only the visible rows,
keeping DOM size and memory bounded. Convert pagination to a cursor-based
scheme (Horizon paging tokens) so deep-page navigation never skips or
repeats rows when new transactions arrive between requests.

- VirtualizedList: dependency-free windowed list, roving-tabindex safe,
  with aria-posinset/aria-setsize per row and a polite live region
- api: encodeCursor/decodeCursor, readCursorFromQuery/updateCursorInUrl,
  dedupeById, mergeRecordPages, prependNewestUnique, cursorSlice
- transactions page: URL keeps ?cursor= anchor, echo-guarded sync
- eslint: set next.rootDir so lint-staged resolves /pages from frontend/
- 17 acceptance tests covering skip/duplicate safety, window mounting,
  ARIA, search across pages, and 10k/50k-row performance
- translations for the new "Newest" control across all locales
…n tests

Harden FinchippayContract against reentrancy from hostile token contracts.
Adds a non-reentrancy lock (DataKey::Reentrant + ContractError::ReentrantCall)
acquired by every value-transferring entry point, enforces
checks-effects-interactions ordering so state is committed before external
token.transfer calls, and adds a ReentrantToken test suite proving no
double-claim/double-drain/double-swap under reentrancy.

Fixes FinChippay#622

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…nChippay#384)

- Add PaymentBuilder component with drag-and-drop recipient reordering and drop targets
- Add QuickAddPanel for dragging token types (XLM, USDC) and preset amounts
- Add BatchSummary component with distribution bar chart and fee estimation
- Integrate payment builder mode toggle into SendPaymentForm and BatchPaymentForm
- Add undo/redo state history with shortcuts (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y)
- Implement screen reader live announcements for all payment builder actions
- Add comprehensive unit test suites and Storybook stories
The 'Manual Greptile Review' job was skipped on every PR because it was
gated to workflow_dispatch only, and the 'label-pr' job failed on fork
PRs (read-only GITHUB_TOKEN) so it was disabled. Move labeling to a
pull_request_target workflow that works for forks (API-only, never
executes PR code) and let the manual-review job validate the Greptile
config as a PR status check.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ken safety

The contract caches its own token balance (LastContractBalance) to avoid
re-reading token.balance(contract) on every deposit. For standard assets the
cache matches reality, but a rebasing or fee-on-transfer token (or any token
whose transfer moves less than the requested amount) can desynchronise the
cache from the real on-chain balance. A drifted cache could weaken the
phantom-deposit check in require_transfer_succeeded and let locked-balance
accounting over-claim.

Changes:
- Harden require_transfer_succeeded: the "before" balance for the
  phantom-deposit check is now the actual on-chain balance (via
  get_contract_balance), never a possibly-stale cache, so a fee-on-transfer
  deposit can never pass the check and over-claim.
- get_contract_balance now detects drift on read: when the cached value
  differs from token.balance(contract) it emits a balance_drift_detected
  event with (cached, actual) and self-heals the cache.
- Add admin-gated reconcile_balance (via propose_admin_action action_type
  "reconcile_balance") that resyncs LastContractBalance with the actual
  balance and emits a balance_reconciled event with (old, new).
- Add tests/balance_reconciliation.rs with FeeOnTransferToken (99% fee) and
  RebasingToken mocks proving deposits/claims stay correct across escrow,
  stream, and multi-sig flows and that no funds can be over-claimed.
- Document the supported token model (standard non-rebasing assets) and the
  failure mode for fee-on-transfer / rebasing tokens.
- Bump CONTRACT_VERSION to 4.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@github-actions

Copy link
Copy Markdown

🤖 Greptile AI Code Review

Greptile will automatically review this PR (39 file(s) changed).

Review gates:

  • ✅ CodeQL Security Scan
  • ✅ Custom rules (.greptile/config.json)
  • ✅ Architecture guidelines (.greptile/rules.md)

To manually trigger a re-review, comment @greptileai on this PR.
To skip review, add the skip-review label.

@github-actions github-actions Bot added the needs-review PR ready for Greptile AI code review label Aug 17, 2026
{isAdvanced && (
<button
onClick={goToNewest}
disabled={loading || loadingMore}
@@ -0,0 +1,238 @@
import React from "react";
import { render, screen, fireEvent, act } from "@testing-library/react";
@Topmatrixmor2014

Copy link
Copy Markdown
Contributor

Please fix this review observations. Good job

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

Labels

needs-review PR ready for Greptile AI code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue #59 — Emergency Withdrawal Edge-Case Property Tests

6 participants