Skip to content

feat(solana): PR-484 review fixes + Treasury/Vault separation#490

Merged
LandynDev merged 5 commits into
contract-v2from
solana-review-followups
Jun 22, 2026
Merged

feat(solana): PR-484 review fixes + Treasury/Vault separation#490
LandynDev merged 5 commits into
contract-v2from
solana-review-followups

Conversation

@anderdc

@anderdc anderdc commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #484 (merged): the review fixes that didn't make the squash, plus a Treasury/Vault separation and the consolidation of the closed #488. Branched off contract-v2 and ingests #489 (comment compression).

Correctness / fund-safety

  • Over-collateralization gated at pool entry (open_or_request): a miner must hold 1.10x the swap size up front. Collateral can't drop while busy, so vote_initiate can no longer strand a user whose source funds are already committed. (484 review Synapse broadcasting to only interact with whitelisted validators #1)
  • busy ⟹ active invariant: vote_deactivate now refuses a busy miner (mirrors self-deactivate), so a miner mid-pool/reservation/swap can't be deactivated. Nothing clears busy_until early, so the invariant holds cleanly.
  • No admin cancel ops. Evaluated restoring cancel_pool/cancel_reservation (deleted in feat(solana): busy-model cleanup + state-based busy lock (Group 4) #485) but dropped them: no permanent stuck state exists — resolve_pool is permissionless (an open pool always progresses) and a reservation self-expires at reservation_ttl. They were only early-clear accelerators and the sole paths that cleared busy_until manually (the footgun behind a fund-safety bug caught in review). resolve_pool needs no active check (documented invariant); vote_initiate keeps a defensive active check as the funds-commit backstop.
  • Contradictory config bounds rejected (set_min/max_swap_amount, collateral) via shared validate.rs, used by both initialize and the setters so the two write paths can't diverge. (Verify dest transaction sender matches miner's posted address #6, Strengthen halt lockdown to block collateral deposits and activations #8)

Treasury / Vault separation

  • Collateral and subnet revenue no longer share an account. Vault holds only collateral (trustless — leaves only via the owning miner's withdraw or a slash to the wronged user); new Treasury PDA holds only revenue (1% confirm fee, reservation fee, quote churn fee). Timeout slash still pays the user from the Vault. Split invariants: vault.lamports == rent + total_collateral, treasury.lamports == rent + total.
  • Also removes the set_quote write-lock on the collateral vault (a parallelism win).

Anti-flash quote fee (supersedes #488)

Other

  • tunables.rs folded into constants.rs (one home for economic levers); DEFAULT_FULFILLMENT_TIMEOUT_SECS = 14400 (4h) canonical deploy default.

Tests

  • LiteSVM 65 passed / 17 ignored / 0 failed; e2e.sh 24/24 against a live validator (build + deploy + on-chain).
  • Coverage: entry over-collateral gate, busy-lock deactivation, bounds validation, quote create-free/remove-fee, treasury lamport conservation.

anderdc added 5 commits June 22, 2026 13:00
…vation, admin cancels, shared validation

- consolidate tunables.rs into constants.rs (economic-levers section)
- open_or_request: gate on 1.10x required_collateral at pool entry so an
  under-collateralized miner can't strand a user at vote_initiate (#1)
- vote_deactivate: forbid deactivating a busy miner (busy => active invariant),
  so resolve_pool never arms a reservation on an inactive miner (#3)
- restore admin cancel_pool / cancel_reservation, clearing busy_until (#4)
- admin setters reject contradictory min/max bounds (#6)
- set_quote charges the churn fee on creation too, closing the
  remove_quote + set_quote dodge (#7)
- validate.rs: shared Config-field validators used by initialize + setters so
  the two write paths can't diverge (#8)
- DEFAULT_FULFILLMENT_TIMEOUT_SECS = 14400 (4h) canonical deploy default
- tests: 12 new (entry gate, busy deactivation, cancels, bounds, quote fee);
  LiteSVM 67/67, e2e.sh 24/24
…teral Vault

Collateral and subnet revenue no longer share an account. The Vault holds ONLY
miner collateral (trustless — leaves only via the owning miner's withdraw or a
slash to the wronged user); a new Treasury PDA holds ONLY subnet income.

- new Treasury { total, bump } PDA (seeds [b"treasury"]); Vault loses treasury_total
- confirm 1% fee, reservation fee, and quote churn fee all accrue to the Treasury
- timeout slash still pays the user from the Vault (never the treasury)
- withdraw_treasury drains the Treasury PDA (admin-only, caller-chosen recipient)
- split invariants: vault.lamports == rent + total_collateral;
  treasury.lamports == rent + total

Anti-flash fee follows the #488 mechanism (creation free; charge on remove_quote):
- set_quote creation is free again; updates still pay the decaying churn fee
- remove_quote charges the same decaying fee -> Treasury, closing the
  remove+recreate dodge without taxing first-time quotes

Tests updated for the split + new fee semantics. LiteSVM 67/67, e2e.sh 24/24.
# Conflicts:
#	smart-contracts/solana/programs/allways_swap_manager/src/tunables.rs
- cancel_reservation: require an active reservation (reserved_until != 0) so it
  can't clear busy_until on a miner whose pool is still open — that could let the
  miner be deactivated mid-contest and resolve_pool match a removed miner against
  a user (fund-safety regression caught in review)
- resolve_pool: restore the inactive-miner backstop (reset pool, never arm a
  reservation or busy lock for an inactive miner)
- vote_initiate: defensive active-miner check before initiating
- remove_quote: document the deliberate "removal can cost the churn fee" stance
- fix stale vault->treasury doc comments (confirm_swap, set_quote, lib, constants)
- tests: cancel_reservation open-pool rejection + treasury lamport conservation

LiteSVM 68/68, e2e.sh 24/24.
…lf-expiry

No permanent stuck state exists: resolve_pool is permissionless (always progresses
an open pool) and a reservation's reserved_until is always now + reservation_ttl,
so a miner abandoned in a reservation self-frees at the TTL. The admin cancel ops
were only early-clear accelerators and were the sole paths that cleared busy_until
manually — the exact footgun behind the fund-safety bug. Removing them restores a
clean busy => active invariant without a resolve_pool backstop.

- remove cancel_pool / cancel_reservation instructions, their events + tests
- resolve_pool: no active check (documented invariant); vote_initiate keeps its
  defensive active check as the funds-commit backstop

LiteSVM 65/65, e2e.sh 24/24.

@LandynDev LandynDev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed for correctness: Vault/Treasury split holds both invariants on every fund path; busy⟹active invariant sound; shared validate.rs wired into initialize + all setters. Approving. Follow-up: bump CONFIG_VERSION; fix stale vote_initiate comment.

@LandynDev LandynDev merged commit 130098f into contract-v2 Jun 22, 2026
@LandynDev LandynDev deleted the solana-review-followups branch June 22, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants