Skip to content

fix: mark_paid payout hardening, add_volume attribution, execute_proposal quorum manipulation (#619-622) - #625

Merged
Levi-Ojukwu merged 10 commits into
Invoice-Liquidity-Network:mainfrom
mhikel66:fix/mark-paid-add-volume-execute-proposal-619-622
Jul 29, 2026
Merged

fix: mark_paid payout hardening, add_volume attribution, execute_proposal quorum manipulation (#619-622)#625
Levi-Ojukwu merged 10 commits into
Invoice-Liquidity-Network:mainfrom
mhikel66:fix/mark-paid-add-volume-execute-proposal-619-622

Conversation

@mhikel66

@mhikel66 mhikel66 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes for issues #619, Fixes #620, Fixes #621, Fixes #622. Note up front: #619 and #621 were both already resolved on main before this PR — details below, with the actual commits identified. #620 and #622 were genuinely still open and are fixed here.

#619mark_paid LP payout underflow — already fixed, hardened further

The exact line the issue cites (lp_earned = primary_lp_payout - primary_lp_funded) was a bare subtraction in an earlier commit, but main already uses .saturating_sub(...) today (introduced before this issue was filed — see the commit history on contracts/invoice_liquidity/src/lib.rs). The panic/wraparound impact described in the issue is not reachable on current main; it's also independently mitigated by this repo's [profile.release] overflow-checks = true.

Since the acceptance criteria specifically asks for checked_sub (not saturating_sub) plus explicit error handling around the adjacent overflow risk, this PR still makes that change for auditability and defense-in-depth:

  • primary_lp_payout's checked_mul(...).unwrap_or(0) now returns ContractError::ArithmeticOverflow instead of silently collapsing to a corrupting zero payout on genuine overflow.
  • lp_earned now uses checked_sub(...).unwrap_or(0) explicitly (same runtime behavior as the existing saturating_sub, just auditable at the call site).
  • Added 3 tests per the acceptance criteria (distribute_amount slightly less than / equal to / significantly less than invoice.amount), asserting settlement succeeds and pays the correct non-negative amount in each case.

Recommend closing #619 referencing the commit that already fixed it, once maintainers confirm.

#620add_volume double-counting / hardcoded EURC index — fixed

Confirmed live and exactly as described: a redundant XLM check block after an early return, and token_list.get(2) hardcoded for EURC. Rather than looping the token list by a different index scheme, add_volume now matches by the token's actual configured SAC address (is_xlm_token/is_usdc_token/is_eurc_token, which already existed for normalization elsewhere in the file) via an if/else if chain — each token increments at most one counter, with no dependency on TokenList order at all. Added 2 tests: one confirming no cross-contamination across all three tokens, one confirming correct EURC attribution after reordering TokenList via remove_token/add_token.

#621 — unbounded reputation decay loop — already fixed

Also already resolved on main: get_payer_score already caps periods_passed against MAX_REPUTATION_DECAY_PERIODS (1000) and short-circuits to a score of 0 beyond that, with both acceptance-criteria tests already present (test_reputation_decay_bounded_for_extremely_long_inactivity, test_reputation_decay_bounded_when_decay_period_is_one_ledger). No changes made — re-implementing an identical fix would just be a no-op diff. Recommend closing with reference to the existing implementation/tests.

#622 — caller-supplied total_supply in execute_proposal — fixed, with one adjustment

Confirmed live: execute_proposal(proposal_id, total_supply) let any caller pick the quorum denominator. The issue's acceptance criteria asks to query total_supply from the governance token on-chain — that's not possible with this repo's pinned soroban-sdk = "21.4.0": neither TokenInterface nor StellarAssetInterface in that version expose a total_supply() method (verified directly against the vendored SDK source). Instead:

  • total_supply is now seeded at initialize() time (new required gov_token_total_supply: i128 param) and stored as contract state.
  • New set_gov_token_total_supply / get_gov_token_total_supply, gated by the same iln_contract.require_auth() boundary already used for set_min_quorum_bps / set_min_proposal_balance.
  • execute_proposal(proposal_id) drops the parameter entirely and reads the stored value.
  • This closes the actual vulnerability (an arbitrary caller can no longer choose the quorum denominator per-call) even though it's config rather than a live balance query — the same trust model this contract already uses for every other quorum-relevant parameter.
  • Updated the SDK's executeProposal — which, independently of this issue, was already calling the contract with completely mismatched arguments (an address where proposal_id was expected, and no total_supply at all) — to just pass proposal_id, fixing that latent bug as a side effect.
  • Updated every initialize/execute_proposal call site across contracts/iln_governance/src/test.rs (~24 sites) and tests_benchmarks.rs. One test (test_execute_quorum_not_reached_rejected) needed a real behavioral adjustment — with the new seeded default it would've hit quorum instead of missing it — fixed via an explicit set_gov_token_total_supply override to restore its original intent.

Pre-existing, unrelated issues found (not fixed here, out of scope)

  • contracts/tests/governance_lifecycle_test.rs and contracts/tests/governance_main_integration_test.rs both call iln_governance's initialize(...) with the wrong argument count independent of this PR (missing distribution_contract) — already broken on main today. governance_lifecycle_test.rs additionally isn't wired into any [[test]] target in any Cargo.toml, so it isn't compiled at all currently. Updated both files' execute_proposal/initialize call sites for consistency with the new signature anyway, but did not fix the pre-existing arg-count mismatch — that's unrelated to any of these 4 issues.
  • contracts/fuzz/src/lib.rs calls both invoice_liquidity::initialize and iln_governance::initialize with argument counts that don't match either contract's current signature — already broken on main, unrelated to this PR.
  • contracts/invoice_liquidity/src/tests_new_features.rs and tests_mev_mitigation.rs fail to compile on current main (missing MockAuth/MockAuthInvoke/testutils::Events imports) — confirmed via A/B against the pristine files, unrelated to this PR.
  • test::test_upgrade_does_not_affect_existing_invoices, test::test_upgrade_emits_correct_event, test::test_upgrade_snapshot_before_after, and tests_min_invoice_amount::test_admin_adds_token_with_different_decimals fail on current main independent of this PR (confirmed via A/B).
  • iln_governance's test::test_vote_receipt_available_within_ttl fails on current main independent of this PR (confirmed via A/B) — appears to be a ledger/TTL-archival interaction unrelated to total_supply.
  • docs/contract-spec.json (generated via scripts/gen-spec.ts) is now stale for both changed contract interfaces — flagging for a maintainer to regenerate, since doing so requires the full Soroban CLI build toolchain.

Verification

Built a scratch Cargo workspace (invoice_liquidity + iln_governance + insurance_pool, matching the real workspace members and the repo's actual Cargo.lock) and ran, natively (no local git clone/push involved — everything above was pushed via the GitHub API):

  • cargo test -p invoice_liquidity --lib: 118 passed, 4 failed — all 4 confirmed pre-existing via A/B against the pristine files (see above).
  • cargo test -p iln_governance --lib: 97 passed, 1 failed — confirmed pre-existing via A/B.
  • cargo clippy on both crates: no new warnings beyond what's already on main.
  • cargo fmt --check: clean on every line this PR touches (pre-existing unrelated formatting nits elsewhere untouched).

Test plan

@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@mhikel66 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

@Levi-Ojukwu
Levi-Ojukwu merged commit 8bf9b80 into Invoice-Liquidity-Network:main Jul 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants