Skip to content

Latest commit

 

History

History
221 lines (177 loc) · 10.6 KB

CONTRIBUTING.md

File metadata and controls

221 lines (177 loc) · 10.6 KB

Contributing to the OracleDesk frontend

Thanks for helping. This is a testnet-only, unaudited project; see docs/STATUS.md for what works today.

Setup

git clone --recurse-submodules https://github.com/OracleDesk/OracleDesk-Frontend.git
cd OracleDesk-Frontend
nvm use            # Node 22 (see .nvmrc)
npm ci
cp .env.example .env.local
npm run dev

Forgot --recurse-submodules? Run git submodule update --init.

Branches and commits

  • Branch names: feat/…, fix/…, docs/…, chore/…, test/….
  • Conventional Commits: feat:, fix:, docs:, test:, chore:, refactor:. One logical change per commit.
  • No force-pushes to shared branches.

Checks (CI runs exactly these)

npm run check:bindings   # generated contract code matches the submodule
npm run typecheck
npm run lint
npm test
npm run build

npm run e2e:testnet is an optional, read-only check against a running backend and live testnet; see the header of e2e/testnet.e2e.test.ts.

Measuring realtime feeds

Use separate terminals:

node scripts/realtime-burst.mjs --manual --port 4310
NEXT_PUBLIC_API_URL=http://localhost:4310/api/v1 npm run dev

Open /markets?profile=1 or /?profile=1 and keep that tab visible. Development-only React Profiler logs [realtime-profile], the component name, commit count and total actualDuration every second. Let the page settle, then trigger each sample from another terminal:

curl 'http://localhost:4310/burst?count=500&rate=500'
# Wait for completion before starting the next sample.
curl 'http://localhost:4310/burst?count=500&rate=50'

Sum logged intervals covering each burst and its final settling interval. Use the same browser, viewport and animation preference for both revisions. For a baseline, instrument the original feed with the same profiler wrapper; the original feeds need no functional changes. Profiler time measures React render work, not socket validation, paint, or end-to-end latency. Slow baselines may coalesce events under load, so fewer commits alone do not prove better responsiveness. Production ignores ?profile=1.

The simulator serves an empty market API and emits synthetic public trades; it never signs or submits transactions. --count, --rate and --port also configure its automatic burst (omit --manual to emit after the first connection). See docs/STATUS.md for measured results.

When the contracts change

The contracts live in OracleDesk-SmartContract and are the source of truth. This repo pins them as the contracts submodule and copies the generated code into lib/stellar/generated/.

git -C contracts fetch origin
git -C contracts checkout <new commit>
npm run sync:bindings
npm run typecheck && npm test   # the Category exhaustiveness check fails here if the enum changed
git add contracts lib/stellar/generated

Never edit lib/stellar/generated/ by hand. If you think a contract needs to change, write it up in docs/contract-requests.md and open the issue in the contracts repo.

x402 contract tests

e2e/x402/ runs the frontend's x402 client (lib/x402/client.ts) against the real trace service from the submodule (x402/trace-api.ts), fully offline: a stub facilitator (e2e/x402/stub-facilitator.ts) checks the signed payment, a fake gateway serves the fixtures in e2e/x402/fixtures/traces/, the Soroban RPC is answered in-process, and any connection to a non-loopback host fails. CI runs it as the x402-contract job.

npm ci --ignore-scripts --prefix contracts/oracledesk-stellar/x402   # the service's own dependencies
npm run check:x402       # frontend and service resolve the same @x402/* versions
npx tsc -p e2e/x402      # typecheck the harness (kept out of the app's tsconfig)
npm run e2e:x402

When the submodule moves:

  1. Run npm ci --ignore-scripts --prefix contracts/oracledesk-stellar/x402, then npm run check:x402. If the service now resolves different @x402/* versions, pin the frontend to exactly those (npm install --save-exact @x402/core@<v> @x402/stellar@<v>) and read the release notes for header, payload or settlement-flow changes.
  2. Run npm run e2e:x402. "contracts/x402 API changed: update e2e/x402/harness.ts and the frontend client" means createTraceApi or MapTraceRepository moved or changed shape: adapt e2e/x402/harness.ts, then lib/x402/client.ts if the wire format changed.
  3. If the service's trace format changes, regenerate the fixture with the submodule's agents/core/trace.ts buildTrace (canonical JSON) and update FIXTURE_TRACE_HASH in e2e/x402/harness.ts to its sha256.

Working on a Drips Wave issue

This repo takes part in Drips Wave. Per the current Drips docs:

  1. Find the issue on the Drips Wave Explore page (or in this repo's issues once a maintainer has added it to a Wave).
  2. Apply for it on Drips. Don't start coding until a maintainer assigns you; the issue isn't yours before that.
  3. Open a PR that links the issue (Closes #123).
  4. Points are earned when the PR is merged and the issue is marked resolved before the Wave ends. Complexity (Trivial 100 / Medium 150 / High 200 points) is set by the maintainer; if an issue turns out harder than labelled, say so on the issue before it's resolved.

Drips also limits how many issues one person can be assigned per organization in a Wave; check the Drips docs for the current numbers. Issue drafts live in docs/wave-issues/ before they're filed.

What a good PR includes

  • A short description and the issue it closes.
  • All checks above passing locally.
  • Screenshots or a short recording for any UI change.
  • Tests for new logic (lib/** especially), and updated docs (docs/api.md is owned by the backend; change it there first).
  • UI copy in plain sentence case, from the user's point of view. Don't claim anything the app doesn't do; label sample data as demo data.
  • Amounts as bigint in 7-decimal USDC base units (lib/stellar/units.ts), never floats, for anything that reaches a transaction.

Number and money formatting

All money, basis-point, and probability display strings in app/ must go through lib/format/money.ts:

  • USDC amounts (formatUsdcDisplay): 2 decimal places by default, truncating toward zero (never rounding up money the user does not have), always grouped (en-US commas), with a USDC suffix and no $ symbol. Non-zero dust below 0.01 USDC renders as < 0.01 USDC. Compact notation ({ compact: true }) uses integer bigint division (1.23M USDC, 14.1K USDC).
  • Signed USDC PnL (formatSignedUsdc): explicit + or − (U+2212) sign for non-zero displayed amounts, and 0.00 USDC without a sign when zero or truncated to zero.
  • Basis points (formatBps) and probabilities (formatProbability): probabilities render with 1 decimal place and a % suffix; signed basis-point changes render with 2 decimal places by default.
  • Backend float records (floatUsdcToRaw): always prefer *Raw decimal string fields when the API provides them (availableCapitalRaw, seedAmountRaw, collateralRaw, amountRaw). Remaining backend float-only fields are converted once at the boundary via floatUsdcToRaw and labelled as backend record in the UI.
  • Lint enforcement: eslint.config.mjs bans .toFixed and .toLocaleString on money-named identifiers and $-prefixed template literals in app/**/*.tsx. If a non-money value matches the naming heuristic, rename the identifier or add // eslint-disable-next-line no-restricted-syntax -- not money: <reason>.

Dialogs

Modal flows (market quick-view, copy-trade, premium checkout, and trace verification) must use the accessible primitive in components/ui/dialog.tsx:

  • Always wrap modal content in Dialog, DialogContent, and provide an accessible name via DialogTitle (use className="sr-only" if visually redundant) and DialogDescription.
  • Choose the appropriate initial focus using initialFocusRef (e.g. amount input or primary action control).
  • Announce transaction lifecycle states and settled quotes politely via components/ui/live-region.tsx (<LiveRegion message="..." />) without announcing in-progress or raw URLs.
  • Mark simulated or blurred background elements as aria-hidden="true" and inert to keep them out of the accessibility tree and tab order.
  • Block dismissals (Escape or outside click) during active transaction submissions to prevent accidental orphan states, and announce the reason why.

Navigation and routes

The site header navigation routes are declared in a single source of truth: components/layout/nav-items.ts (NAV_ITEMS).

  • Any new primary route or nested route section must be added to NAV_ITEMS.
  • Active styling is computed using isActive(item, pathname).
  • The single exact link carrying aria-current="page" is resolved using activeHref(pathname). Exact child matches take precedence over section parent links.
  • 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.

Bumping the contracts submodule

When the contracts repository changes:

  1. Update the pinned submodule:
    git -C contracts checkout <commit-or-tag>
    npm run sync:bindings
  2. Run the interface change report:
    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.