Skip to content

Release: audit remediation + Workstream A access system - #48

Merged
y4hyya merged 20 commits into
mainfrom
staging
Aug 19, 2026
Merged

Release: audit remediation + Workstream A access system#48
y4hyya merged 20 commits into
mainfrom
staging

Conversation

@y4hyya

@y4hyya y4hyya commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Remediation sprint (R-1..R-13: TTL hygiene, receipt checks, min-out slippage bounds + AUM cap, SEP-40 twap fix, admin rotation, typed overflow, admin-setter events) + the full waitlist/access system (gateway endpoints, admin panel, wallet unlock, Turnstile, ACS email from noreply@noether.exchange) + audit dossier seed.

Contract ABI note: vault/factory deposit+withdraw gained min-out params — the prod contract stack upgrades in-place as part of this release, web ships together.

🤖 Generated with Claude Code

y4hyya added 20 commits August 19, 2026 00:35
Waitlist + admin access system, audit dossier program, on-chain caps
enforcement, seed/jury plan, freeze->audit->tranche->ceremony sequencing.


24 Almanax findings (8 high) and 416 Scout detections normalized, verified
against code, and folded into remediation/REGISTER.md (R-1..R-13).
Zero new vulnerabilities; R-11 (missing set_admin on market/referral/factory)
is the concrete new pre-freeze item.
TTL findings from the Almanax+Scout triage: instance TTLs were only
extended at initialize in shim/router/referral/risk, so an idle-enough
contract could archive out from under the trade path. Every contract's
require_initialized guard (and risk's get_config, router's relay_stork)
now re-arms the instance rent; risk also re-arms the config entry on
read. Regression tests jump the ledger into the re-extend window and
assert the hot call restores the full extension.
…R-11

The multisig migration (P3-8) needs set_admin on every contract; these
three only had internal storage helpers. All three copy the verified
two-side-signed pattern (require_admin + new_admin.require_auth) from
vault/router/shim/risk so a mistyped rotation can't brick the admin
role, and emit admin_rotated(old, new). Auth-matrix tests cover
one-sided rejection and old-admin lockout. Market raw WASM 123.4KB,
still under the 128KB limit pre-optimizer.
receive_loss / fund_buffer / route_protocol_fee credited accounting on
the market's word alone. After any credit the physical USDC balance must
now cover every cash-backed bucket (total_usdc + fees + buffer +
shortfall_reserve) or the call reverts InsufficientBalance — phantom
assets can no longer be booked even by a buggy caller. Belt tests that
deliberately fabricated drifted states now build them via direct
storage writes instead of the credit endpoints.
…rd — R-8

Optics + boundary hardening from the scan triage (overflow-checks=true
already prevents silent wraps; these make the failure modes typed and
the invariants explicit): calculate_glp_for_deposit/-withdrawal/-price
and calculate_position_value now return NoetherError::Overflow instead
of trapping, and record_trade_volume ignores non-positive sizes so a
future upstream bug can never deflate the fee-tier window. Non-Result
math fns deliberately unchanged — documented in R-8.
Both vaults' deposit/withdraw now take a caller-declared min-out
(min_noe_out/min_usdc_out on the protocol vault, min_shares_out/
min_usdc_out on the factory; 0 = no bound) so LPs get bounded outcomes
while AUM/NAV move between signing and inclusion. Protocol vault reuses
InvalidSlippageTolerance (error enum is at the 50-variant ceiling);
factory adds MinOutputNotMet=21. Plus set_aum_cap/get_aum_cap — a
global LP-principal ceiling (0 = off), the guarded-launch wave lever.
ABI change: deploy market+vault-consumers' rebuilt clients together.
The mode-1 twap branch returned env.ledger().timestamp() as freshness,
so a stale/frozen vendor mean would always pass the market's
twap_max_age_secs gate. It now pairs the mean with the feed's newest
lastprice timestamp, and returns None (degrade to spot) when that tick
can't be read. Latent in prod (deployed mode is Noeracle-native) but
one set_backend call from active.
…ote — R-6

