| type | Feature |
|---|---|
| title | Add a clear_sme_collateral_commitment entrypoint to release recorded collateral metadata |
| labels | type:feature, area:collateral, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN |
| assignees |
record_sme_collateral_commitment in escrow/src/lib.rs writes a metadata-only DataKey::SmeCollateralPledge and emits CollateralRecordedEvt, but there is no way to remove it. Once recorded, the commitment lingers in storage and is surfaced by get_sme_collateral_commitment forever, even after the underlying pledge is released off-chain β so indexers and dashboards report stale collateral on a settled or cancelled invoice.
This issue adds an SME-authorized clear_sme_collateral_commitment() that removes the pledge entry and emits a dedicated retirement event, mirroring the existing record path.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
clear_sme_collateral_commitment(env) -> ()that loads the escrow viaload_escrow_require_sme, asserts a commitment exists (append-only typed errorNoCollateralToClear), removesDataKey::SmeCollateralPledge, and emits a newCollateralClearedEvt#[contractevent]carryinginvoice_idand the prior amount. - Preserve the metadata-only semantics documented on
record_sme_collateral_commitment: no token movement, no balance reservation. - Keep guard ordering consistent with ADR-002 (read-only existence check, then
require_auth, then the storage remove and event). - Do not renumber existing
EscrowErrorcodes; append the new variant.
- Fork the repo and create a branch
git checkout -b feature/contracts-clear-sme-collateral- Implement changes
- Write code in:
escrow/src/lib.rsβclear_sme_collateral_commitment,CollateralClearedEvt,NoCollateralToClear. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ record then clear, clear-without-record rejection, non-SME caller rejection, event payload. - Add documentation: update
docs/escrow-sme-collateral.mdand the README entrypoint table. - Include NatSpec-style
///comments on the new entrypoint and event. - Validate security: SME-only auth, no token movement, idempotent removal.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: clear with no prior commitment, wrong caller, clear after settle/cancel.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add clear_sme_collateral_commitment entrypoint to retire collateral metadata with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Expose the configured yield-tier table through a read-only view" labels: type:feature, area:read-api, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
init in escrow/src/lib.rs persists an optional DataKey::YieldTierTable (validated by validate_yield_tiers_table) and fund_with_commitment consumes it via effective_yield_for_commitment, but there is no public getter for the tier table. Investors deciding which committed_lock_secs to pick, and dashboards rendering the tier ladder, cannot read the on-chain tiers β they must reconstruct them from the EscrowInitialized event or off-chain config.
This issue adds a pure get_yield_tiers(env) -> Vec<YieldTier> read returning the stored table (empty when none was configured).
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
get_yield_tiers(env) -> Vec<YieldTier>readingDataKey::YieldTierTable, returning an emptyVecwhen unset (matching theinit"empty tiers not stored" behavior). - Pure read: no auth, no state change; consistent with the other
get_*views. - Document that the returned order matches the validated non-decreasing tier ordering enforced at
init.
- Fork the repo and create a branch
git checkout -b feature/contracts-get-yield-tiers- Implement changes
- Write code in:
escrow/src/lib.rsβget_yield_tiersview. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ table round-trips through init, empty when no tiers, ordering preserved. - Add documentation: update
docs/escrow-read-api.mdand ADR-005. - Include NatSpec-style
///comments on the view. - Validate security: pure read, no auth, no mutation.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: no tiers, single tier, multi-tier ordering, legacy instance.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add get_yield_tiers read view for the configured tier table with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a funding deadline so under-funded escrows can expire and become cancellable" labels: type:feature, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The escrow in escrow/src/lib.rs has no time limit on the open (status 0) funding window: fund_impl accepts deposits indefinitely while status == 0, and cancel_funding requires the admin to act manually. There is no on-chain signal that a primary issuance has stalled, so investors' principal can sit in an open escrow with no automatic recovery trigger.
This issue adds an optional funding_deadline (ledger timestamp) configured at init: after it passes, new fund calls are rejected and the escrow is eligible for cancellation/refund recovery.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add an
Option<u64> funding_deadlineparameter toinit, validated (0/absent β no deadline; otherwise must be> now), stored under a newDataKey::FundingDeadline. - In
fund_impl, after the status/legal-hold checks, reject deposits when a deadline is set andnow > deadlinewith an append-only typed errorFundingDeadlinePassed. - Add a pure
get_funding_deadline(env) -> Option<u64>view and anis_funding_expired(env) -> boolhelper. - Preserve the
funding_deadline == 0"no deadline" semantics; do not affect already-funded (status 1) escrows.
- Fork the repo and create a branch
git checkout -b feature/contracts-funding-deadline- Implement changes
- Write code in:
escrow/src/lib.rsβDataKey::FundingDeadline, init param/validation, fund gate, views, error. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ fund before/after deadline, no-deadline default,is_funding_expiredtransitions usingLedgertestutils. - Add documentation: update
docs/escrow-lifecycle.mdanddocs/escrow-ledger-time.md. - Include NatSpec-style
///comments on the new param, views, and error. - Validate security: deadline cannot retroactively trap funded escrows; ledger-time trust model documented.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: no deadline, exactly at deadline, after deadline, funded before deadline.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add optional funding deadline gating fund and recovery with tests and docs
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add an admin entrypoint to rebind the off-chain registry reference" labels: type:feature, area:admin, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
init in escrow/src/lib.rs optionally stores DataKey::RegistryRef, surfaced by get_registry_ref, but the pointer is write-once at init β there is no entrypoint to update it. If the off-chain registry contract is redeployed or the address was set incorrectly, the escrow points at a stale registry for its entire life with no recovery short of redeploying the whole escrow.
This issue adds an admin-gated set_registry_ref(new_registry: Option<Address>) so the reference can be corrected or cleared.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
set_registry_ref(env, new_registry: Option<Address>)gated viaload_escrow_require_admin;SomesetsDataKey::RegistryRef,Noneremoves it. - Emit a new
RegistryRefUpdated#[contractevent]carryinginvoice_id, the prior registry (if any), and the new value for indexers. - The registry is an informational pointer only; document that rebinding does not migrate or revalidate any registry-side state.
- Keep ADR-002 guard ordering: load escrow + admin
require_authbefore the storage write.
- Fork the repo and create a branch
git checkout -b feature/contracts-set-registry-ref- Implement changes
- Write code in:
escrow/src/lib.rsβset_registry_ref,RegistryRefUpdatedevent. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ set, clear, non-admin rejection,get_registry_refreflects update, event payload. - Add documentation: update
docs/escrow-data-model.mdand the README entrypoint table. - Include NatSpec-style
///comments on the entrypoint and event. - Validate security: admin-only, no impact on funds or status.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: set from none, overwrite existing, clear to none, non-admin caller.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add admin set_registry_ref entrypoint to rebind the registry pointer with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a remaining-funding-capacity read view for the open funding window" labels: type:enhancement, area:read-api, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Front-ends sizing a deposit must currently read InvoiceEscrow::funding_target and funded_amount separately from get_escrow/get_escrow_summary (escrow/src/lib.rs) and subtract them client-side, re-deriving the saturating semantics. There is no single on-chain view that answers "how much more can be funded before the target is reached?", and over-funding past the target is permitted, which clients frequently mishandle.
This issue adds a pure get_remaining_funding_capacity(env) -> i128 view returning max(0, funding_target - funded_amount).
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
get_remaining_funding_capacity(env) -> i128returningfunding_target.saturating_sub(funded_amount)clamped at0so it never goes negative when over-funded. - Pure read, no auth, no mutation; reuse the loaded escrow.
- Document that this is informational only β
fundmay still accept deposits that over-fund past the target whilestatus == 0.
- Fork the repo and create a branch
git checkout -b feature/contracts-remaining-capacity-view- Implement changes
- Write code in:
escrow/src/lib.rsβget_remaining_funding_capacityview. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ capacity at zero/partial/exact/over-funded states. - Add documentation: update
docs/escrow-read-api.md. - Include NatSpec-style
///comments on the view. - Validate security: clamped non-negative, no mutation.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: unfunded, partially funded, exactly funded, over-funded.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add get_remaining_funding_capacity read view with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Extend EscrowSummary with collateral commitment and attestation status" labels: type:enhancement, area:read-api, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
get_escrow_summary in escrow/src/lib.rs bundles core state (escrow, legal hold, snapshot, funder count, allowlist flag, schema version) into a single EscrowSummary host call, but it omits two metadata families that callers must fetch separately: the SME collateral commitment (get_sme_collateral_commitment) and the attestation binding (get_primary_attestation_hash / append-log length). Dashboards therefore make three extra round-trips for a complete view.
This issue extends EscrowSummary (additively) with collateral presence/amount and attestation status so one call returns the full picture.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add fields to
EscrowSummary: an optional collateral commitment (Option<SmeCollateralCommitment>), whether a primary attestation hash is bound (bool), and the attestation append-log length (u32). - Populate them in
get_escrow_summaryby reusing existing getters; keep the existing fields and their order stable per the additive-key policy (ADR-007). - Pure read, no auth; ensure legacy instances with no collateral/attestation return the unset/zero defaults.
- Fork the repo and create a branch
git checkout -b feature/contracts-summary-collateral-attestation- Implement changes
- Write code in:
escrow/src/lib.rsβ extendEscrowSummaryandget_escrow_summary. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ summary with/without collateral and attestations, log-length accuracy. - Add documentation: update
docs/escrow-read-api.mdanddocs/escrow-data-model.md. - Include NatSpec-style
///comments on the new fields. - Validate security: pure read, defaults for legacy state, stable field ordering.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: no collateral, recorded collateral, no attestation, bound + appended attestations.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: extend EscrowSummary with collateral and attestation status with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Make the dust-sweep per-call ceiling an admin-configurable parameter" labels: type:feature, area:treasury, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
sweep_terminal_dust in escrow/src/lib.rs hard-caps each sweep at the compile-time constant MAX_DUST_SWEEP_AMOUNT (100_000_000 base units), rejecting larger requests with SweepAmountExceedsMax. For high-decimal tokens or large rounding residues this fixed ceiling can be too small, forcing many repeated sweeps; for low-value tokens it may be looser than desired. The cap cannot be tuned per deployment.
This issue adds an optional admin-configured override stored at init/via an admin setter, falling back to MAX_DUST_SWEEP_AMOUNT when unset.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
DataKey::MaxDustSweepOverride(i128); add an admin entrypointset_max_dust_sweep(env, cap: i128)validated tocap > 0, gated viaload_escrow_require_admin, emitting aMaxDustSweepUpdatedevent. - In
sweep_terminal_dust, use the override when present, otherwiseMAX_DUST_SWEEP_AMOUNT; keep the liability-floor invariant unchanged. - Add a
get_max_dust_sweep(env) -> i128view returning the effective cap. - Append any new
EscrowErrorcodes; never renumber.
- Fork the repo and create a branch
git checkout -b feature/contracts-configurable-dust-cap- Implement changes
- Write code in:
escrow/src/lib.rsβ override key,set_max_dust_sweep, getter, sweep cap logic, event. - Write comprehensive tests in:
escrow/src/tests/integration.rsβ default cap, raised cap allows larger sweep, lowered cap rejects, non-admin setter rejection, liability floor still holds. - Add documentation: update
docs/escrow-gas-storage-notes.mdand ADR-006. - Include NatSpec-style
///comments on the setter, getter, and event. - Validate security: admin-only override, positive bound, floor invariant preserved.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: unset default, exactly at override, above override, non-admin caller, floor interaction.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add admin-configurable dust-sweep cap overriding the compile-time max with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add tests for the attestation bind and bounded append-log flow" labels: type:test, area:attestations, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
bind_primary_attestation_hash and append_attestation_digest in escrow/src/lib.rs implement a write-once primary hash plus a bounded append-only audit chain (capped at MAX_ATTESTATION_APPEND_ENTRIES = 32), with typed errors PrimaryAttestationAlreadyBound and AttestationAppendLogCapacityReached. These admin-gated funds-adjacent provenance writes need dedicated coverage in escrow/src/tests/attestations.rs to prove write-once, capacity, indexing, and auth boundaries.
This issue adds an exhaustive attestation test suite.
- Repository scope: Liquifact/Liquifact-contracts only.
- Assert
bind_primary_attestation_hashsucceeds once and rejects a second bind withPrimaryAttestationAlreadyBound;get_primary_attestation_hashreflects the bound value. - Assert
append_attestation_digestappends in order, increments the index inAttestationDigestAppended, and rejects the 33rd entry withAttestationAppendLogCapacityReached;get_attestation_append_logreturns the full ordered vector. - Assert both entrypoints reject non-admin callers via
mock_auths. - No production change unless a real gap surfaces (then file/fix separately).
- Fork the repo and create a branch
git checkout -b test/contracts-attestation-flow- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a gap surfaces. - Write comprehensive tests in:
escrow/src/tests/attestations.rsβ bind, re-bind rejection, append ordering, capacity, auth. - Add documentation: cross-link scenarios in
docs/escrow-attestations.md. - Include NatSpec-style
///comments on shared helpers. - Validate security: write-once primary, bounded log growth, admin-only.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: empty log, full log boundary, double bind, non-admin caller.
- Include full
cargo testoutput and a short security notes section in the PR.
test: add coverage for attestation bind and bounded append-log flow
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add tests for the SME collateral commitment record and replace path" labels: type:test, area:collateral, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
record_sme_collateral_commitment in escrow/src/lib.rs is a metadata-only write with non-trivial validation: positive amount (CollateralAmountNotPositive), non-empty asset symbol (CollateralAssetEmpty), monotonic recorded_at on replacement (CollateralTimestampBackwards), SME-only auth, and a CollateralRecordedEvt carrying the prior amount. This path has no dedicated coverage proving each validation branch and the replace-overwrite behavior.
This issue adds a focused collateral-commitment test suite.
- Repository scope: Liquifact/Liquifact-contracts only.
- Assert a first record succeeds and
get_sme_collateral_commitmentreturns the asset/amount/timestamp; the event'sprior_amountis0. - Assert replacement overwrites and emits the prior amount; assert a backwards ledger timestamp is rejected with
CollateralTimestampBackwardsusingLedgertestutils. - Assert rejection of zero/negative amount and empty asset symbol, and that a non-SME caller is rejected.
- Confirm the metadata-only invariant: no token balance changes occur.
- Fork the repo and create a branch
git checkout -b test/contracts-collateral-commitment- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a gap surfaces. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ record, replace, validation rejections, auth, no token movement. - Add documentation: cross-link scenarios in
docs/escrow-sme-collateral.md. - Include NatSpec-style
///comments on helpers. - Validate security: SME-only, validation completeness, metadata-only.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero amount, empty asset, backwards timestamp, replace, non-SME caller.
- Include full
cargo testoutput and a short security notes section in the PR.
test: add coverage for SME collateral commitment record and replace path
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add dual-authorization tests for the beneficiary rotation entrypoint" labels: type:test, area:beneficiary-rotation, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
rotate_beneficiary in escrow/src/lib.rs is a funds-routing-critical entrypoint: it changes sme_address (the withdrawal recipient) and uniquely requires both the outgoing SME and the admin to authorize, only in pre-settlement states (status 0 or 1), with a no-op guard (NewSmeSameAsCurrent), a state guard (RotationNotOpen), and a legal-hold gate. This dual-auth path has no dedicated test asserting that a single signer is insufficient.
This issue adds a rotation test suite covering both signers, the guards, and the emitted event.
- Repository scope: Liquifact/Liquifact-contracts only.
- Assert rotation succeeds only when both SME and admin authorize; assert it fails with only SME, only admin, or neither (via
mock_auths). - Assert
BeneficiaryRotatedcarries the correct prior/new SME and that a subsequentwithdrawwould route to the new beneficiary. - Assert guards:
NewSmeSameAsCurrent(no-op),RotationNotOpen(settled/withdrawn/cancelled), andLegalHoldBlocksBeneficiaryRotationwhile a hold is active. - No production change unless a guard gap surfaces.
- Fork the repo and create a branch
git checkout -b test/contracts-beneficiary-rotation- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a gap surfaces. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ dual-auth matrix, guards, event, post-rotation withdraw target. - Add documentation: cross-link scenarios in
docs/ESCROW_BENEFICIARY_ROTATION.md. - Include NatSpec-style
///comments on helpers. - Validate security: both signers required; rotation blocked post-settlement and under hold.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: missing one signer, same-address no-op, wrong status, legal hold active.
- Include full
cargo testoutput and a short security notes section in the PR.
test: add dual-auth and guard tests for rotate_beneficiary
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add boundary tests for min-contribution floor and the per-investor and unique caps" labels: type:test, area:investor-caps, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
fund_impl in escrow/src/lib.rs enforces three independent limits: a per-call minimum (MinContributionFloor β FundingBelowMinContribution), a cumulative per-investor cap (MaxPerInvestorCap β InvestorContributionExceedsCap), and a distinct-funder cap (MaxUniqueInvestorsCap β UniqueInvestorCapReached), plus their init-time validation (MinContributionNotPositive, MinContributionExceedsAmount, MaxPerInvestorNotPositive, MaxUniqueInvestorsNotPositive). These boundary conditions need exhaustive coverage at the exact limit values.
This issue adds boundary tests for each floor/cap, including the interaction with follow-on deposits.
- Repository scope: Liquifact/Liquifact-contracts only.
- Floor: deposit below floor rejected, exactly at floor accepted, follow-on below floor still rejected (floor applies per call).
- Per-investor cap: cumulative deposits exactly at cap accepted, one over rejected (across multiple
fundcalls). - Unique cap: distinct funders up to the cap accepted, the next new funder rejected, while follow-on deposits from existing funders still succeed.
- Init validation: assert each
init-time rejection for non-positive/over-amount configurations.
- Fork the repo and create a branch
git checkout -b test/contracts-caps-and-floor-boundaries- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a gap surfaces. - Write comprehensive tests in:
escrow/src/tests/cap_validation.rsβ floor, per-investor cap, unique cap, init validation boundaries. - Add documentation: cross-link scenarios in
docs/escrow-investor-caps.md. - Include NatSpec-style
///comments on helpers. - Validate security: caps and floor are inclusive/exclusive exactly as documented.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: exact floor, exact per-investor cap, exact unique cap, follow-on deposits, init validation failures.
- Include full
cargo testoutput and a short security notes section in the PR.
test: add boundary tests for min-contribution floor and investor caps
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Convert the tiered second-deposit panic in fund_with_commitment to a typed error" labels: type:security, area:errors, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
fund_impl (reached via fund_with_commitment) in escrow/src/lib.rs still uses a raw assert! with a panic string β "Additional principal after a tiered first deposit must use fund(), not fund_with_commitment()" β when an investor with an existing contribution (prev != 0) calls the commitment path again. Every other funding guard uses the append-only EscrowError enum, and the contract's SDK contract is that callers "branch on the numeric code rather than legacy panic strings". This one assert breaks that discipline on a funding-critical path.
This issue replaces the assert with a typed error.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add an append-only
EscrowErrorvariant (e.g.TieredSecondDepositNotAllowed); never renumber existing codes. - Replace the
assert!(prev == 0, ...)in the tiered branch withensure(&env, prev == 0, EscrowError::TieredSecondDepositNotAllowed). - Preserve exact behavior and guard ordering β only the revert type changes;
fund()follow-on deposits remain unaffected.
- Fork the repo and create a branch
git checkout -b security/contracts-tiered-second-deposit-typed-error- Implement changes
- Write code in:
escrow/src/lib.rsβ new error variant andensurecall. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ assert the typed error viatry_fund_with_commitmentafter a prior deposit; assertfund()follow-on still works. - Add documentation: update
docs/escrow-error-messages.mdand ADR-005. - Include NatSpec-style
///comments on the new variant. - Validate security: identical revert condition, stable numeric codes.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: tiered first deposit then tiered second (rejected), tiered first then
fund(accepted). - Include full
cargo testoutput and a short security notes section in the PR.
fix: replace tiered second-deposit panic with typed EscrowError in fund_with_commitment with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Bound the commitment lock so an investor claim cannot be locked past maturity" labels: type:security, area:tiered-yield, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
In fund_impl (escrow/src/lib.rs) a tiered deposit derives InvestorClaimNotBefore = now + committed_lock_secs, enforced later in claim_investor_payout via InvestorCommitmentLockNotExpired. The lock is only checked for arithmetic overflow (InvestorClaimTimeOverflow); it is not bounded relative to the escrow's maturity. A tier lock longer than the maturity window means a settled escrow (status 2) holds an investor's payout claim hostage past the point where principal is due β funds the investor is entitled to are unclaimable until the lock expires.
This issue rejects, at deposit time, any commitment lock that would push the claim time beyond settlement maturity.
- Repository scope: Liquifact/Liquifact-contracts only.
- When
committed_lock_secs > 0and the escrow has a maturity lock (maturity > 0), reject the deposit ifnow + committed_lock_secs > maturitywith a new append-onlyEscrowError(e.g.CommitmentLockExceedsMaturity). - Preserve the
committed_lock_secs == 0(no lock) andmaturity == 0(no maturity lock) semantics β only constrain when both are set. - Keep the existing
InvestorClaimTimeOverflowoverflow guard; this is an additional, narrower bound.
- Fork the repo and create a branch
git checkout -b security/contracts-commitment-lock-bound- Implement changes
- Write code in:
escrow/src/lib.rsβ bound check in the tiered branch, new error variant. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ lock within maturity accepted, lock past maturity rejected, no-maturity escrow unaffected, zero-lock unaffected, usingLedgertestutils. - Add documentation: update ADR-005 and
docs/escrow-legal-hold.mdcross-reference for claim timing. - Include NatSpec-style
///comments on the new bound and error. - Validate security: no payout can be locked beyond the funds-due maturity.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: lock exactly at maturity, lock one second past maturity, no maturity, zero lock.
- Include full
cargo testoutput and a short security notes section in the PR.
fix: bound commitment lock to settlement maturity in fund_with_commitment with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Document the SME collateral commitment model and its metadata-only guarantees" labels: type:docs, area:collateral, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
record_sme_collateral_commitment in escrow/src/lib.rs carries an important and easily-misread guarantee: it is metadata-only β it writes DataKey::SmeCollateralPledge and emits CollateralRecordedEvt but does not transfer tokens, reserve balances, verify custody, create an on-chain encumbrance, or block any flow. Misreading this as an enforced lien is a material risk for integrators. The existing docs/escrow-sme-collateral.md should be made authoritative and code-accurate.
This issue produces a complete, code-accurate collateral-commitment document.
- Repository scope: Liquifact/Liquifact-contracts only.
- Document the SME-only auth, the validation rules (positive amount, non-empty asset symbol, monotonic
recorded_aton replace), and replacement semantics with theprior_amountevent field. - Prominently state the metadata-only limitations and contrast with the on-chain custody flows so integrators do not treat it as an enforced encumbrance.
- Reference the
SmeCollateralCommitmentstruct fields and theCollateralRecordedEvttopic/payload.
- Fork the repo and create a branch
git checkout -b docs/contracts-collateral-model- Implement changes
- Write code in:
escrow/src/lib.rsβ only rustdoc clarifications if the inline comment drifts from docs. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ a test asserting no token-balance change accompanies a record, anchoring the metadata-only claim. - Add documentation: rewrite/expand
docs/escrow-sme-collateral.md; cross-link fromREADME.md. - Include NatSpec-style
///comments where clarified. - Validate security: documented behavior matches enforced rules.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: record with/without replacement, asset symbol formatting, anchoring no-balance-change test.
- Include full
cargo testoutput and a short security notes section in the PR.
docs: document SME collateral commitment metadata-only model with anchoring test
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Document the beneficiary rotation flow and its dual-authorization requirement" labels: type:docs, area:beneficiary-rotation, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
rotate_beneficiary in escrow/src/lib.rs changes the SME withdrawal recipient and is the only entrypoint requiring both the outgoing SME and the admin to sign, restricted to pre-settlement states (status 0/1), with a no-op guard and a legal-hold gate. Because it redirects where funded principal is eventually disbursed, its authorization model and timing constraints need a precise, code-accurate operator-facing document; docs/ESCROW_BENEFICIARY_ROTATION.md should be the authoritative reference.
This issue produces a complete rotation document.
- Repository scope: Liquifact/Liquifact-contracts only.
- Document the dual SME+admin
require_authrequirement, why both are needed, and the exact guard ordering (legal hold, status, no-op, dual auth). - Document the allowed states (open/funded only) and the rejection codes
RotationNotOpen,NewSmeSameAsCurrent,LegalHoldBlocksBeneficiaryRotation. - Explain the downstream effect on
withdraw(funds route to the newsme_address) and theBeneficiaryRotatedevent for indexers.
- Fork the repo and create a branch
git checkout -b docs/contracts-beneficiary-rotation- Implement changes
- Write code in:
escrow/src/lib.rsβ only rustdoc corrections if inline docs drift. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ a test asserting post-rotation withdrawal target matches the new SME, anchoring the doc. - Add documentation: rewrite/expand
docs/ESCROW_BENEFICIARY_ROTATION.md; reconcile with ADR-002. - Include NatSpec-style
///comments where clarified. - Validate security: documented dual-auth matches enforced auth.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: rotation in open vs funded, blocked post-settlement, hold active, post-rotation withdraw target.
- Include full
cargo testoutput and a short security notes section in the PR.
docs: document beneficiary rotation dual-auth flow with anchoring test
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Extract the repeated legal-hold and terminal-status gate checks into shared guard helpers" labels: type:refactor, area:guards, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The legal-hold gate ensure(&env, !Self::legal_hold_active(&env), EscrowError::LegalHoldBlocks*) is repeated across fund_impl, settle, withdraw, claim_investor_payout, cancel_funding, rotate_beneficiary, and sweep_terminal_dust in escrow/src/lib.rs, each with a different error variant. Likewise the terminal-state check (status == 2 || status == 3 || status == 4) and the open-state check (status == 0) recur verbatim. The repeated, hand-written gates are error-prone β a future entrypoint can omit the hold check or mis-pick a status.
This issue extracts small named guard helpers parameterized by the error code, with no behavior change.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add private helpers, e.g.
guard_not_legal_hold(&env, err: EscrowError),is_terminal_status(status: u32) -> bool, andguard_status_eq(&env, escrow_status, expected, err). - Replace the inline checks at each call site, preserving the exact error variant and ADR-002 guard ordering (legal-hold/status checks before
require_authwhere they already are). - No new errors, no behavior change; this is a readability/safety refactor only.
- Fork the repo and create a branch
git checkout -b refactor/contracts-shared-gate-helpers- Implement changes
- Write code in:
escrow/src/lib.rsβ guard helpers and call-site replacement. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ assert each refactored entrypoint still emits the same legal-hold/status error as before. - Add documentation: note the helpers in ADR-002 and
docs/escrow-security-checklist.md. - Include NatSpec-style
///comments on the helpers. - Validate security: identical gate conditions and error codes at every site.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: legal-hold-blocked path per entrypoint, terminal vs non-terminal status, open-state guard.
- Include full
cargo testoutput and a short security notes section in the PR.
refactor: extract shared legal-hold and status-gate helpers with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Fix the duplicate EscrowError discriminant shared by FundingDeadlinePassed and NoPendingAdmin" labels: type:security, area:errors, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
In the EscrowError enum in escrow/src/lib.rs, two distinct variants are assigned the same numeric discriminant: FundingDeadlinePassed = 163 and NoPendingAdmin = 163. The contract's documented SDK contract is that callers "branch on the numeric code rather than legacy panic strings", so two unrelated failures (a funding window that has closed versus an accept_admin with no pending successor) are indistinguishable to clients β a real correctness and observability bug. Because the codes are meant to be append-only and stable, simply renumbering one in place would itself break the policy unless done carefully.
This issue assigns a unique, append-only code to one of the colliding variants and proves every error code is distinct.
- Repository scope: Liquifact/Liquifact-contracts only.
- Keep
FundingDeadlinePassedat its existing slot (it is referenced frominit) and moveNoPendingAdminto a fresh unused discriminant in the admin-handover range (e.g. the 80s block alongsideNewAdminSameAsCurrent = 80), or vice versa, choosing whichever minimizes churn against deployed instances. - Add a compile-time or test-time assertion that no two
EscrowErrorvariants share a discriminant. - Update
docs/escrow-error-messages.mdto reflect the corrected, collision-free table and note the historical collision in a migration note.
- Fork the repo and create a branch
git checkout -b security/contracts-dedupe-error-code-163- Implement changes
- Write code in:
escrow/src/lib.rsβ reassign the colliding discriminant. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ assertaccept_adminwith no pending admin and a deadline-passedfundraise distinct, correct codes; add a uniqueness test over all variants. - Add documentation: update
docs/escrow-error-messages.md. - Include NatSpec-style
///comments on the reassigned variant explaining the history. - Validate security: no two codes collide; client branching is unambiguous.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: deadline-passed funding, accept-admin with no proposal, full-enum uniqueness check.
- Include full
cargo testoutput and a short security notes section in the PR.
fix: resolve duplicate EscrowError discriminant 163 with uniqueness test and docs
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Convert revoke_attestation_digest panic strings to typed EscrowError codes" labels: type:security, area:errors, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
revoke_attestation_digest in escrow/src/lib.rs still validates with raw assert! panic strings β "attestation index out of range" and "attestation already revoked at index" β while every other attestation path (bind_primary_attestation_hash, append_attestation_digest) uses the append-only EscrowError enum with codes such as PrimaryAttestationAlreadyBound and AttestationAppendLogCapacityReached. This breaks the documented SDK contract that callers branch on numeric codes, leaving the revoke path inconsistent with its sibling entrypoints.
This issue replaces both asserts with typed errors.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add append-only
EscrowErrorvariants in the attestation range (alongside50/51), e.g.AttestationIndexOutOfRangeandAttestationAlreadyRevoked; never renumber existing codes. - Replace the two
assert!calls inrevoke_attestation_digestwithensure(&env, cond, EscrowError::...), preserving exact behavior, guard ordering, and theAttestationDigestRevokedevent. - Keep admin authorization first; no behavior change beyond the revert type.
- Fork the repo and create a branch
git checkout -b security/contracts-revoke-attestation-typed-errors- Implement changes
- Write code in:
escrow/src/lib.rsβ new error variants andensurecalls. - Write comprehensive tests in:
escrow/src/tests/attestations.rsβ assert each typed error viatry_revoke_attestation_digest(out-of-range index, double revoke), plus non-admin rejection. - Add documentation: update
docs/escrow-error-messages.mdanddocs/escrow-attestations.md. - Include NatSpec-style
///comments on the new variants and the entrypoint. - Validate security: identical revert conditions, stable numeric codes.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: index past log length, already-revoked index, valid revoke, non-admin caller.
- Include full
cargo testoutput and a short security notes section in the PR.
fix: replace revoke_attestation_digest panic strings with typed EscrowError codes and tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Disambiguate the al_set event symbol shared by single and batch allowlist writes" labels: type:enhancement, area:events, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Both set_investor_allowlisted and the batch set_investors_allowlisted in escrow/src/lib.rs publish the InvestorAllowlistChanged event with the identical symbol_short!("al_set") name. The batch path's documented invariant is that "the end state and emitted events are identical to calling set_investor_allowlisted individually", which is correct for per-investor accounting, but it leaves indexers unable to distinguish a single administrative change from a bulk operation, and provides no batch-level marker (size, common allowed flag) for audit trails.
This issue adds a dedicated batch-level event while preserving the per-investor events.
- Repository scope: Liquifact/Liquifact-contracts only.
- Keep emitting one
InvestorAllowlistChanged(al_set) per address from the batch path so the documented per-address invariant holds. - Add a single additional
InvestorAllowlistBatchApplied#[contractevent](distinct symbol, e.g.al_batch) emitted once perset_investors_allowlistedcall carryinginvoice_id, batch size, and the commonallowedflag. - Keep
set_investor_allowlistedunchanged; this is purely additive for indexers.
- Fork the repo and create a branch
git checkout -b feature/contracts-allowlist-batch-event- Implement changes
- Write code in:
escrow/src/lib.rsβ new batch event and emission point. - Write comprehensive tests in:
escrow/src/test_allowlist_tests.rsβ assert N per-investor events plus exactly one batch event with correct size/flag. - Add documentation: update
docs/EVENT_SCHEMA.mdanddocs/escrow-events.md. - Include NatSpec-style
///comments on the new event. - Validate security: per-address invariant unchanged; single batch event.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: single-element batch, max-size batch, allow vs disallow flag.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add distinct batch allowlist event topic alongside per-investor events with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add an inbound funding-token transfer helper to external_calls with balance-delta checks" labels: type:feature, area:token-safety, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
escrow/src/external_calls.rs exposes only transfer_funding_token_with_balance_checks, an outbound helper used by refund and sweep_terminal_dust to move tokens from the contract to a recipient with strict pre/post balance-delta conservation. There is no symmetric inbound helper that pulls tokens from an external payer into the contract while applying the same fee-on-transfer / rebasing / hook-token safe-fail invariants. Any future on-chain custody at fund (recording investor principal) must hand-roll the balance checks, duplicating subtle logic and risking divergence from the audited outbound path.
This issue adds a hardened inbound helper mirroring the outbound one.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
transfer_into_escrow_with_balance_checks(env, token, from, to_contract, amount)that records the contract's pre-balance, callstoken::Client::transfer, then asserts the recipient delta equalsamountand the sender delta is non-positive, reusing the existing typed errors (TransferAmountNotPositive,RecipientBalanceDeltaMismatch,SenderBalanceDeltaMismatch, underflow guards). - Do not wire it into
fundin this issue (custody activation is tracked separately); deliver the audited primitive and its tests so callers can adopt it safely. - Keep the outbound helper untouched; share constants/error codes where applicable.
- Fork the repo and create a branch
git checkout -b feature/contracts-inbound-transfer-helper- Implement changes
- Write code in:
escrow/src/external_calls.rsβ the inbound helper. - Write comprehensive tests in:
escrow/src/tests/external_calls_mocked.rsβ adversarial tokens (fee-on-transfer under-credit, rebasing over-credit, no-op), zero/negative amount, and a happy-path delta assertion. - Add documentation: update
docs/ESCROW_TOKEN_INTEGRATION_CHECKLIST.md. - Include NatSpec-style
///comments on the helper and its invariants. - Validate security: every non-compliant inbound path safe-fails; balance conservation holds.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: under-delivery, over-credit, no-op transfer, zero amount.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add inbound funding-token transfer helper with balance-delta checks and tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a paginated view enumerating revoked attestation indices" labels: type:feature, area:read-api, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Attestation revocation in escrow/src/lib.rs is stored per index under DataKey::AttestationRevoked(u32) and is only queryable one index at a time via is_attestation_revoked(index). There is no way to enumerate which entries in the bounded append-log (capped at MAX_ATTESTATION_APPEND_ENTRIES = 32) have been revoked β an indexer or auditor must probe all 32 slots individually, with no single authoritative on-chain answer.
This issue adds a read returning the set of revoked indices for the log.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
get_revoked_attestation_indices(env) -> Vec<u32>that scans0..get_attestation_append_log().len()and collects indices whereDataKey::AttestationRevoked(i)is set. - Pure read, no auth, no mutation; bounded by
MAX_ATTESTATION_APPEND_ENTRIES. - Document that indices align with
get_attestation_append_logordering and that legacy instances with no revocations return an emptyVec.
- Fork the repo and create a branch
git checkout -b feature/contracts-revoked-attestation-view- Implement changes
- Write code in:
escrow/src/lib.rsβget_revoked_attestation_indicesview. - Write comprehensive tests in:
escrow/src/tests/attestations.rsβ none revoked, some revoked, all revoked, ordering matches the log. - Add documentation: update
docs/escrow-attestations.mdanddocs/escrow-read-api.md. - Include NatSpec-style
///comments on the view. - Validate security: pure read, bounded scan, no mutation.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: empty log, partial revocation, full revocation.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add get_revoked_attestation_indices read view with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add an un-revoke entrypoint to reverse an erroneous attestation revocation" labels: type:feature, area:attestations, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
revoke_attestation_digest in escrow/src/lib.rs sets DataKey::AttestationRevoked(index) permanently, and there is no way to undo it. If an admin revokes the wrong index (a fat-finger on a 0-based index), the provenance entry is marked revoked forever even though the underlying digest was legitimate, polluting the audit chain that indexers surface.
This issue adds an admin-gated unrevoke_attestation_digest(index) that clears the flag and emits a dedicated event.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
unrevoke_attestation_digest(env, index: u32)gated by admin auth; assert the index is in range and currently revoked (append-only typed errors, reusing/extending the attestation error range), then removeDataKey::AttestationRevoked(index). - Emit a new
AttestationDigestUnrevoked#[contractevent]carryinginvoice_idandindex. - Keep ADR-002 guard ordering: range/state checks then admin
require_authconsistent with the existing revoke path.
- Fork the repo and create a branch
git checkout -b feature/contracts-unrevoke-attestation- Implement changes
- Write code in:
escrow/src/lib.rsβunrevoke_attestation_digest, event, errors. - Write comprehensive tests in:
escrow/src/tests/attestations.rsβ revoke then unrevoke restores state, unrevoke-without-revoke rejection, out-of-range rejection, non-admin rejection. - Add documentation: update
docs/escrow-attestations.md. - Include NatSpec-style
///comments on the entrypoint and event. - Validate security: admin-only, idempotency, bounded index.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: unrevoke of a non-revoked index, out-of-range index, double unrevoke, non-admin caller.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add admin unrevoke_attestation_digest entrypoint with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add an admin entrypoint to cancel a pending admin handover proposal" labels: type:feature, area:admin, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The two-step admin handover in escrow/src/lib.rs writes DataKey::PendingAdmin in propose_admin and only clears it when the successor calls accept_admin. There is no way for the current admin to retract a proposal once made β if the wrong successor was proposed, or the handover is abandoned, the pending key lingers and the proposed address can accept at any later time, which is a standing key-rotation risk.
This issue adds an admin-gated cancel_pending_admin() that clears the pending proposal.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
cancel_pending_admin(env)gated viaload_escrow_require_admin; require a pending admin to exist (reuseNoPendingAdmin), then removeDataKey::PendingAdmin. - Emit a new
AdminProposalCancelled#[contractevent]carryinginvoice_idand the cancelled pending address. - Keep
propose_admin/accept_adminsemantics unchanged; this only removes an unaccepted proposal.
- Fork the repo and create a branch
git checkout -b feature/contracts-cancel-pending-admin- Implement changes
- Write code in:
escrow/src/lib.rsβcancel_pending_admin, event. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ propose then cancel clearsget_pending_admin, accept-after-cancel fails, cancel-without-proposal rejection, non-admin rejection. - Add documentation: update
docs/OPERATOR_RUNBOOK.mdand the README entrypoint table. - Include NatSpec-style
///comments on the entrypoint and event. - Validate security: admin-only, proposal cannot be accepted after cancel.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: cancel with no proposal, cancel then re-propose, accept blocked after cancel, non-admin caller.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add cancel_pending_admin entrypoint to retract an unaccepted handover with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a settled-coupon read view exposing the total pool owed at settlement" labels: type:enhancement, area:read-api, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
compute_investor_payout in escrow/src/lib.rs derives a per-investor gross_payout from the FundingCloseSnapshot and the documented formula settle_pool = total_principal + coupon, but the aggregate settle_pool (the total amount the SME must repay to fully satisfy all investors) is never exposed as a view. SME repayment tooling and dashboards must re-derive total_principal Γ yield_bps / 10_000 off-chain, risking a rounding divergence from the on-chain math.
This issue adds an authoritative aggregate view of the settlement pool.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
get_settlement_pool(env) -> i128returningtotal_principal + floor(total_principal Γ yield_bps / 10_000)computed fromDataKey::FundingCloseSnapshotand the escrow's baseyield_bps, using the samechecked_*arithmetic andComputePayoutArithmeticOverflowguard ascompute_investor_payout. - Return
0when the snapshot is absent (escrow not yet funded), matchingcompute_investor_payoutsemantics. - Document that this uses the escrow base yield (tier-specific effective yields are per-investor and reflected only in
compute_investor_payout).
- Fork the repo and create a branch
git checkout -b feature/contracts-settlement-pool-view- Implement changes
- Write code in:
escrow/src/lib.rsβget_settlement_poolview reusing the coupon math. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ pool equals principal+coupon, zero before snapshot, rounding floor, overflow guard. - Add documentation: update
docs/escrow-pro-rata.mdanddocs/escrow-read-api.md. - Include NatSpec-style
///comments on the view. - Validate security: pure read, identical rounding to the payout formula, overflow-safe.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero yield, max yield, no snapshot, large principal near overflow.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add get_settlement_pool aggregate coupon view with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Emit a dedicated event when an admin handover is proposed via the deprecated transfer_admin shim" labels: type:enhancement, area:admin, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
transfer_admin in escrow/src/lib.rs is a #[deprecated] shim that silently delegates to propose_admin, so the only on-chain signal of a call is the generic AdminProposedEvent β indistinguishable from a direct propose_admin call. Operators migrating off the legacy one-step API have no way to detect that integrations are still calling the deprecated path, so they cannot drive the deprecation to completion.
This issue makes deprecated-shim usage observable without changing the handover behavior.
- Repository scope: Liquifact/Liquifact-contracts only.
- Keep
transfer_admindelegating topropose_admin(no behavior change to the two-step flow). - Emit an additional
DeprecatedTransferAdminUsed#[contractevent]carryinginvoice_idand the proposed address, so indexers can flag legacy callers. - Update the deprecation rustdoc to mention the observability event and the intended removal path.
- Fork the repo and create a branch
git checkout -b enhancement/contracts-deprecated-transfer-admin-event- Implement changes
- Write code in:
escrow/src/lib.rsβ emit the new event fromtransfer_admin. - Write comprehensive tests in:
escrow/src/tests/admin.rsβtransfer_adminemits both the proposal and the deprecation event;propose_adminemits only the proposal. - Add documentation: update
docs/EVENT_SCHEMA.mdanddocs/OPERATOR_RUNBOOK.md. - Include NatSpec-style
///comments on the new event. - Validate security: handover behavior unchanged; purely additive event.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: shim vs direct propose, same-address rejection still typed.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: emit deprecation event on transfer_admin shim usage with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a raise-only entrypoint to increase the unique-investor cap before funding closes" labels: type:feature, area:investor-caps, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
lower_max_unique_investors in escrow/src/lib.rs lets an admin only lower the MaxUniqueInvestorsCap while the escrow is open, with guards NewCapNotLower and NewCapBelowCurrentFunderCount. There is no symmetric way to raise the cap: if primary issuance attracts more demand than initially configured, the admin cannot widen participation without redeploying, even though the open state would otherwise permit it.
This issue adds a raise-only counterpart with parallel guards.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
raise_max_unique_investors(env, new_cap: u32)gated viaload_escrow_require_admin, allowed only whilestatus == 0, requiring an existing cap (NoInvestorCapConfigured) andnew_cap > old_cap(new append-only typed errorNewCapNotHigher). - Emit a new
MaxUniqueInvestorsCapRaised#[contractevent]carryinginvoice_id,old_cap,new_cap(parallel toMaxUniqueInvestorsCapLowered). - Preserve all funding-cap enforcement in
fund_impl; this only widens the ceiling pre-close.
- Fork the repo and create a branch
git checkout -b feature/contracts-raise-unique-cap- Implement changes
- Write code in:
escrow/src/lib.rsβraise_max_unique_investors, event, error. - Write comprehensive tests in:
escrow/src/tests/cap_validation.rsβ raise accepted, equal/lower rejected, no-cap rejection, post-close rejection, more funders allowed after raise. - Add documentation: update
docs/escrow-investor-caps.md. - Include NatSpec-style
///comments on the entrypoint, event, and error. - Validate security: admin-only, open-state-only, raise-only monotonicity.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: raise from existing cap, equal cap rejected, no configured cap, status != open.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add raise_max_unique_investors entrypoint mirroring the lower-only setter with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Allow an admin to update the funding deadline while the escrow is open" labels: type:feature, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The optional funding_deadline in escrow/src/lib.rs is validated and stored once at init (DataKey::FundingDeadline) and surfaced via get_funding_deadline/is_funding_expired, but it is write-once β there is no entrypoint to extend or set a deadline after deployment. An issuer who needs to extend a stalled raise, or who omitted a deadline at init, cannot adjust it without redeploying, unlike update_funding_target and update_maturity which are both adjustable while open.
This issue adds an admin-gated deadline update consistent with the other open-state setters.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
update_funding_deadline(env, new_deadline: Option<u64>)gated viaload_escrow_require_admin, allowed only whilestatus == 0;Some(d)requiresd > now(reuse theinitvalidation /FundingDeadlinePassed),Noneclears the deadline. - Emit a new
FundingDeadlineUpdated#[contractevent]carryinginvoice_id, prior, and new deadline. - Preserve
is_funding_expiredsemantics and the "no deadline" meaning of an absent key.
- Fork the repo and create a branch
git checkout -b feature/contracts-update-funding-deadline- Implement changes
- Write code in:
escrow/src/lib.rsβupdate_funding_deadline, event. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ set from none, extend, clear, past-deadline rejection, post-close rejection,is_funding_expiredreflects update (Ledger testutils). - Add documentation: update
docs/escrow-lifecycle.mdanddocs/escrow-ledger-time.md. - Include NatSpec-style
///comments on the entrypoint and event. - Validate security: admin-only, open-state-only, deadline must be in the future.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: no prior deadline, extend, clear to none, deadline in the past, status != open.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add admin update_funding_deadline entrypoint for the open window with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a deposit-preview view that simulates a fund call without mutating state" labels: type:feature, area:read-api, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
A client sizing a deposit against fund in escrow/src/lib.rs must replicate every fund_impl precondition off-chain β status open, not legal-held, not deadline-expired, allowlist gate, min-contribution floor, per-investor cap, and unique-funder cap β to know whether a given (investor, amount) will succeed. This logic is duplicated in every front-end and drifts from the contract as guards evolve.
This issue adds a pure preview view that runs the same checks read-only and reports the outcome.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
preview_fund(env, investor: Address, amount: i128) -> u32returning0for "would succeed" or the numericEscrowErrorcode thatfundwould raise first, evaluating the guards in the exact same order asfund_impl. - Pure read: no auth, no state mutation; must not call
require_auth. - Document that this is advisory β
fundremains the source of truth and can still revert under racing state changes.
- Fork the repo and create a branch
git checkout -b feature/contracts-preview-fund- Implement changes
- Write code in:
escrow/src/lib.rsβpreview_fundreusing the same guard predicates asfund_impl. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ each rejection reason returns its code, a valid deposit returns 0, ordering matchesfundfailures. - Add documentation: update
docs/escrow-read-api.md. - Include NatSpec-style
///comments on the view and the code mapping. - Validate security: pure read, no mutation, ordering matches enforcement.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: below floor, over per-investor cap, unique cap reached, not allowlisted, deadline passed, legal hold, closed status, valid deposit.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add preview_fund read view reporting the first fund guard failure with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Emit a settlement-coupon snapshot event carrying the computed pool at settle()" labels: type:enhancement, area:events, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
settle() in escrow/src/lib.rs flips status to 2 and emits EscrowSettled with funded_amount, yield_bps, and maturity, but it does not announce the computed settle_pool (principal plus coupon) that investors are collectively owed. Indexers must re-derive the coupon from funded_amount Γ yield_bps / 10_000, duplicating the on-chain rounding and risking divergence from compute_investor_payout.
This issue adds the realized pool to the settlement event additively.
- Repository scope: Liquifact/Liquifact-contracts only.
- Extend
EscrowSettled(append-only field) with the computedsettle_poolderived from theFundingCloseSnapshot.total_principaland baseyield_bps, using the samechecked_*arithmetic ascompute_investor_payout. - Keep existing topics and fields stable per the additive policy (ADR-007); compute the pool once during
settle. - If a
get_settlement_poolview exists, reuse its math to guarantee identical rounding.
- Fork the repo and create a branch
git checkout -b enhancement/contracts-settle-pool-event- Implement changes
- Write code in:
escrow/src/lib.rsβ extendEscrowSettledand populate it insettle. - Write comprehensive tests in:
escrow/src/tests/settlement.rsβ event pool equals principal+coupon, zero-yield case, rounding floor. - Add documentation: update
docs/EVENT_SCHEMA.mdanddocs/escrow-events.md. - Include NatSpec-style
///comments on the new field. - Validate security: additive field, identical rounding, overflow-safe.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero yield, max yield, no-maturity escrow, large principal.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add realized settlement pool to EscrowSettled event payload with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a view exposing the contract's live funding-token balance for reconciliation" labels: type:feature, area:read-api, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
sweep_terminal_dust and refund in escrow/src/lib.rs read the contract's funding-token balance via TokenClient::balance(this) to enforce the liability floor and move funds, but there is no public view that returns this balance. Auditors reconciling on-chain custody against funded_amount and distributed_principal must construct a token client call themselves and know the funding-token address, with no single contract-level answer.
This issue adds a read returning the contract's current funding-token balance.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
get_token_balance(env) -> i128reading the boundDataKey::FundingTokenand returningTokenClient::balance(env.current_contract_address()); raiseFundingTokenNotSetif uninitialized (matching the existing getter behavior). - Pure read, no auth, no mutation.
- Document the reconciliation relationship: balance versus
funded_amount - distributed_principalfor cancelled escrows.
- Fork the repo and create a branch
git checkout -b feature/contracts-token-balance-view- Implement changes
- Write code in:
escrow/src/lib.rsβget_token_balanceview. - Write comprehensive tests in:
escrow/src/tests/integration.rsβ register a SAC, mint to the contract, assert the view matches the token balance after refund/sweep. - Add documentation: update
docs/escrow-read-api.mdanddocs/adr/ADR-006-dust-sweep-and-token-safety.md. - Include NatSpec-style
///comments on the view. - Validate security: pure read, correct token resolution, no mutation.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero balance, post-mint balance, balance after a sweep, uninitialized escrow.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: add get_token_balance reconciliation view with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Validate the invoice amount against a maximum bound at init to prevent overflow-prone configs" labels: type:security, area:init-validation, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
init in escrow/src/lib.rs validates only amount > 0 and yield_bps in 0..=10_000, but places no upper bound on amount. Because compute_investor_payout later computes total_principal Γ yield_bps and contribution Γ settle_pool with i128 checked arithmetic, an extreme amount near i128::MAX makes settlement-time payout computation revert with ComputePayoutArithmeticOverflow for every investor β funds become un-claimable through the on-chain view, discovered only after the escrow is fully funded.
This issue rejects implausibly large amounts at init so overflow is impossible by construction.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a
MAX_INVOICE_AMOUNTconstant chosen soamount Γ 10_000andamount Γ settle_poolcannot overflowi128for any valid yield; rejectamount > MAX_INVOICE_AMOUNTatinitwith a new append-only typed error (e.g.AmountExceedsMax). - Document the bound's derivation relative to the
compute_investor_payoutformula. - Preserve all existing
initvalidation and ordering; this is an additional guard.
- Fork the repo and create a branch
git checkout -b security/contracts-init-amount-bound- Implement changes
- Write code in:
escrow/src/lib.rsβ constant, validation, error. - Write comprehensive tests in:
escrow/src/tests/init.rsβ accept at-bound amount, reject above-bound, and assert a near-bound funded escrow'scompute_investor_payoutnever overflows. - Add documentation: update
docs/escrow-numeric-model.md. - Include NatSpec-style
///comments on the constant and error. - Validate security: no overflow path reachable from any valid init.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: exactly at bound, one over bound, max yield with large amount.
- Include full
cargo testoutput and a short security notes section in the PR.
fix: bound init amount to prevent settlement payout overflow with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Bound the legal-hold clear delay at init to prevent an unclearable hold" labels: type:security, area:legal-hold, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
init in escrow/src/lib.rs accepts an optional legal_hold_clear_delay and stores it in DataKey::LegalHoldClearDelay when > 0, with no upper-bound validation. The clear flow (request_clear_legal_hold then set_legal_hold(false)) gates on now >= clearable_at, where clearable_at = now + delay and the addition is overflow-guarded by LegalHoldClearDelayOverflow. A mistaken delay just below the overflow threshold (e.g. decades) makes a placed legal hold effectively permanent β funds frozen with no realistic clear path short of an admin handover.
This issue adds a sane upper bound on the clear delay at init.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a
MAX_LEGAL_HOLD_CLEAR_DELAY_SECSconstant and rejectdelay > MAX_LEGAL_HOLD_CLEAR_DELAY_SECSatinitwith a new append-only typed error (e.g.LegalHoldClearDelayTooLarge). - Preserve the
delay == 0/ absent "immediate clear" semantics and the existing overflow guard. - Reference the ledger-time trust model and the legal-hold timing in the docs.
- Fork the repo and create a branch
git checkout -b security/contracts-clear-delay-bound- Implement changes
- Write code in:
escrow/src/lib.rsβ constant, validation, error. - Write comprehensive tests in:
escrow/src/tests/legal_hold.rsβ accept zero/in-window delay, reject above-bound delay. - Add documentation: update
docs/escrow-legal-hold.mdanddocs/adr/ADR-004-legal-hold.md. - Include NatSpec-style
///comments on the constant and error. - Validate security: no permanently-unclearable hold from a bad delay.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero delay, exactly at bound, above bound.
- Include full
cargo testoutput and a short security notes section in the PR.
fix: bound legal-hold clear delay at init to prevent an unclearable hold with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Reject fund_batch entries containing duplicate investor addresses" labels: type:security, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
fund_batch in escrow/src/lib.rs applies per-entry funding validation and is bounded by MAX_FUND_BATCH = 50, but it does not reject a batch that lists the same investor address twice. Two entries for one address are processed as sequential deposits, which can silently bypass a caller's intent (a single intended deposit applied twice), interact confusingly with the per-investor cap mid-batch, and complicate auditing the unique-funder count transition. Whether duplicates are valid is undocumented and untested.
This issue makes the duplicate-handling contract explicit and safe.
- Repository scope: Liquifact/Liquifact-contracts only.
- Reject any batch containing a repeated address with a new append-only typed error (e.g.
FundingBatchDuplicateInvestor) detected before any state mutation, so the batch is atomic and intent-preserving. - Keep
MAX_FUND_BATCHbounded so the duplicate scan stays within CPU limits. - Document that repeat deposits for one investor must be separate single
fundcalls, matching the tiered second-deposit discipline.
- Fork the repo and create a branch
git checkout -b security/contracts-fund-batch-dedupe- Implement changes
- Write code in:
escrow/src/lib.rsβ duplicate detection and error. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ duplicate batch rejected with no partial state, unique batch succeeds, boundary atMAX_FUND_BATCH. - Add documentation: update
docs/escrow-lifecycle.mdand the README entrypoint table. - Include NatSpec-style
///comments on the guard and error. - Validate security: atomic rejection, no partial-state corruption, bounded scan.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: adjacent duplicates, non-adjacent duplicates, all-unique batch, single-element batch.
- Include full
cargo testoutput and a short security notes section in the PR.
fix: reject duplicate investor addresses in fund_batch with tests
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward.