| type | Feature |
|---|---|
| title | Move investor principal on-chain during fund() via SEP-41 token transfers |
| labels | type:feature, area:funding, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN |
| assignees |
Today fund_impl in escrow/src/lib.rs records DataKey::InvestorContribution and increments funded_amount, but it never moves tokens β investor principal is only an accounting record while the bound DataKey::FundingToken sits unused for inflows. This means the contract's real token balance can diverge from funded_amount, and the refund/sweep_terminal_dust liability-floor math assumes funds are actually custodied on-chain.
This issue closes that gap: fund and fund_with_commitment must pull amount of the bound funding token from the investor into the contract atomically with the contribution write, so custody is real and balances reconcile.
- Repository scope: Liquifact/Liquifact-contracts only.
- In
fund_impl, afterinvestor.require_auth()and all validation, transferamountofDataKey::FundingTokenfrominvestortoenv.current_contract_address()using a new inbound helper inescrow/src/external_calls.rswith strict pre/post balance-delta checks (mirrortransfer_funding_token_with_balance_checks). - Preserve canonical guard ordering (ADR-002): read-only checks, then
require_auth, then storage writes and the token transfer last. - Keep
funded_amountand the contract's real token balance reconciled sorefundandsweep_terminal_dust'sfunded_amount - distributed_principalinvariant remains sound. - Add typed errors (append-only in
EscrowError) for inbound transfer failures; do not reuse numeric codes. - Bump persistent TTL for the investor's
DataKey::InvestorContributionentry since it now backs a real balance.
- Fork the repo and create a branch
git checkout -b feature/contracts-01-fund-onchain-custody- Implement changes
- Write code in:
escrow/src/lib.rsβ inbound transfer call insidefund_impl; newEscrowErrorvariants. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ register a Stellar Asset Contract, mint to investors, assert balance deltas, insufficient-balance failure, and reconciliation withfunded_amount. - Add documentation: update
README.mdanddocs/escrow-lifecycle.mdto describe real inbound custody. - Include NatSpec-style
///comments on the new helper and updatedfunddocs. - Validate security assumptions: no double-credit, auth on
investor, balance conservation, no fund lock-up.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero/negative amount, under-funded investor wallet, fee-on-transfer rejection, paused (legal hold), allowlist gate, cap violations.
- Include full
cargo testoutput and a short security notes section in the PR.
feat: custody investor principal on-chain in fund via SEP-41 transfers 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: "Disburse funded liquidity to the SME on withdraw() via real token transfer" labels: type:feature, area:settlement, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
withdraw() in escrow/src/lib.rs flips status to 3 and emits SmeWithdrew with funded_amount, but it is purely an accounting record β no funding token ever reaches escrow.sme_address. If fund custodies principal on-chain, the SME has no trustless way to actually pull it.
This issue makes withdraw move funded_amount of the bound DataKey::FundingToken from the contract to sme_address, atomically with the status transition, so SME disbursement is final and auditable.
- Repository scope: Liquifact/Liquifact-contracts only.
- After
escrow.sme_address.require_auth()and thestatus == 1check, transferfunded_amounttosme_addressviaexternal_calls::transfer_funding_token_with_balance_checksinescrow/src/external_calls.rs. - Preserve the legal-hold gate and forward-only status transition; keep
SmeWithdrewevent but extend its payload with the recipient. - Add a typed error path for insufficient contract balance (append-only
EscrowErrorcode). - Coordinate with the dust-sweep liability floor so withdrawn principal is consistent with
distributed_principalaccounting.
- Fork the repo and create a branch
git checkout -b feature/contracts-02-withdraw-onchain-disbursement- Implement changes
- Write code in:
escrow/src/lib.rsβ transfer call insidewithdraw. - Write comprehensive tests in:
escrow/src/tests/integration.rsβ SAC token, assert SME balance delta equalsfunded_amount, legal-hold block, wrong-status rejection. - Add documentation: update
docs/ESCROW_SME_WITHDRAWAL.MDandREADME.md. - Include NatSpec-style
///comments on the updatedwithdraw. - Validate security: auth, balance conservation, no double-withdraw.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: legal hold active, non-funded state, repeated withdraw, insufficient balance.
- Include full
cargo testoutput and a security notes section in the PR.
feat: disburse funded liquidity to SME on withdraw via token transfer 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: "Settle investor payouts on-chain so claim_investor_payout() transfers tokens" labels: type:feature, area:settlement, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
claim_investor_payout() in escrow/src/lib.rs only writes the DataKey::InvestorClaimed marker and emits InvestorPayoutClaimed β it never transfers the pro-rata payout that compute_investor_payout() already computes. Investors record a claim but receive nothing on-chain.
This issue wires compute_investor_payout into claim_investor_payout so a settled escrow pays the investor their gross payout in the bound funding token, atomically with marking the claim.
- Repository scope: Liquifact/Liquifact-contracts only.
- After the settled-status and
not_beforegates, compute the payout viaSelf::compute_investor_payout, then transfer it from the contract toinvestorusingescrow/src/external_calls.rs. - Keep the idempotency invariant: mark
InvestorClaimedbefore transfer, early-return on repeat claims, no double-pay. - Add typed errors (append-only) for a zero-computed payout or insufficient contract balance.
- Document the relationship between the dust-sweep liability floor, distributed payouts, and rounding residue.
- Fork the repo and create a branch
git checkout -b feature/contracts-03-claim-onchain-payout- Implement changes
- Write code in:
escrow/src/lib.rsβ transfer insideclaim_investor_payout. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ multi-investor pro-rata payouts, rounding residue, double-claim no-op, legal-hold block. - Add documentation: update
docs/escrow-pro-rata.mdandREADME.md. - Include NatSpec-style
///comments on the updated entrypoint. - Validate security: checks-effects-interactions ordering, no double-spend, balance conservation.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero contribution, unsettled escrow, locked claim, repeated claim, insufficient balance.
- Include full
cargo testoutput and a security notes section in the PR.
feat: pay investors on-chain in claim_investor_payout using pro-rata math 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 contract WASM upgrade entrypoint guarded by admin authorization" labels: type:feature, area:upgradeability, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The contract in escrow/src/lib.rs exposes migrate() for schema-version bookkeeping but has no way to replace the deployed WASM. The README upgrade policy says "redeploy required" for layout changes, which strands long-lived escrows whose admin, holds, and balances cannot be moved to a fixed binary.
This issue adds an upgrade(new_wasm_hash: BytesN<32>) entrypoint that calls env.deployer().update_current_contract_wasm(...) under current-admin authorization, enabling in-place code fixes while preserving stored state.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
upgrade(env, new_wasm_hash: BytesN<32>); requireSelf::get_escrow(env).admin.require_auth()before the deployer call (matches themigrateauth-first ordering). - Emit a new
ContractUpgraded#[contractevent]carryinginvoice_idand the new hash for indexers. - Document interaction with
SCHEMA_VERSION/DataKey::Versionand the additive-key policy (ADR-007). - Do not change existing stored layout; the entrypoint only swaps code.
- Fork the repo and create a branch
git checkout -b feature/contracts-04-wasm-upgrade-entrypoint- Implement changes
- Write code in:
escrow/src/lib.rsβupgradeentrypoint andContractUpgradedevent. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ register a second WASM, upgrade, assert state survives and unauthorized callers are rejected. - Add documentation: update
docs/OPERATOR_RUNBOOK.mdand the README upgrade policy. - Include NatSpec-style
///comments documenting admin gating and risks. - Validate security: only current admin can upgrade; no state wipe.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: unauthorized caller, upgrade then read preserved
DataKey::Escrow. - Include full
cargo testoutput and a security notes section in the PR.
feat: add admin-gated upgrade entrypoint for contract wasm replacement 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: "Batch fund recording to onboard multiple investor contributions in one call" labels: type:feature, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
fund in escrow/src/lib.rs records exactly one investor per call, while set_investors_allowlisted already demonstrates a bounded-batch pattern (capped at MAX_INVESTOR_ALLOWLIST_BATCH). High-volume primary issuance has no equivalent for funding, forcing one transaction per investor.
This issue adds fund_batch(entries: Vec<(Address, i128)>) that applies the same per-entry validation, caps, and accounting as fund, with a bounded batch size and per-entry authorization.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a
MAX_FUND_BATCHconstant mirroringMAX_INVESTOR_ALLOWLIST_BATCH; reject empty and oversized batches with typed errors (append-only). - Each entry must satisfy all existing
fund_implinvariants (per-investor cap, unique-investor cap, min-contribution floor, allowlist, status); require auth per investor address. - Emit one
EscrowFundedevent per entry, identical to singlefundsemantics. - Ensure the funded-target snapshot transition fires correctly mid-batch.
- Fork the repo and create a branch
git checkout -b feature/contracts-05-fund-batch- Implement changes
- Write code in:
escrow/src/lib.rsβfund_batchandMAX_FUND_BATCH. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ batch equals N single funds, cap rejection, mid-batch funded transition. - Add documentation: update
README.mdentrypoint table anddocs/escrow-lifecycle.md. - Include NatSpec-style
///comments. - Validate security: bounded CPU/storage, per-investor auth, no partial-state corruption.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: empty batch, oversized batch, duplicate addresses, cap boundary.
- Include full
cargo testoutput and a security notes section in the PR.
feat: add bounded fund_batch entrypoint for multi-investor funding 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 maturity-based settlement readiness view and settlement timestamp event" labels: type:enhancement, area:settlement, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
settle() in escrow/src/lib.rs enforces now >= maturity but emits only EscrowSettled with no signal about when an escrow became settleable, and there is no read-only entrypoint to ask "is this escrow settleable now?". Indexers and SME tooling must re-derive the maturity comparison off-chain.
This issue adds a is_settleable(env) -> bool view and a settled_at_ledger_timestamp field on the settlement path so the maturity window is observable on-chain.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
is_settleable(env) -> boolreturningstatus == 1 && (maturity == 0 || now >= maturity) && !legal_hold. - Extend
EscrowSettled(append-only field) with the ledger timestamp at settlement; keep existing topics stable. - Reuse
Env::ledger().timestamp()semantics fromdocs/escrow-ledger-time.md.
- Fork the repo and create a branch
git checkout -b feature/contracts-06-settlement-readiness- Implement changes
- Write code in:
escrow/src/lib.rsβis_settleableview, extended event. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ readiness across status/maturity/hold combinations, event field assertion. - Add documentation: update
docs/EVENT_SCHEMA.mdanddocs/escrow-events.md. - Include NatSpec-style
///comments. - Validate security: pure read, no auth, no state change.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: no-maturity escrow, pre-maturity, post-maturity, hold active.
- Include full
cargo testoutput and a security notes section in the PR.
feat: add is_settleable view and settlement timestamp event field 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 investors to revoke a contribution while the escrow is still open" labels: type:feature, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Once an investor calls fund in escrow/src/lib.rs, there is no way to back out before the escrow is funded β refund only works in the cancelled state (status 4). An investor who funds and then changes their mind while the invoice is still open (status 0) is stuck.
This issue adds unfund(investor, amount) allowing partial or full contribution withdrawal while status == 0, decrementing funded_amount, InvestorContribution, and (when zeroed) UniqueFunderCount.
- Repository scope: Liquifact/Liquifact-contracts only.
- Allow only while
status == 0; requireinvestor.require_auth(); reject when a legal hold is active. - Decrement
InvestorContribution,funded_amount, and decrementUniqueFunderCountif contribution reaches zero; never underflow (usechecked_sub). - If on-chain custody is enabled, return tokens via
escrow/src/external_calls.rs; otherwise update accounting only and document the dependency. - Add typed errors (append-only) for over-withdrawal and wrong status.
- Fork the repo and create a branch
git checkout -b feature/contracts-07-investor-unfund- Implement changes
- Write code in:
escrow/src/lib.rsβunfundentrypoint andEscrowUnfundedevent. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ partial/full unfund, funder-count decrement, status guard. - Add documentation: update
docs/escrow-lifecycle.md. - Include NatSpec-style
///comments. - Validate security: no underflow, no unfund after funded, auth.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: over-withdrawal, full exit resets funder count, hold active, funded state rejection.
- Include full
cargo testoutput and a security notes section in the PR.
feat: add investor unfund entrypoint for open-state contribution withdrawal 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 paginated read API for enumerating investor positions" labels: type:feature, area:read-api, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Per-investor data in escrow/src/lib.rs lives under address-keyed persistent entries (DataKey::InvestorContribution(Address)), which are not enumerable β get_contribution requires the caller to already know each address. Indexers and dashboards cannot list all funders from the contract, and UniqueFunderCount gives only a count.
This issue records funder addresses in a bounded append-only DataKey::InvestorIndex vector at first deposit and adds a paginated get_investors(start, limit) read.
- Repository scope: Liquifact/Liquifact-contracts only.
- On first deposit (
prev == 0) infund_impl, append the investor to a boundedDataKey::InvestorIndex, consistent withMaxUniqueInvestorsCapandUniqueFunderCount. - Add
get_investors(env, start: u32, limit: u32) -> Vec<Address>with a boundedlimit; pure read, no auth. - Document the additive-key compatibility (ADR-007): legacy instances return an empty index.
- Fork the repo and create a branch
git checkout -b feature/contracts-08-investor-index- Implement changes
- Write code in:
escrow/src/lib.rsβDataKey::InvestorIndex, append infund_impl,get_investorsview. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ index population, pagination bounds, no duplicate on repeat fund. - Add documentation: update
docs/escrow-read-api.mdanddocs/escrow-data-model.md. - Include NatSpec-style
///comments. - Validate security: bounded growth, no duplicate entries.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: empty index, single funder, pagination past end, repeated deposits.
- Include full
cargo testoutput and a security notes section in the PR.
feat: add bounded investor index with paginated get_investors read api 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: "Emit a structured lifecycle event when the escrow first reaches the funded state" labels: type:enhancement, area:events, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
In fund_impl (escrow/src/lib.rs) the escrow silently flips status from 0 to 1 and writes DataKey::FundingCloseSnapshot, but the only signal is the generic EscrowFunded event with status: 1 β indexers must diff sequential events to detect the moment of close. The pro-rata denominator is captured here yet never announced as a distinct event.
This issue emits a dedicated FundingClosed event carrying the snapshot fields exactly once at the transition.
- Repository scope: Liquifact/Liquifact-contracts only.
- Define a
FundingClosed#[contractevent]withinvoice_id,total_principal,funding_target,closed_at_ledger_timestamp,closed_at_ledger_sequence. - Emit it only inside the
status == 0 && funded_amount >= funding_targetbranch, alongside the snapshot write, and also inpartial_settlewhere the snapshot is written. - Keep
EscrowFundedunchanged; this is additive.
- Fork the repo and create a branch
git checkout -b feature/contracts-09-funding-closed-event- Implement changes
- Write code in:
escrow/src/lib.rsβFundingClosedevent and emission points. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ event emitted exactly once, fields match snapshot, also onpartial_settle. - Add documentation: update
docs/EVENT_SCHEMA.mdanddocs/escrow-events.md. - Include NatSpec-style
///comments. - Validate security: single emission, no over-funding double-fire.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: exact-target close, over-funding close, partial-settle close.
- Include full
cargo testoutput and a security notes section in the PR.
feat: emit dedicated FundingClosed event at open-to-funded transition 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: "Support a configurable protocol fee deducted at SME withdrawal" labels: type:feature, area:settlement, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The escrow in escrow/src/lib.rs routes all funded principal to the SME at withdrawal with no protocol revenue mechanism, even though a Treasury address is already bound at init. There is no way for LiquiFact to capture a basis-point fee on disbursed liquidity.
This issue adds an immutable protocol_fee_bps configured at init that, on withdrawal, splits funded_amount into an SME payout and a treasury fee.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add an optional
protocol_fee_bps: Option<i64>parameter toinit, validated to0..=10_000, stored under a newDataKey::ProtocolFeeBps(default 0). - In
withdraw, computefee = funded_amount * fee_bps / 10_000(floor, checked) and route it toDataKey::Treasurywith the remainder tosme_address. - Extend
SmeWithdrew(append-only) with the fee amount; add typed overflow errors. - This depends on on-chain disbursement; document the interaction.
- Fork the repo and create a branch
git checkout -b feature/contracts-10-protocol-fee- Implement changes
- Write code in:
escrow/src/lib.rsβ fee storage, split logic inwithdraw. - Write comprehensive tests in:
escrow/src/tests/integration.rsβ fee math, zero-fee default, rounding, treasury delta. - Add documentation: update
docs/escrow-numeric-model.mdandREADME.md. - Include NatSpec-style
///comments. - Validate security: overflow safety, fee bounds, conservation
sme + fee == funded_amount.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: 0 bps, max bps, rounding residue, large
funded_amount. - Include full
cargo testoutput and a security notes section in the PR.
feat: add immutable protocol fee split on SME withdrawal 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 a batch investor payout claim entrypoint for settled escrows" labels: type:feature, area:settlement, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
claim_investor_payout in escrow/src/lib.rs processes one investor per transaction. For a settled escrow with many funders, distributing payouts requires one transaction per address, which is operationally heavy for a relayer or keeper.
This issue adds a bounded claim_payouts_batch(investors: Vec<Address>) that applies identical per-investor gates and idempotency, mirroring the set_investors_allowlisted batch pattern.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a
MAX_CLAIM_BATCHconstant; reject empty/oversized batches with typed errors. - Each entry must pass the settled-status gate,
not_beforelock, idempotency check, and (if payouts are on-chain) the transfer; require auth per investor. - Skip already-claimed entries without failing the whole batch; emit one
InvestorPayoutClaimedper newly-claimed investor.
- Fork the repo and create a branch
git checkout -b feature/contracts-11-claim-batch- Implement changes
- Write code in:
escrow/src/lib.rsβclaim_payouts_batchandMAX_CLAIM_BATCH. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ batch equals N single claims, skip-claimed, cap rejection. - Add documentation: update
README.mdentrypoint table. - Include NatSpec-style
///comments. - Validate security: per-investor auth, idempotency, bounded work.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: empty batch, oversized, mixed claimed/unclaimed, locked claims.
- Include full
cargo testoutput and a security notes section in the PR.
feat: add bounded batch investor payout claim 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 a global pause switch independent of the compliance legal hold" labels: type:feature, area:admin, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The only circuit breaker in escrow/src/lib.rs is DataKey::LegalHold, which carries compliance semantics and a two-phase clear delay. There is no lightweight operational pause for incident response (e.g. a suspected token bug) that an admin can toggle without the legal-hold ceremony.
This issue adds an admin-controlled DataKey::Paused flag that gates fund, settle, withdraw, and claim_investor_payout, orthogonal to legal hold.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add
set_paused(active: bool)(admin auth) andis_paused()view; add aPausedChangedevent. - Add the pause gate to risk-bearing entrypoints as a read-only precondition before
require_auth, consistent with ADR-002 ordering. - Add typed errors (append-only) for each paused entrypoint; keep legal-hold logic untouched.
- Fork the repo and create a branch
git checkout -b feature/contracts-12-operational-pause- Implement changes
- Write code in:
escrow/src/lib.rsβ pause flag, gate, events, errors. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ pause blocks each gated entrypoint, unpause restores, independence from legal hold. - Add documentation: update
docs/escrow-security-checklist.mdandREADME.md. - Include NatSpec-style
///comments. - Validate security: admin-only toggle, gate ordering before auth.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: paused + legal hold both active, unauthorized toggle, each gated entrypoint.
- Include full
cargo testoutput and a security notes section in the PR.
feat: add admin operational pause switch independent of legal 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: "Convert panic-string guards in partial_settle to typed EscrowError codes" labels: type:security, area:errors, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
partial_settle in escrow/src/lib.rs uses raw assert! with panic strings ("Legal hold blocks partial settlement", "Unauthorized caller for partial settlement", "Escrow must be in Open state for partial settlement") instead of the project's append-only EscrowError enum. This breaks the documented client-SDK contract that callers "branch on the numeric code rather than legacy panic strings".
This issue replaces those asserts with typed errors so partial_settle matches every other entrypoint.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add append-only
EscrowErrorvariants (e.g.LegalHoldBlocksPartialSettle,UnauthorizedPartialSettle,PartialSettleNotOpen); never renumber existing codes. - Replace each
assert!inpartial_settlewithensure(&env, cond, EscrowError::...). - Preserve guard ordering and the
EscrowPartialSettleevent; no behavior change beyond error type.
- Fork the repo and create a branch
git checkout -b security/contracts-13-partial-settle-typed-errors- Implement changes
- Write code in:
escrow/src/lib.rsβ new error variants andensurecalls. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ assert each typed error viatry_partial_settle. - Add documentation: update
docs/escrow-error-messages.md. - Include NatSpec-style
///comments on the new error variants. - 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: hold active, wrong caller, non-open status.
- Include full
cargo testoutput and a security notes section in the PR.
fix: replace partial_settle 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: "Replace panic-string asserts in admin and allowlist batch paths with typed errors" labels: type:security, area:errors, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Several admin paths in escrow/src/lib.rs still panic with raw strings: accept_admin panics "No pending admin", lower_max_unique_investors uses four assert!/panic! messages, and set_investors_allowlisted asserts on batch bounds. This is inconsistent with the contract's append-only EscrowError discipline and forces SDKs to parse strings.
This issue migrates all of these to typed errors while preserving exact semantics.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add append-only
EscrowErrorvariants (e.g.NoPendingAdmin; reuseCapLowerNotOpen/NewCapNotLower/NewCapBelowCurrentFunderCountwhere already defined, otherwise add allowlistBatchEmpty/BatchTooLargeanalogues) and never renumber. - Replace asserts in
accept_admin,lower_max_unique_investors, andset_investors_allowlistedwithensure(...). - Keep return values, events, and ordering identical.
- Fork the repo and create a branch
git checkout -b security/contracts-14-admin-typed-errors- Implement changes
- Write code in:
escrow/src/lib.rsβ error variants andensurecalls. - Write comprehensive tests in:
escrow/src/test_allowlist_tests.rsandescrow/src/tests/admin.rsβ assert typed errors for each path. - Add documentation: update
docs/escrow-error-messages.md. - Include NatSpec-style
///comments on the new error variants. - Validate security: stable codes, identical revert conditions.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: accept with no pending admin, cap not open, cap not lower, batch empty/too-large.
- Include full
cargo testoutput and a security notes section in the PR.
fix: convert admin and allowlist batch panics to typed EscrowError codes 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: "Bump persistent TTL for per-investor keys inside fund and claim flows" labels: type:security, area:storage-ttl, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
fund_impl and claim_investor_payout in escrow/src/lib.rs write per-investor persistent keys (InvestorContribution, InvestorEffectiveYield, InvestorClaimNotBefore, InvestorClaimed) but rely entirely on the permissionless bump_ttl entrypoint to keep them alive. For a long-dated escrow, an investor's contribution entry can be archived before settlement, defaulting reads to zero and silently erasing their position.
This issue extends persistent TTL at write time inside the funding and claim paths using the existing PERSISTENT_TTL_MIN_EXTENSION_LEDGERS horizon.
- Repository scope: Liquifact/Liquifact-contracts only.
- In the per-investor setters in
fund_impl(and the claim marker write), callenv.storage().persistent().extend_ttl(...)for the keys just written. - Use the documented
PERSISTENT_TTL_MIN_EXTENSION_LEDGERSconstant; do not shorten any TTL (extend is monotonic). - Keep
bump_ttlas the permissionless top-up; this adds defense-in-depth at write time.
- Fork the repo and create a branch
git checkout -b security/contracts-15-per-investor-ttl-bump- Implement changes
- Write code in:
escrow/src/lib.rsβextend_ttlcalls in the persistent setters / fund path. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ assert entries persist past the prior horizon usingLedgertestutils. - Add documentation: update
docs/escrow-gas-storage-notes.mdand ADR-007. - Include NatSpec-style
///comments. - Validate security: no premature archival of live positions.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: repeat deposits re-bump, claim marker bump, long-dated maturity.
- Include full
cargo testoutput and a security notes section in the PR.
fix: extend per-investor persistent TTL at write time in fund and claim flows 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: "Enforce maturity bounds at init so settlement cannot be locked forever" labels: type:security, area:init-validation, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
init and update_maturity in escrow/src/lib.rs accept any u64 maturity with no upper-bound or relative-to-now validation. A mistaken value (e.g. far beyond any plausible ledger time) silently locks settle() and the claim path forever, since both gate on now >= maturity with no escape other than legal-hold gymnastics.
This issue adds bounded validation: maturity must be either 0 (no lock) or within a sane window relative to Env::ledger().timestamp().
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a
MAX_MATURITY_HORIZON_SECSconstant and rejectmaturity > now + horizonwith a new typed error (append-only). - Apply the same validation in
initandupdate_maturity; preserve thematurity == 0"no lock" semantics. - Reference the ledger-time trust model in
docs/escrow-ledger-time.md.
- Fork the repo and create a branch
git checkout -b security/contracts-16-maturity-bounds- Implement changes
- Write code in:
escrow/src/lib.rsβ constant, validation, error. - Write comprehensive tests in:
escrow/src/tests/init.rsβ accept zero/in-window, reject far-future. - Add documentation: update
docs/escrow-ledger-time.md. - Include NatSpec-style
///comments. - Validate security: no permanent settlement lock from bad input.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: maturity 0, exactly at horizon, beyond horizon, update path.
- Include full
cargo testoutput and a security notes section in the PR.
fix: bound maturity timestamps at init and update_maturity to prevent settlement lock 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 authorization audit test matrix for every role-gated entrypoint" labels: type:security, area:auth, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
ADR-002 in docs/adr/ADR-002-auth-boundaries.md defines per-role auth (admin, SME, investor, treasury), and each entrypoint in escrow/src/lib.rs calls require_auth() for one bound role. There is no single test matrix that asserts each entrypoint rejects the wrong signer and that no entrypoint silently mutates state before require_auth.
This issue adds an exhaustive negative-authorization test matrix.
- Repository scope: Liquifact/Liquifact-contracts only.
- For each mutating entrypoint (
init,fund,settle,withdraw,claim_investor_payout,set_legal_hold,sweep_terminal_dust,propose_admin,accept_admin,cancel_funding,refund, allowlist setters,update_*, attestation writes), assert the call fails without the correct signer usingenv.mock_authstoggling. - Assert the read-only preconditions and ordering from the module rustdoc "Authorization guard ordering".
- No production code change unless a missing guard is discovered (then file/fix separately).
- Fork the repo and create a branch
git checkout -b security/contracts-17-auth-matrix- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a guard gap is found. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ the full negative-auth matrix. - Add documentation: cross-link results in
docs/escrow-security-checklist.md. - Include NatSpec-style
///comments on test helpers. - Validate security: every role boundary is covered.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: wrong signer per entrypoint, no signer, treasury vs admin on sweep.
- Include full
cargo testoutput and a security notes section in the PR.
test: add exhaustive negative-authorization matrix for all role-gated entrypoints
- 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: "Repair and re-enable the disabled settlement test module" labels: type:test, area:settlement, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The settlement test module is disabled in escrow/src/tests.rs: // mod settlement; is commented out with a note that escrow/src/tests/settlement.rs "has interleaved fragments left behind by overlapping PR merges (#290..#301) that produced six unbalanced brace points." The most critical state transitions (settle, withdraw, claims, maturity boundaries, dust sweep) therefore have no compiled coverage.
This issue repairs the broken fragments and re-enables the module.
- Repository scope: Liquifact/Liquifact-contracts only.
- Fix the unbalanced braces and interleaved fragments in
escrow/src/tests/settlement.rsso it compiles cleanly. - Re-enable
mod settlement;inescrow/src/tests.rs. - Cover
settle(status/maturity/hold),withdraw,claim_investor_payout(lock + idempotency), andsweep_terminal_dustterminal-state and liability-floor cases.
- Fork the repo and create a branch
git checkout -b test/contracts-18-reenable-settlement-tests- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a real bug surfaces while fixing tests. - Write comprehensive tests in:
escrow/src/tests/settlement.rsβ repaired and expanded suite. - Add documentation: update the test-organization table in
README.md. - Include NatSpec-style
///comments on shared helpers. - Validate security: settlement invariants are actually exercised.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: pre/at/post maturity, hold-blocked settle, double withdraw, dust floor.
- Include full
cargo testoutput and a security notes section in the PR.
test: repair and re-enable settlement test module with expanded coverage
- 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 property-based invariants for funding accounting and unique-funder count" labels: type:test, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
escrow/src/tests/properties.rs already hosts proptest-based invariants, but the core funding accounting in fund_impl (escrow/src/lib.rs) β funded_amount == sum of contributions, UniqueFunderCount == distinct funders, and cap monotonicity β lacks randomized coverage.
This issue adds property tests over random funding sequences to assert these invariants hold for all orderings.
- Repository scope: Liquifact/Liquifact-contracts only.
- Generate randomized sequences of
fund/fund_with_commitmentacross multiple investors and amounts. - Assert: sum of
get_contributionequalsfunded_amount;get_unique_funder_countequals distinct funders; per-investor and unique caps are never exceeded; status flips to 1 exactly whenfunded_amount >= funding_target. - Persist any discovered counterexamples to
escrow/proptest-regressions/test.txt.
- Fork the repo and create a branch
git checkout -b test/contracts-19-funding-properties- Implement changes
- Write code in:
escrow/src/lib.rsβ only if an invariant violation is found. - Write comprehensive tests in:
escrow/src/tests/properties.rsβ new proptest cases. - Add documentation: note invariants in
docs/escrow-numeric-model.md. - Include NatSpec-style
///comments on generators. - Validate security: accounting conservation under all orderings.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: cap boundaries, overflow inputs, repeated funders.
- Include full
cargo testoutput and a security notes section in the PR.
test: add proptest invariants for funding accounting and unique funder count
- 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 proptest coverage for compute_investor_payout pro-rata rounding" labels: type:test, area:settlement, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
compute_investor_payout in escrow/src/lib.rs implements floor integer division for coupon and pro-rata share, documenting the invariant that the sum over all investors is <= total_principal + coupon with residue swept as dust. That conservation invariant is not exercised by randomized tests.
This issue adds property tests asserting the documented rounding and conservation bounds.
- Repository scope: Liquifact/Liquifact-contracts only.
- Generate random investor sets, contributions, and yield bps; fund to close; assert
sum(compute_investor_payout) <= total_principal + couponand that residue is non-negative. - Assert non-participants return 0 and overflow inputs raise
ComputePayoutArithmeticOverflow. - Reference the formula in
docs/escrow-pro-rata.md.
- Fork the repo and create a branch
git checkout -b test/contracts-20-payout-properties- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a rounding bug is found. - Write comprehensive tests in:
escrow/src/tests/properties.rsβ payout conservation properties. - Add documentation: clarify rounding edge cases in
docs/escrow-pro-rata.md. - Include NatSpec-style
///comments. - Validate security: no over-distribution beyond pool.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: single investor, equal splits, prime denominators, zero yield, max yield.
- Include full
cargo testoutput and a security notes section in the PR.
test: add proptest conservation and rounding invariants for compute_investor_payout
- 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 end-to-end lifecycle tests for the cancel and refund path" labels: type:test, area:funding, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
cancel_funding and refund in escrow/src/lib.rs implement the cancelled-escrow recovery path, with refund tracking DataKey::DistributedPrincipal that sweep_terminal_dust uses to enforce the liability floor. This multi-step interaction needs dedicated end-to-end coverage to prove the floor holds after partial refunds.
This issue adds lifecycle tests covering fund, cancel, refund, sweep with the liability invariant.
- Repository scope: Liquifact/Liquifact-contracts only.
- Set up a SAC token, fund multiple investors, cancel, refund a subset, then attempt
sweep_terminal_dustand assert it respectsbalance - sweep >= funded_amount - distributed_principal. - Assert double-refund fails with
NoContributionToRefund,is_investor_refundedflips, andDistributedPrincipalaccumulates. - Reference
docs/adr/ADR-006-dust-sweep-and-token-safety.md.
- Fork the repo and create a branch
git checkout -b test/contracts-21-cancel-refund-lifecycle- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a bug surfaces. - Write comprehensive tests in:
escrow/src/tests/integration.rsβ full cancel/refund/sweep lifecycle. - Add documentation: cross-link scenarios in
docs/escrow-lifecycle.md. - Include NatSpec-style
///comments. - Validate security: liability floor never violated after partial refunds.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: partial refunds, full refunds, sweep attempt before/after refunds, legal hold.
- Include full
cargo testoutput and a security notes section in the PR.
test: add cancel_funding to refund lifecycle tests with liability floor assertions
- 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 legal-hold two-phase clear timing and recovery tests" labels: type:test, area:legal-hold, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The legal-hold clear flow in escrow/src/lib.rs spans request_clear_legal_hold, the LegalHoldClearDelay, and set_legal_hold(false) with typed errors LegalHoldClearRequestMissing and LegalHoldClearNotReady. The documented recovery lever (propose_admin, accept_admin, clear_legal_hold) is a critical funds-safety path that needs explicit timing coverage in escrow/src/tests/legal_hold.rs.
This issue adds tests for the delay window and the admin-handover recovery scenario.
- Repository scope: Liquifact/Liquifact-contracts only.
- Assert clearing without a prior request fails; clearing before
clearable_atfails; clearing at/after succeeds. - Assert the recovery path: hold active, propose+accept new admin, new admin clears the hold.
- Assert holds block
settle,withdraw, andclaim_investor_payoutwhile active. - Reference
docs/escrow-legal-hold.mdand ADR-004.
- Fork the repo and create a branch
git checkout -b test/contracts-22-legal-hold-timing- Implement changes
- Write code in:
escrow/src/lib.rsβ only if a bug surfaces. - Write comprehensive tests in:
escrow/src/tests/legal_hold.rsβ timing and recovery cases. - Add documentation: clarify timing examples in
docs/escrow-legal-hold.md. - Include NatSpec-style
///comments. - Validate security: clear delay cannot be bypassed; recovery requires admin authority.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: zero delay, nonzero delay, missing request, admin handover mid-hold.
- Include full
cargo testoutput and a security notes section in the PR.
test: add legal-hold two-phase clear timing and admin recovery 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 SEP-41 token-safety tests for the external_calls balance-delta wrapper" labels: type:test, area:token-safety, stack:soroban, stack:rust, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
transfer_funding_token_with_balance_checks in escrow/src/external_calls.rs enforces strict pre/post balance conservation to safe-fail on fee-on-transfer, rebasing, and hook tokens. The existing mocked tests in escrow/src/tests/external_calls_mocked.rs should be extended to prove each delta-mismatch error path triggers.
This issue adds adversarial-token tests for every typed error the wrapper can emit.
- Repository scope: Liquifact/Liquifact-contracts only.
- Build mock tokens that under-deliver (fee-on-transfer), over-credit (rebasing), and leave balances unchanged; assert
SenderBalanceDeltaMismatch/RecipientBalanceDeltaMismatch. - Assert
TransferAmountNotPositiveandInsufficientTokenBalanceBeforeTransferpaths. - Keep production code unchanged unless a real gap is found.
- Fork the repo and create a branch
git checkout -b test/contracts-23-token-safety-wrapper- Implement changes
- Write code in:
escrow/src/external_calls.rsβ only if a gap surfaces. - Write comprehensive tests in:
escrow/src/tests/external_calls_mocked.rsβ adversarial token scenarios. - Add documentation: update
docs/ESCROW_TOKEN_INTEGRATION_CHECKLIST.md. - Include NatSpec-style
///comments on the mock tokens. - Validate security: every non-compliant token path safe-fails.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: fee-on-transfer, rebasing, no-op transfer, zero amount, insufficient balance.
- Include full
cargo testoutput and a security notes section in the PR.
test: add adversarial SEP-41 token-safety tests for external_calls balance-delta wrapper
- 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 complete escrow state machine including the cancelled branch" labels: type:docs, area:state-machine, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
InvoiceEscrow::status in escrow/src/lib.rs spans 0=open, 1=funded, 2=settled, 3=withdrawn, 4=cancelled, but the README entrypoint table and ADR-001 describe statuses 0β3 and omit the cancelled (4) branch added with cancel_funding/refund. The state machine doc is out of date relative to the code.
This issue updates the state-machine documentation to cover all five states, every transition, and each guard.
- Repository scope: Liquifact/Liquifact-contracts only.
- Document each transition (entrypoint, required role, legal-hold gate, status precondition) for
fund,partial_settle,settle,withdraw,cancel_funding,refund. - Update
docs/STATE_MACHINE_IMPLEMENTATION.mdand reconcile ADR-001 to include status 4. - Add a Mermaid diagram; ensure the README entrypoint table is consistent.
- Fork the repo and create a branch
git checkout -b docs/contracts-24-state-machine- Implement changes
- Write code in:
escrow/src/lib.rsβ only doc-comment corrections if the inlinestatuscomment is stale. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ a test asserting illegal transitions are rejected, anchoring the docs. - Add documentation:
docs/STATE_MACHINE_IMPLEMENTATION.md, ADR-001,README.md. - Include NatSpec-style
///comments where the inline status doc is updated. - Validate security: documented guards match code.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: each illegal transition rejected (e.g. settle from open, refund from funded).
- Include full
cargo testoutput and a security notes section in the PR.
docs: document full escrow state machine including cancelled branch 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: "Reconcile the events catalog with all emitted contractevents" labels: type:docs, area:events, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
escrow/src/lib.rs defines many #[contractevent] types (EscrowInitialized, EscrowFunded, EscrowPartialSettle, EscrowSettled, SmeWithdrew, InvestorPayoutClaimed, FundingCancelled, InvestorRefundedEvt, TreasuryDustSwept, attestation and allowlist events, etc.), but docs/EVENT_SCHEMA.md and docs/escrow-events.md may not list every event, its #[topic] fields, and the symbol_short! name emitted.
This issue produces a complete, code-accurate events catalog.
- Repository scope: Liquifact/Liquifact-contracts only.
- Enumerate every
#[contractevent]inlib.rs: event struct, emittednamesymbol, topic fields, and payload fields. - Note which entrypoint emits each event and under what status transition.
- Keep
docs/escrow-indexer.mdconsistent with the catalog.
- Fork the repo and create a branch
git checkout -b docs/contracts-25-events-catalog- Implement changes
- Write code in:
escrow/src/lib.rsβ only doc-comment fixes if an event's rustdoc is wrong. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ assert emitted event names/topics match the documented catalog. - Add documentation:
docs/EVENT_SCHEMA.md,docs/escrow-events.md,docs/escrow-indexer.md. - Include NatSpec-style
///comments where event rustdoc is corrected. - Validate security: documented topics match emitted topics.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: events with
Optionfields, multi-topic events. - Include full
cargo testoutput and a security notes section in the PR.
docs: reconcile events catalog with all emitted contractevents and add assertion 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: "Write a complete error-code reference mapping every EscrowError variant" labels: type:docs, area:errors, stack:soroban, stack:rust, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The EscrowError enum in escrow/src/lib.rs carries roughly 60 append-only numeric codes across grouped ranges (init, terminal, sweep, attestation, collateral, funding, settlement, refund, legal-hold). The contract explicitly tells SDKs to "branch on the numeric code", yet docs/escrow-error-messages.md may not document every code with its trigger and recommended client handling.
This issue produces a complete, code-accurate error reference.
- Repository scope: Liquifact/Liquifact-contracts only.
- Tabulate every variant: numeric code, name, emitting entrypoint(s), trigger condition, and recommended client action.
- Document the range-grouping convention and the append-only / no-renumber policy.
- Cross-link from
README.mdsecurity notes.
- Fork the repo and create a branch
git checkout -b docs/contracts-26-error-reference- Implement changes
- Write code in:
escrow/src/lib.rsβ only rustdoc additions on undocumented variants. - Write comprehensive tests in:
escrow/src/tests/coverage.rsβ a test asserting representative codes match documented numbers. - Add documentation:
docs/escrow-error-messages.md,README.md. - Include NatSpec-style
///comments on any newly-documented variant. - Validate security: documented numbers match the
#[repr(u32)]discriminants.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: boundary codes per range group.
- Include full
cargo testoutput and a security notes section in the PR.
docs: add complete EscrowError code reference 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 tiered-yield and commitment-lock model with worked examples" labels: type:docs, area:tiered-yield, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The tiered-yield logic in escrow/src/lib.rs (validate_yield_tiers_table, effective_yield_for_commitment, fund_with_commitment) enforces non-decreasing tiers, first-deposit-only tier selection, and a not_before claim lock derived from committed_lock_secs. The interaction between tier matching, the TieredSecondDeposit rule, and claim-time gating deserves a worked, example-driven explanation beyond ADR-005.
This issue expands the tiered-yield documentation with concrete numeric examples.
- Repository scope: Liquifact/Liquifact-contracts only.
- Walk through tier-table validation rules and rejection cases (
TierYieldBelowBase,TierLockNotIncreasing,TierYieldNotNonDecreasing). - Show first deposit via
fund_with_commitmentselecting an effective yield, and why follow-on principal must usefund. - Explain
InvestorClaimNotBeforederivation and its enforcement inclaim_investor_payout.
- Fork the repo and create a branch
git checkout -b docs/contracts-27-tiered-yield- Implement changes
- Write code in:
escrow/src/lib.rsβ only rustdoc clarifications. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ tests matching each documented example. - Add documentation: ADR-005,
docs/escrow-numeric-model.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: no tiers, single tier, max-lock tier, tiered second-deposit rejection.
- Include full
cargo testoutput and a security notes section in the PR.
docs: expand tiered-yield and commitment-lock model with worked examples 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: "Extract the funded-close snapshot write into a single shared helper" labels: type:refactor, area:funding, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The FundingCloseSnapshot write block is duplicated in escrow/src/lib.rs: once in fund_impl (openβfunded transition) and again in partial_settle. Both construct the same struct with total_principal, funding_target, ledger timestamp and sequence, gated by !has(&DataKey::FundingCloseSnapshot). Divergence between the two copies is a latent correctness risk for the pro-rata denominator.
This issue extracts a single private helper used by both call sites.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a private
write_funding_close_snapshot_if_absent(&env, &escrow)helper and call it from bothfund_implandpartial_settle. - Preserve write-once immutability and identical field values; no behavior change.
- Keep the snapshot's documented immutability invariant intact.
- Fork the repo and create a branch
git checkout -b refactor/contracts-28-snapshot-helper- Implement changes
- Write code in:
escrow/src/lib.rsβ extract helper, replace both call sites. - Write comprehensive tests in:
escrow/src/tests/funding.rsβ assert identical snapshot from both paths, write-once on over-funding. - Add documentation: note the shared helper in
docs/escrow-snapshot.md. - Include NatSpec-style
///comments on the helper. - Validate security: snapshot remains write-once and immutable.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: fund close, partial-settle close, no double-write on over-funding.
- Include full
cargo testoutput and a security notes section in the PR.
refactor: extract shared funding-close snapshot helper used by fund and partial_settle
- 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: "Consolidate repeated escrow-read and admin-auth boilerplate into helpers" labels: type:refactor, area:admin, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Nearly every admin entrypoint in escrow/src/lib.rs (set_legal_hold, set_allowlist_active, set_investor_allowlisted, update_funding_target, update_maturity, propose_admin, bind_primary_attestation_hash, append_attestation_digest, cancel_funding, migrate) opens with the same let escrow = Self::get_escrow(env.clone()); escrow.admin.require_auth(); boilerplate. The repetition is error-prone β a future entrypoint could forget the auth call.
This issue introduces a small helper that reads the escrow and enforces admin authorization in one place.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add a private
require_admin(&env) -> InvoiceEscrowhelper returning the loaded escrow afteradmin.require_auth(). - Replace the duplicated pattern at each admin call site without changing behavior or guard ordering (ADR-002).
- Keep SME/investor/treasury auth sites unchanged (different roles).
- Fork the repo and create a branch
git checkout -b refactor/contracts-29-admin-guard-helper- Implement changes
- Write code in:
escrow/src/lib.rsβrequire_adminhelper and call-site replacement. - Write comprehensive tests in:
escrow/src/tests/admin.rsβ assert every refactored entrypoint still rejects non-admin callers. - Add documentation: note the helper in ADR-002.
- Include NatSpec-style
///comments on the helper. - Validate security: identical auth boundary at every site.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: non-admin rejection per refactored entrypoint, post-handover admin.
- Include full
cargo testoutput and a security notes section in the PR.
refactor: consolidate admin auth boilerplate into a require_admin helper 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: "Centralize repeated funding-token and treasury storage reads" labels: type:refactor, area:storage, stack:soroban, stack:rust, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The DataKey::FundingToken read with unwrap_or_else(|| fail(&env, EscrowError::FundingTokenNotSet)) is repeated across sweep_terminal_dust, refund, and the getter in escrow/src/lib.rs, and the Treasury read is similarly duplicated. As more entrypoints move funds on-chain, this pattern will spread, risking inconsistent error handling.
This issue centralizes these reads behind private accessors that already raise the correct typed error.
- Repository scope: Liquifact/Liquifact-contracts only.
- Add private
funding_token_or_fail(&env) -> Addressandtreasury_or_fail(&env) -> Addresshelpers. - Replace the inline
unwrap_or_else(... fail ...)reads insweep_terminal_dust,refund, and getters with the helpers; preserve identical error codes. - No behavior change; this is a readability/maintainability refactor.
- Fork the repo and create a branch
git checkout -b refactor/contracts-30-token-treasury-accessors- Implement changes
- Write code in:
escrow/src/lib.rsβ accessors and call-site replacement. - Write comprehensive tests in:
escrow/src/tests/init.rsβ assertFundingTokenNotSet/TreasuryNotSetstill raised pre-init via the accessors. - Add documentation: note the accessors in
docs/escrow-data-model.md. - Include NatSpec-style
///comments on the accessors. - Validate security: identical typed errors and immutability semantics.
- Write code in:
- Test and commit
- Run
cargo fmt --all -- --check,cargo build, andcargo test. - Cover edge cases: pre-init reads, post-init reads, sweep and refund paths.
- Include full
cargo testoutput and a security notes section in the PR.
refactor: centralize funding-token and treasury storage reads behind typed accessors 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.