Skip to content

Latest commit

 

History

History
87 lines (69 loc) · 14.3 KB

File metadata and controls

87 lines (69 loc) · 14.3 KB

Contract backlog

Source of truth for contract follow-ups. Update before opening a PR.

Legend: [ ] open, [~] in flight, [x] done.


P0 (blocking mainnet)

  • Third-party security audit (events + profile). Cost ~$30-80k for a Soroban-experienced firm. Schedule blocker for the mainnet date.
  • Non-USDC token end-to-end smoke. 2026-06-05: registered the testnet native XLM SAC (CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC) on the new events contract; ran scripts/smoke/contract-non-usdc-token.ts. Bounty published in XLM, alice applied + submitted + won + got paid; payout tx a6c8daad…. Smoke updated to tolerate native-asset tx fees (~143k stroops / 0.014 XLM for 3 alice-signed txs); for non-native tokens the assertion still demands exact-budget delta.
  • Mainnet admin multi-sig provisioned per docs/admin-custody-policy.md (3 signers, 2-of-3). 2026-06-05: launch baseline updated to software multi-sig (Freighter on 3 isolated machines) with a hardware-upgrade trigger codified in admin-custody-policy.md §10 (TVL $250k OR daily volume $50k/day for 7 days OR first signer-machine incident → mandatory Yubikey upgrade). Pre-flight gate codified for both paths — see docs/multisig-preflight.md (Section 1.A hardware, Section 1.B Freighter; everything else identical) and scripts/admin/verify-multisig.sh (chain-state gate, hardware-agnostic). The verify script was sanity-tested against a single-sig account and correctly fails with the per-check breakdown.
  • Mainnet deploy executed per docs/mainnet-deploy-runbook.md. Result committed to deployments/mainnet.json.

From the 2026-06 Stellar-skill audit

See docs/audit-2026-06-stellar-skill.md for full findings.

  • H1 + H2 — Admin/config moved to instance() storage; persistent reads of event-scoped data bump extend_ttl. Both contracts. (2026-06-04)
  • H3 + H4 — Per-event Vec<Address> and Map<Address, Submission> storage replaced with per-element keys (*Count + *At(idx) + *Slot(addr) for O(1) membership). Submissions keyed per-(event, applicant). Caps lifted to 5,000 each after paged cancel landed. New errors TooManyApplicants, TooManyContributors. Paged read APIs get_*_count + get_*_at alongside the capped snapshot reads. (2026-06-04)
  • Paged cancel_event — Replaces single-tx cancel with start_cancel + process_cancel_batch + finalize_cancel. start_cancel snapshots the refund math and flips EventStatus::Cancelling; OwnerOnly branch settles inline. Batches refund up to MAX_REFUNDS_PER_BATCH = 25 partners per tx. Finalize pays owner residual on the FullPartnerThenResidual branch and flips Cancelled. Lifts the contributor cap to 5,000. (2026-06-04)
  • H5 — Two-step rotation for set_events_contract on profile contract. First-set stays single-step (deploy bootstrap); rotation is propose_events_contract + accept_events_contract with a ~1-day timelock and a 7-day expiry. cancel_pending_events_contract to prune. (2026-06-04)
  • H6 — Timelocked upgrade flow (propose_upgrade + apply_upgrade + cancel_pending_upgrade with ~1-day timelock, 30-day expiry); on-chain version() view (initial 0.2.0); migrate() admin one-shot per version guarded by MigratedToVersion. Both contracts. (2026-06-04)
  • Storage layout change shipped post-testnet-deploy. 2026-06-05: testnet redeployed fresh at 0.2.0; the 0.1.x rows held nothing precious. Mainnet has not deployed yet, so the 0.1.0→0.2.0 path will never run in production. migrate() exists per H6 with a documented per-(from, to) dispatch pattern in contracts/events/src/admin.rs + the profile mirror, ready for the first real storage-layout upgrade after mainnet goes live.
  • Domain subscribers ignore the new cancel kinds. 2026-06-05: all four subscribers (BountyEscrowSubscriber, HackathonEscrowSubscriber, GrantEscrowSubscriber, CrowdfundingEscrowSubscriber) now handle FINALIZE_CANCEL (mark domain row CANCELLED) and the START_CANCEL OwnerOnly inline path (read on-chain status; if already Cancelled, mirror). Tests green (events + queues + escrow contract suites = 133/133).
  • Crowdfunding claim_milestone orchestrator path needs admin co-sign. 2026-06-05: new AdminSorobanAuthSignerService finds the SorobanAuthorizationEntry whose address matches the configured admin and signs it via authorizeEntry() from @stellar/stellar-base. Smoke helper exposes adminPreSign: true on driveToCompletion; the crowdfunding claim path passes that flag. Verified end-to-end on testnet — all three milestone claims confirmed with signed 1 admin auth entry in the log and the builder receiving the full 900 TUSD across three claim txs.