deposit/withdraw (protocol vault) and depositToVault/withdrawFromVault
(leader vaults) now simulate the call first, take the quoted output, and
bound the real transaction 0.5% below it via the new min-out contract
params — the chain reverts instead of filling under the user's quote.
New simulateCallResult helper in client.ts; caller signatures unchanged.
24 admin setters across all 7 crates now emit a change event (fee
split, referral hook, oracle/backend rotations, stork/reflector guard
configs, referral economics, risk configs, vault caps, factory launch
gates, admin rotations incl. old->new on the pre-existing three) so
every admin action is chain-observable — the transparency claim the
dossier makes. The two initializes stay silent by choice (the deploy
itself is the observable event). Market WASM 123.7KB, within budget.
R-1/R-4/R-6/R-7/R-8/R-11/R-13 flipped to CLOSED with fix commits;
pre-freeze checklist ticked; scout re-run delta documented — every
residual detection carries a written verdict.
…orkstream A

Migration 009 (access_grants + access_audit_log, RLS-enabled). Public:
POST /v1/waitlist (StrKey+email validation, Turnstile server-verify —
503 when unconfigured, 5/min/IP join budget), coarse /v1/waitlist/status,
one-shot /v1/access/challenge + /v1/access/verify for the site unlock.
Admin (bearer key + ADMIN_WALLETS): list/decide/export.csv with an
append-only audit trail and fire-and-forget ACS approval email
(@azure/communication-email; noop without config). Closed-beta key
issuance now honours env-allowlist UNION approved grants. All injected
via ServerDeps — 9 new vitest cases, 152 total green.
…rkstream A

The /audit teaser gains a lazy client island: waitlist form (StrKey
validation, optional email/segment, required ToS+eligibility attestation,
Cloudflare Turnstile) posting straight to the gateway, coarse status view,
and the unlock flow — gateway challenge, wallet signature, exchanged for
the gate cookie by the new /api/access/wallet route (the one-shot
challenge's single consumer). Gate cookie crypto extracted to lib/gate.ts
shared by middleware + both routes: v1 code cookies (30d) unchanged, new
v2 wallet cookies (7d), timing-safe compares. /terms beta ToS page;
middleware whitelists the new open paths. Wallet/stellar imports stay out
of the teaser's first load (dynamic ConnectButton + in-handler signer
import).
Wallet-challenge sign-in (same session-key flow as the referral
dashboard; gateway enforces ADMIN_WALLETS), pending-first table with
status filters and counts, batch approve/reject/revoke with wave tag +
notes, email-sent markers, CSV export.
The Dockerfile's build-arg allowlist silently dropped the new sitekey,
so production builds inlined an empty key and the waitlist card rendered
null. One ARG + ENV mapping entry.
The persisted wallet session lags an extension account switch, and the
drift guard then blocks signing with only 'reconnect from the navbar'
advice — on pages that have no navbar. Both cards now offer 'connect a
different wallet', which drops the stale session and brings the wallet
picker back.
An admin wallet couldn't mint the session key that opens the very panel
that approves wallets (chicken-and-egg): key issuance gated on
env-allowlist U access_grants only. The union now includes ADMIN_WALLETS,
beta-status mirrors it, and a dedicated env-first test file finally
covers the gate at route level (unknown 403; env-listed, DB-approved,
and admin wallets admitted).
The session-auth selector built a fresh object per store snapshot, so
the load effect re-armed on every render — ~90k GETs against
/v1/admin/waitlist until the browser hit ERR_INSUFFICIENT_RESOURCES.
Primitive selectors + useMemo make auth referentially stable; the panel
now fetches only on sign-in, filter change, and actions, plus a manual
Refresh button (no timed polling — admin data changes only when the
admin acts).
Spoken-language copy with a numbered 30-second unlock walkthrough, gold
CTA, and the guarded-launch heads-up. Sender domain swap to
noreply@noether.exchange follows separately (ACS custom domain).
@y4hyya
y4hyya merged commit 66acb55 into main Aug 19, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant