From e5c89d5aa9ea408a2c36fbaafdac8124c174e2d7 Mon Sep 17 00:00:00 2001
From: Alosnireda
Date: Tue, 20 Oct 2026 22:16:40 +0100
Subject: [PATCH] fix #144 contract integrity, #143 spec diff, #142 sdk check,
and #141 watch-only mode
---
.env.example | 3 +
.github/workflows/ci.yml | 7 +
CONTRIBUTING.md | 28 +-
app/admin/create/page.tsx | 144 +---
app/copy-trade/page.tsx | 25 +-
app/layout.tsx | 17 +-
app/markets/page-client.tsx | 13 +-
app/markets/quick-view/page.tsx | 6 +-
app/portfolio/analytics/page.tsx | 6 +-
app/portfolio/page.tsx | 164 +++-
app/portfolio/wallets/page.tsx | 38 +-
app/reasoning/page.tsx | 4 +
components/layout/IntegrityBanner.tsx | 112 +++
components/layout/ViewingBanner.tsx | 78 ++
components/layout/WalletDropdown.tsx | 17 +
components/market/buy-ticket.tsx | 24 +-
components/ui/icon.tsx | 4 +
docs/STATUS.md | 9 +
docs/architecture.md | 32 +
docs/manual-test.md | 26 +
lib/api/traces.ts | 2 -
lib/hooks/__tests__/write-guard.test.ts | 135 +++
lib/hooks/useMarkets.ts | 10 +
lib/hooks/useMyPositions.ts | 27 +-
lib/hooks/useStellar.ts | 62 +-
lib/hooks/useViewedAddress.tsx | 228 +++++
lib/portfolio/export.worker.ts | 2 +-
lib/portfolio/useExportData.ts | 17 +-
lib/stellar/__tests__/contract-check.test.ts | 269 ++++++
lib/stellar/__tests__/errors-coverage.test.ts | 40 +
lib/stellar/__tests__/identity.test.ts | 81 ++
lib/stellar/__tests__/positions.test.ts | 158 ++++
lib/stellar/__tests__/sdk-major.test.ts | 124 +++
lib/stellar/config.ts | 10 +
lib/stellar/contract-check.ts | 806 ++++++++++++++++++
lib/stellar/errors.ts | 17 +
lib/stellar/identity.ts | 74 ++
lib/stellar/positions.ts | 143 ++++
lib/stellar/sdk-major.ts | 124 +++
lib/stellar/write-guard.ts | 60 ++
package-lock.json | 94 ++
package.json | 4 +
scripts/__tests__/spec-diff.test.ts | 277 ++++++
scripts/check-sdk-major.mjs | 41 +
scripts/lib/files.mjs | 17 +
scripts/lib/spec.mjs | 243 ++++++
scripts/spec-diff.mjs | 411 +++++++++
scripts/sync-bindings.mjs | 10 +-
vitest.config.mts | 2 +-
49 files changed, 3988 insertions(+), 257 deletions(-)
create mode 100644 components/layout/IntegrityBanner.tsx
create mode 100644 components/layout/ViewingBanner.tsx
create mode 100644 lib/hooks/__tests__/write-guard.test.ts
create mode 100644 lib/hooks/useViewedAddress.tsx
create mode 100644 lib/stellar/__tests__/contract-check.test.ts
create mode 100644 lib/stellar/__tests__/errors-coverage.test.ts
create mode 100644 lib/stellar/__tests__/identity.test.ts
create mode 100644 lib/stellar/__tests__/positions.test.ts
create mode 100644 lib/stellar/__tests__/sdk-major.test.ts
create mode 100644 lib/stellar/contract-check.ts
create mode 100644 lib/stellar/identity.ts
create mode 100644 lib/stellar/positions.ts
create mode 100644 lib/stellar/sdk-major.ts
create mode 100644 lib/stellar/write-guard.ts
create mode 100644 scripts/__tests__/spec-diff.test.ts
create mode 100644 scripts/check-sdk-major.mjs
create mode 100644 scripts/lib/files.mjs
create mode 100644 scripts/lib/spec.mjs
create mode 100644 scripts/spec-diff.mjs
diff --git a/.env.example b/.env.example
index e051e26..f3f5534 100644
--- a/.env.example
+++ b/.env.example
@@ -17,6 +17,9 @@ NEXT_PUBLIC_TREASURY_CONTRACT_ID=
NEXT_PUBLIC_RESOLVER_CONTRACT_ID=
NEXT_PUBLIC_REASONING_REGISTRY_CONTRACT_ID=
NEXT_PUBLIC_USDC_CONTRACT_ID=
+# USDC asset identifier (format CODE:ISSUER). Empty defaults to USDC:
+# on Stellar Testnet; required when running on a different network.
+NEXT_PUBLIC_USDC_ASSET=
# Where premium payments go. Must match the backend's PAYMENTS_RECIPIENT.
# Empty means the treasury contract (see docs/api.md).
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ef4a728..7d4883d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,6 +14,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: true
+ fetch-depth: 0
- uses: actions/setup-node@v4
with:
@@ -25,6 +26,12 @@ jobs:
- name: Generated bindings match the contracts submodule
run: npm run check:bindings
+ - name: Single SDK copy and major alignment guard
+ run: npm run check:sdk
+
+ - name: Report contract interface changes
+ run: node scripts/spec-diff.mjs --base "origin/${{ github.base_ref || 'main' }}" --summary "$GITHUB_STEP_SUMMARY"
+
- run: npm run typecheck
- run: npm run lint
- run: npm test
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a8e4176..3607cb2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -197,11 +197,25 @@ The site header navigation routes are declared in a single source of truth: `com
- Desktop dropdown navigation is powered by Radix `NavigationMenu` (disclosure navigation with keyboard arrow navigation and Escape support).
- Mobile navigation is rendered by `MobileNav.tsx` using Radix `Dialog` as an accessible sheet that traps focus and auto-closes on route changes.
-## Never commit secrets
-
-No `.env*` files (only `.env.example`), no Stellar secret seeds (`S…`), no
-API keys. Every `NEXT_PUBLIC_*` variable ends up in browser JavaScript, so a
-secret must never go in one. If you commit a secret by accident, tell a
-maintainer privately (see [SECURITY.md](SECURITY.md)) so it can be rotated;
-removing it in a later commit is not enough.
+## Bumping the contracts submodule
+
+When the contracts repository changes:
+
+1. Update the pinned submodule:
+ ```bash
+ git -C contracts checkout
+ npm run sync:bindings
+ ```
+2. Run the interface change report:
+ ```bash
+ npm run spec:diff
+ ```
+ This decodes embedded contract specifications from both the base branch and the new bindings, producing a structured diff of functions, types, error codes, and events.
+
+### Clearing CI spec-diff failures
+
+- **Uncovered Contract Errors**: If a new contract error code was added without a friendly user-facing message, add an entry describing it to `FRIENDLY` in `lib/stellar/errors.ts` (or add it to `UNFRIENDLY_ALLOWLIST` with justification if it is internal/admin-only).
+- **Breaking Call-Site References**: If a contract function was removed or its signature changed, update the calling code in `lib/`, `app/`, `components/`, or `e2e/` so no dead or mismatched invocations remain.
+- **Renumbered Error Codes**: Renumbering existing error codes breaks client-side error mapping and is rejected. Coordinate with contract maintainers to deprecate rather than renumber codes.
+- **Event Changes**: Changes to `Trade` or `TracePublished` events flag a requirement for backend indexer review.
diff --git a/app/admin/create/page.tsx b/app/admin/create/page.tsx
index d54bc48..546811f 100644
--- a/app/admin/create/page.tsx
+++ b/app/admin/create/page.tsx
@@ -1,8 +1,8 @@
"use client";
-import { Icon } from "@/components/ui/icon";
import React, { useEffect, useRef, useState } from "react";
import Link from "next/link";
+import { Icon } from "@/components/ui/icon";
import { RequireWallet } from "@/components/require-wallet";
import { SourceTag } from "@/components/ui/source-tag";
import { useWallet } from "@/lib/contexts/WalletContext";
@@ -10,11 +10,11 @@ import { useGenerateMarket, useMarketGenerationStatus } from "@/lib/hooks/useMar
import { isTerminalStatus } from "@/lib/hooks/generation-status";
import { ApiError } from "@/lib/api/client";
import { UserFacingError } from "@/lib/stellar/errors";
-import type { MarketCategory } from "@/lib/api/markets";
-import { formatUsdcDisplay } from "@/lib/format/money";
import { isAdminAddress } from "@/lib/stellar/config";
-import { tryParseUsdc } from "@/lib/stellar/units";
-import { BACKEND_CATEGORIES, BACKEND_CATEGORY_LABELS, BACKEND_TO_CONTRACT_CATEGORY } from "@/lib/stellar/categories";
+import { DEFAULTS, serializeDiscovery } from "@/lib/markets/discovery-params";
+import { runsInLastHour, saveJob, useAdminJobs, type StoredJob } from "@/lib/admin/job-store";
+import { completedWithoutChain, elapsedFor, formatElapsed, jobFeed, shortJobId, type JobEventTone } from "@/lib/admin/job-events";
+import { JobStatusChip } from "@/components/admin/job-status-chip";
/** Backend limit on POST /markets/generate (per IP; each run calls paid LLM APIs). */
const RUNS_PER_HOUR = 5;
@@ -116,95 +116,6 @@ function AdminCreateContent() {
not used by the backend yet, so this page sends none.
-
-