P1 (post-launch)

  • select_winners re-run semantics: today rejected; revisit if a real customer wants "append-only" behavior. Open question, not blocking.
  • Grant committee multi-sig primitive (dedicated signer set + quorum at the contract level vs the current address-level multi-sig).
  • Bounty Showdown participation badge on the boundless-profile contract for non-winning finalists.
  • Multi-token support audit: verify the whitelist mechanism handles tokens with non-Stellar 7-decimal scales (currently assumed uniform).
  • M1select_winners percent math now uses remaining_escrow snapshotted at select time so partner top-ups via add_funds flow to winners instead of getting trapped until cancel. (2026-06-04)
  • M2 — Documented operational policy: contract-layer trustline checks aren't reliably possible in Soroban today (SAC balance() collapses "no trustline" and "0 balance"). Admin verifies off-chain before set_fee_account / register_supported_token; FeeAccountUpdated + TokenRegistered events are the off-chain monitoring hooks. (2026-06-04)
  • M4 — Dropped wins_count, submissions_count, applications_count, milestones_completed from Profile. Off-chain indexer derives counters from emitted events. (2026-06-04)
  • M5 — Crowdfunding claim_milestone requires admin.require_auth() in addition to event.owner.require_auth(). Grants stay single-auth (event owner is the grant org). (2026-06-04)
  • L1 — Audit recommendation targeted the pre-H1 persistent layout. After H1 the Pending* keys live in instance storage which auto-extends and is the right home for them; the temporary-storage swap is no longer a net improvement. Decision recorded inline in storage.rs.
  • L2Error::NotInitialized added to both error enums. (Shipped with H1+H2.)
  • L3panic_with_error!(NotInitialized) replaces .expect("admin not configured"). (Shipped with H1+H2.)
  • L4MAX_FEE_BPS tightened from 5000 (50%) to 1000 (10%). Per-event overrides still respect the cap. (2026-06-04)
  • L5EventCreated event carries title. Indexers can populate listings without a follow-up get_event read. (2026-06-04)
  • L6__link_keep removed; evt::EVENTS_LINK_KEEP constant gone. The events module is widely consumed (37+ evt:: references across operation modules), so the keep-alive trick is no longer needed. (2026-06-04)
  • L7 — Crowdfunding validate_create rejects nonzero application_credit_cost (reuses Error::InvalidPillar to stay inside the contracterror 50-variant cap). (2026-06-04)

P2 (roadmap)

  • EVM adapter (per B14 phase 3).
  • Solana adapter (per B14 phase 4).
  • Richer boundless-profile read API for the cross-pillar builder profile UI.

Done

  • 2026-06-03 — fee_bps_override per-event field + effective_fee_bps resolver.
  • 2026-06-03 — WinnersAlreadySelected replay lock on select_winners.
  • 2026-06-03 — Grant last-milestone sweep (G4).
  • 2026-06-03 — Contract upgrade primitive verified end to end on testnet (upgrade tx 50a6ab65).
  • 2026-06-04 — Stellar-skill audit landed (docs/audit-2026-06-stellar-skill.md).
  • 2026-06-04 — H1+H2 storage migration: admin/config → instance(), persistent reads bump TTL, both contracts. Tests green (events 64/64, profile 2/2). Adds Error::NotInitialized to both error enums; replaces .expect() with panic_with_error! on the admin getter (L2/L3).
  • 2026-06-04 — H5 two-step events-contract rotation on the profile contract: propose + accept with a ~1-day timelock and a 7-day expiry; cancel_pending_events_contract prunes expired or unwanted proposals. First-set unchanged. Tests green (profile 9/9).
  • 2026-06-04 — H3+H4 per-element storage layout for applicants, contributors, winners; per-(event, addr) submission entries. Soft caps at 100 per list to keep cancel_event single-tx. New paged read APIs (get_*_count + get_*_at). Tests green (events 66/66, profile 9/9).
  • 2026-06-04 — Paged cancel: start_cancel + process_cancel_batch + finalize_cancel. EventStatus::Cancelling intermediate state. CancellationState snapshots refund math (non_owner_total, remaining_at_start, count_at_start, next_idx, branch). OwnerOnly branch settles inline. Lifts contributor + applicant caps to 5,000. Tests green (events 67/67, profile 9/9).
  • 2026-06-04 — H6 timelocked upgrade + on-chain version + migrate. propose_upgrade(wasm_hash, new_version) + apply_upgrade() (after ~1 day, before 30 days) + cancel_pending_upgrade() + migrate() (one-shot per version). version() view returns the on-chain semver string (initial 0.2.0). Both contracts. Tests green (events 74/74, profile 13/13). Closes audit recommendation; the prior immediate-upgrade path is gone, callers must rotate through the timelock.
  • 2026-06-04 — M1 + M2 + M4 + M5. select_winners Single math switched to remaining_escrow (partner top-ups flow to winners). Operational trustline policy documented in token_whitelist.rs + admin.rs (M2). Profile counter fields dropped (M4). Crowdfunding claim_milestone requires admin auth on top of builder auth (M5). Tests green (events 76/76, profile 13/13).
  • 2026-06-04 — L4 + L5 + L6 + L7. MAX_FEE_BPS tightened to 10% (L4). EventCreated event carries title for indexer convenience (L5). __link_keep + EVENTS_LINK_KEEP removed (L6). Crowdfunding rejects nonzero application_credit_cost (L7). L1 skipped per inline note: post-H1 the Pending* keys are already in instance storage, so the temporary swap is no longer a net improvement. Tests green (events 76/76, profile 13/13).
  • 2026-06-04 — Off-chain catch-up. deploy_and_upgrade.sh rewritten for the timelocked upgrade flow (propose-upgrade + apply-upgrade + cancel-pending-upgrade + migrate + status); the old upgrade action errors out with migration instructions. boundless-nestjs: EscrowContractClient exposes buildStartCancel / buildProcessCancelBatch / buildFinalizeCancel + the H6 upgrade builders; orchestrator gains beginStartCancel + beginProcessCancelBatch + beginFinalizeCancel + beginProposeUpgrade + beginApplyUpgrade + beginCancelPendingUpgrade + beginMigrate. Paged read APIs (get_applicant_count / get_applicant_at / get_winner_count / get_winner_at / get_contributor_count / get_contributor_at). Smoke scripts updated. New EscrowOpKind values (START_CANCEL, PROCESS_CANCEL_BATCH, FINALIZE_CANCEL, PROPOSE_UPGRADE, APPLY_UPGRADE, CANCEL_PENDING_UPGRADE, MIGRATE) shipped in Prisma migration 20260604100000_paged_cancel_upgrade_op_kinds. Profile.ts loses the four counter fields (M4). EventStatus gains Cancelling. tsc --noEmit clean; escrow-contract jest 125/125 green.
  • 2026-06-05 — Orchestrator-path testnet smokes against the new wasm (escrow-cancel.ts, escrow-multi-contributor-cancel.ts, escrow-select-winners.ts, grant-publish-and-claim.ts, hackathon-publish.ts). All confirm the new contract surface flows cleanly through EscrowContractClient → orchestrator → reconcile → subscriber. Two smokes surfaced real off-chain integration gaps (recorded above as audit follow-ups): domain subscribers don't yet recognize the new cancel kinds, and the crowdfunding claim_milestone orchestrator path needs admin co-signing. Both are downstream of the audit, not in the audit itself.
  • 2026-06-05 — H5 admin rotation flow on testnet. propose_events_contractaccept_events_contract returning contract error 17 (PendingEventsContractTimelock) before the ~1-day timelock elapses → cancel_pending_events_contract (emits EventsRotationCancelled) → get_pending_events_contract returns null. Verified the events binding stays unchanged when the rotation is aborted.
  • 2026-06-05 — Testnet redeploy + smoke battery against the new wasm.
    • profile: CDSURDCUHB5GU64METASSZW6SZ7IIKA7RABBYPZTNM45GANGN72K22MA (wasm hash bf9fd1a7…)
    • events: CDKZ5N2D4HYU7BAGNUJAB7WT5EYWVCRNUBJA54G6AVXXRLMAQFUEVMXH (wasm hash d9b0b9a1…)
    • both version() return "0.2.0".
    • TUSD registered (CC2OZCF4HDJJEOTQJ6QMRXR5MZRALRTSVJHGTUFBBGDMHKHQJWCR7O7N).
    • Surface verification: legacy upgrade + cancel_event confirmed absent from the contract spec (CLI parser rejects them); all 13 new methods listed by --help (propose_upgrade, apply_upgrade, cancel_pending_upgrade, migrate, start_cancel, process_cancel_batch, finalize_cancel, get_applicant_count, get_winner_count, get_contributor_count, version, get_pending_upgrade, get_migrated_to_version).
    • OwnerOnly cancel smoke (event 12552982520397825, hackathon, 100 TUSD budget, 0 contributors): start_cancel inline-flipped to Cancelled, full 100 TUSD refunded to admin. Tx 8c4390de5b…. EventCreated.title field present in the event payload (L5 live).
    • Paged cancel smoke (event 12552982520397826, hackathon, 50 TUSD budget + Alice 20 TUSD): start_cancel flipped to Cancelling (tx 7b3df18945…); process_cancel_batch(max_refunds=25) refunded Alice 20 TUSD and returned remaining=0 (tx 288aeaa94d…); finalize_cancel refunded owner 50 TUSD residual and flipped to Cancelled (tx e6c1049fc7…). Final state: status: "Cancelled", remaining_escrow: "0".
    • H6 migrate smoke: first migrate() emitted Migrated(from "0.0.0", to "0.2.0"); replay returned contract error 69 = MigrationAlreadyApplied; get_migrated_to_version returns "0.2.0".
    • All txes recorded in deployments/testnet.json.

How to use this file

Same convention as boundless-nestjs/BACKLOG.md. External coordination (audit firm, formal verification, Stellar SDF) goes in GitHub Issues.