Skip to content

Latest commit

 

History

History
880 lines (830 loc) · 52.3 KB

File metadata and controls

880 lines (830 loc) · 52.3 KB

Agent Notes

NeonSoup now must became production-ready. NeonSoup always needs hardening as it is a DeFi dapp. The repo contains both an advanced/internal DevTool shell and a final user-facing Frontend shell for the P2P DeFi Kernel integration. Treat the Frontend as the production-target product UI.

Critical Architecture Goal

Clearly separate src/devtool/ UI code from reusable protocol, domain, provider, wallet, GCScript, common app, and core helpers so user-facing NeonSoup UIs can consume the same underlying code without depending on DevTool implementation details.

  • src/devtool/ is a consumer shell and developer tool, not the owner of protocol semantics.
  • src/core/ should be framework-neutral, browser-runtime-neutral where practical, and safe for future UIs.
  • UI shells should import shared app/browser behavior from src/common/** and UI-agnostic protocol/domain behavior from src/core/**, not from another shell.
  • A change in src/core/ must keep all consumer shells/UIs working and up-to-date with NeonSoup Core.
  • A change in src/common/ must keep all consumer shells/UIs working and up-to-date with shared app behavior.
  • Core must not import React, Bootstrap, CSS, devtool app state/hooks, components, localStorage, window, document, history, or browser event APIs.
  • Browser-only responsibilities belong in src/common services/adapters or shell-level adapters: popup/window behavior, return URL cleanup, localStorage, history.replaceState, URL param decoding, and wallet launch.

Current Shape

  • src/intents/*.gcscript.jsonc are top-level GameChanger Wallet intents.
  • src/intents/lib/*.gcscript.jsonc contains composable intent fragments.
  • src/intents/lib/common.gcscript.jsonc is shared by multiple intents; do not change it unless the user explicitly approves the protocol-wide impact.
  • src/devtool/ is the Vite React TypeScript source for the advanced developer shell.
  • src/frontend/ is the Vite React TypeScript source for the user-facing DEX shell.
  • src/common/ contains shared React/browser app state, domain UI helpers, services, adapters, and reusable UI primitives consumed by DevTool and Frontend.
  • src/core/ is reusable NeonSoup Core. Keep it independent from React, Bootstrap, devtool state, and browser runtime APIs.
  • src/common/** may depend on React, Bootstrap, browser APIs, and Core, but must remain design-parametrizable and must not become locked to only DevTool or only Frontend.
  • Frontend and DevTool must not import from each other. Both should consume src/common/** and src/core/**.
  • Never cross-contaminate shell designs or styling unless explicitly requested. Do not apply the production Frontend design system to DevTool, and do not force DevTool's basic Bootstrap styling into the production Frontend.
  • Updating src/core/** or src/common/** must include matching updates in all consuming shells, especially src/frontend/ and src/devtool/.
  • dist/index.html is generated by the landing-page build. Treat dist/assets/ as generated output.
  • dist/app/ is generated by the Vite user frontend build.
  • dist/devtool/ is generated by the Vite DevTool build.
  • dist/intents/*.gcscript.json are built artifacts from the source intents.
  • dist/ is generated and ignored. Do not hand-edit deploy-critical files in dist/; root deploy files must come from source files and build scripts.
  • Root landing deploy fallbacks are sourced from src/landing/_redirects and src/landing/.htaccess, then copied by scripts/build-landing.mjs.
  • Standalone shell deploy fallbacks live in src/frontend/public/_redirects and src/devtool/public/_redirects.
  • Raw high-resolution visual assets live in top-level assets/ and must stay byte-for-byte source material unless the user explicitly asks to replace raw art.
  • Optimized runtime visual assets live in src/assets and are copied to dist/assets by the existing build asset copy step. Old shell-local asset copies are not the source of truth.
  • Keep protocol source intent files under src/intents/ unchanged unless the user explicitly approves the protocol/signature impact. Generated dist/intents/*.gcscript.json files may be rebuilt freely from unchanged sources.
  • Provider defaults are read through shared app config from Vite env variables. Use .env.example as the committed template and keep private .env files untracked.
  • Landing, Frontend, and DevTool Google Analytics injection uses NEONSOUP_GOOGLE_ANALYTICS_ID. Do not commit analytics secrets or private env files.
  • Mainnet/preprod support is network-aware across GCScript intent deployment constants, wallet launch requirements, app cleanup/refetch, and user-facing mainnet disclaimer behavior. Keep those layers aligned across Frontend, DevTool, common state, and intent generation.

Provider Goals

  • Cardano GraphQL MKII is the default provider for the shared app network layer.
  • All providers must share the same app-facing signatures and interfaces.
  • Provider selection must change transport and response mapping only; it must not change application behavior, row semantics, or call-site shape.
  • Keep provider-neutral domain types in the shared app layer. Do not leak raw provider response shapes into React components, reducers, or table renderers.
  • Prefer blockchain-backed categorization from provider queries over wallet return payloads, metadata, or UI state snapshots.
  • For protocol transactions, treat wallet receipts as hint-only. Replace them with chain-backed classification once getTransactions can supply valid_contract, inputs, outputs, token beacons, and parsed datum previousInput evidence.
  • Treat wallet receipts, provider-visible transaction hashes, and confirmed chain transactions as different evidence levels. Do not promote pending Cart items or transaction rows to confirmed from a receipt or hash lookup alone; require explicit chain confirmation evidence in the normalized domain layer.
  • Keep provider fallback explicit. Do not silently switch providers after an error because responses may represent different chain snapshots.

MKII Provider Design

  • Use range on MKII root list queries. Do not rely on unbounded root list requests.
  • Prefer small, whitelist-friendly GraphQL documents with stable operation names and a narrow field surface.
  • Use GraphQL nesting to fetch the required UTxO, datum, token, transaction, and asset fields in the same HTTP response when the query shape supports it.
  • Keep query documents immutable and named. Runtime values belong in GraphQL variables, not interpolated query text.
  • Inspect extensions.explain.operations before accepting a query as semantically correct or efficient.
  • When sorting or filtering is needed, map app-level enums through a closed allowlist to provider-level query arguments. Do not forward arbitrary UI values directly into GraphQL order_by or filter shapes.
  • MKII open-offer normalization must hydrate previousInput source outputs when available so My Orders can show original offer quantity and accumulated ask quantity correctly.
  • For ADA-ask orders, accumulated fills are coin value deltas from the original/root output; for native-asset asks, accumulated fills are token quantities. Do not infer ADA fills from visible-row reserve heuristics.
  • API/provider mistakes that hide protocol data include matching tokens only by missing assetId, treating absent ADA token rows as zero fills, skipping protocol-version parse arguments, and promoting wallet/hash evidence to confirmed chain state.
  • Prefer deterministic pagination helpers over ad hoc page merging. Pagination should normalize limit, offset, nextOffset, and deduplication by canonical identity.
  • Keep pagination reusable across offers, assets, transactions, and future tables. Do not rebuild paging logic inside individual views.
  • Use bundle-aware transaction classification. A single on-chain protocol transaction may represent multiple fills or mixed actions, so summaries and row labels must reflect bundle counts instead of assuming one action per row.
  • Use canonical keys for page deduplication and row identity: txHash#index for UTxOs and policyId.assetNameHex for assets.
  • Treat server-side results as one source of truth, but keep client-side de-duplication and sort fallback in reusable helpers when the backend cannot prove the full ordering.
  • After API calls or wallet-return URL handling, reconcile cart and app state through normalized shared helpers so the visible state reflects the latest confirmed chain/provider result. Do not patch state differently for each provider; keep the reconciliation path provider-neutral and reuse the same normalization layer across transports.
  • Only mark Cart items confirmed after the provider ChainTransaction has real inclusion evidence. Wallet receipts and provider-visible hashes stay pending otherwise.
  • Wallet receipts are evidence hints, not chain confirmation. Do not mark Cart items or transaction rows confirmed without provider/chain inclusion evidence.
  • Provider contracts must return normalized domain types. Raw MKII and Blockfrost response shapes should remain private to provider modules.

Swap Feature Goals

  • Build Swap as an AMM-like user experience on top of the order book, not as a pool swap abstraction.
  • Default to bundle mode for cheapest atomic execution.
  • Offer opt-in parallel mode for best-effort partial completion on fast markets.
  • Offer opt-in contention-premium routing so the user can select a slightly worse-priced order to improve inclusion probability.
  • Current Swap execution routes one-way order UTxOs only.
  • Two-way swap support is contemplated for future implementation: discover both one-way and two-way swap UTxOs, then normalize them into one directional quote model for the UI without reusing one-way datum/redeemer logic for two-way execution.
  • Keep all quote and fill decisions local to the user device or wallet flow; do not add a trusted matcher, batcher, or custom indexer just to make the UX convenient.
  • Re-quote immediately before submission and reconcile visible state from the final chain result.

Swap Design Practices

  • Treat the live chain book as the source of truth for executable liquidity.
  • Keep detailed Swap math, threshold policy, p2p-wallet notes, route-bar rules, color treatment, and current limitations in docs/SWAP.md; update that file instead of bloating this agent note.
  • Keep Swap book state layered: raw canonical book, policy executable book, and actual route. Raw orders stay keyed by txHash#index; asset policy uses canonical policyId.assetNameHex asset keys.
  • Use minExecutableOfferQuantity for book-level open-order filtering and minMakerRemainderQuantity for route-boundary remainder decisions. Both are base-unit integer strings; 0 disables each policy independently for coins and native assets.
  • Do not reintroduce a single overloaded asset threshold for both book filtering and maker-remainder routing.
  • Quote labels and route bars must use the exact denominator from docs/SWAP.md; do not mix input quantities, receive-asset liquidity, and maker remainders in one progress bar without converting to the documented display denominator.
  • Filtered offers affect executable price and UX, so keep filter summaries, effective price, slippage baselines, route colors, and cart generation derived from the raw/executable/route layers consistently.
  • Preserve one-way swap semantics for currently executable instant user fills and two-way swap semantics for future liquidity-provider support.
  • Use directional depth, not raw UTxO count, to describe available liquidity.
  • Keep discovery narrow: pair-scoped queries, bounded pagination, and minimal fields for quote generation.
  • Prefer reusable normalization helpers for open offers, quote rows, partial fills, and bundle summaries.
  • Let the UI present simple amount to offer, amount to receive, and Swap actions while the app layer handles price discovery and protocol-specific routing.

Commands

  • Install dependencies: pnpm install
  • Build intents and frontend: pnpm build
  • Serve full built dist locally: pnpm serve
  • Serve devtool with Vite locally: pnpm run dev:devtool
  • Serve user frontend locally: pnpm run dev:frontend
  • Typecheck devtool: pnpm exec tsc -p ./src/devtool/tsconfig.json
  • Typecheck user frontend: pnpm exec tsc -p ./src/frontend/tsconfig.json
  • Build user frontend: pnpm run build:frontend
  • Optimize runtime assets from raw source assets: pnpm run assets:optimize
  • Verify optimized runtime assets, references, and stray dist/ images: pnpm run assets:verify
  • Build only one intent:
    • pnpm run build:open
    • pnpm run build:close
    • pnpm run build:swap
  • Generate the GC auto-template demo: pnpm run serve:autogen

After editing any source intent, rebuild the matching dist/intents/*.gcscript.json. After editing src/common/, run checks for every consuming shell you affected, normally pnpm exec tsc -p ./src/devtool/tsconfig.json, pnpm exec tsc -p ./src/frontend/tsconfig.json, and targeted tests. After editing src/devtool/ or src/frontend/, run the narrowest useful check first, then the relevant build. Run pnpm run build when the change touches shared frontend behavior, bundling, provider contracts, or wallet-intent loading. pnpm serve must mirror production SPA fallback behavior for /app/* and /devtool/*, including wallet return URLs with query parameters. Asset optimization commands are dev-only pre-prepare tools. Do not wire pnpm run assets:optimize or pnpm run assets:verify into build, dev, serve, or shell build scripts.

TODO

  • Add explicit NeonSoup discovery for two-way swap UTxOs. Current execution only routes one-way orders; keep two-way rows unsupported until provider discovery, directional normalization, and two-way-specific wallet protocols are implemented.
  • Normalize both swap types into directional quote rows in the future so the swap UI can reuse the existing fill/composition pipeline without pretending two-way orders are one-way orders.
  • Keep the Swap feature aligned across DevTool and Frontend for bundle-first, parallel best-effort, and contention-premium execution modes.
  • Keep the quote engine on-device and chain-backed; avoid introducing a centralized matching service.
  • Validate the final fill path against live chain state immediately before submission.
  • Remove the temporary Swap/Fill full-fill window.alert() once wallet-side full-fill support works.
  • Remove the temporary connected-wallet requirement for wallet-launching actions once user-agnostic intent execution no longer fails with missing address data.

Safety Rules

  • Keep changes scoped. This repo is intentionally messy while the protocol is being explored; avoid broad refactors, formatting churn, or production polish.
  • Preserve existing working code that already meets the intended boundary. Avoid broad rewrites and use tests as a safety net before moving ownership boundaries.
  • For any code or design change, preserve the existing working logic and style unless the user explicitly asks to change them.
  • Do not edit generated optimized runtime assets in src/assets by hand when the raw source in assets/ is the intended source of truth; regenerate via pnpm run assets:optimize.
  • Use scripts/asset-policy.mjs as the local source of truth for generated runtime asset extensions and intentional compatibility fallbacks.
  • Generated transparent runtime art must preserve source resolution and alpha. The only current transparency exception is assets/images/dark-bg.png and assets/images/light-bg.png: they are background-only assets generated as full-resolution JPGs by compositing over theme-matched background colors because JPG cannot preserve alpha.
  • Keep all Landing, Frontend, DevTool, README, and docs image references on optimized src/assets or /assets paths. Avoid root-level or stale images such as banner.jpg, root logo.png, root icon.png, or root favicon.*.
  • Do not store Blockfrost keys, wallet secrets, seed phrases, private keys, or personal addresses in committed files.
  • Do not hardcode provider URLs or API tokens in source. Use Vite env variables: VITE_NEONSOUP_PREPROD_BLOCKFROST_URL, VITE_NEONSOUP_PREPROD_BLOCKFROST_KEY, VITE_NEONSOUP_MAINNET_BLOCKFROST_URL, VITE_NEONSOUP_MAINNET_BLOCKFROST_KEY, VITE_NEONSOUP_PREPROD_GRAPHQL_MK2_URL, VITE_NEONSOUP_MAINNET_GRAPHQL_MK2_URL, VITE_NEONSOUP_ENABLE_WALLET_URL_PATTERN_OVERRIDE, and VITE_NEONSOUP_GC_WALLET_URL_PATTERN.
  • Keep GameChanger wallet URL-pattern runtime customization disabled in production builds. The feature flag hides the Options input only; it must not erase or ignore the configured gcWalletUrlPattern value. Route all wallet launches through the centralized gcWallet.ts option handling and show a warning in Options when the editable field is enabled and non-empty.
  • Wallet return URLs for the production Frontend deep-link under /app/*, such as /app/swap?result=.... Static hosts, local preview servers, Netlify _redirects, Apache .htaccess, and equivalent host configs must rewrite those paths to the Frontend shell before React can process returns.
  • Network changes must continue to use one centralized cleanup/refetch path: disconnect wallet state, purge Cart/history/open-offer/portfolio/wallet-return and network-specific endpoint state, then refetch network data. Preserve only preferences that are not network-specific.
  • The mainnet disclaimer should appear whenever the user sets mainnet, including app-default-mainnet cases, but should not reappear on ordinary reload after the same mainnet selection was acknowledged.
  • The shared app version uses legal SemVer build metadata: package.json version + VITE_NEONSOUP_BUILD_TAG, for example 0.0.1+local. Use that build tag to intentionally force local-state update prompts when persisted state is incompatible.
  • Preserve the intent argument interface unless the user asks for a coordinated migration across all intents and frontend code.
  • When relying on GameChanger Wallet/buildTx to show missing-balance or refill dialogs, still emit every required GCScript argument. Undefined values in earlier nodes such as plutusData fail before buildTx can provide friendly wallet UX.
  • Keep generated GCScript reusable and data-driven. Do not hardcode execution-specific parameter values such as modes, IDs, item data, source references, roles, counts, or indexes inside reusable run, macro, or finally structures. Pass those values through GCScript args, propagate args explicitly into nested scripts, and resolve them with ISL get('args...'); use get('cache...') for wallet-runtime results.
  • Keep GCScript composers inspectable. For bundled/parallel composition, prefer top-to-bottom code that visually resembles the resulting script structure over many small helper functions.
  • Keep the cart/composer layer flat unless a nested scope is required by the protocol fragment itself. isolateCache is fine inside protocol fragments, but adding another isolation wrapper around the cart composer/import layer can shift outputs into group-local cache paths and break root references like cache.myAddress or cache.intents.<index>.... Example: the cart bundling bug came from wrapping each group in its own script, which moved the wallet address out of the expected root cache.
  • Fetch getCurrentAddress once at the cart root and pass it through as a shared root value. Do not re-fetch it inside per-group wrappers. Example: the myAddress issue in cart execution was not that getCurrentAddress was missing, but that it was introduced at the wrong scope, so imported intents could not reliably resolve offer-address against cache.myAddress.
  • Before changing close logic, verify the exact txHash#index against the live chain API. A historical transaction that no longer yields a UTxO is a stale or spent input, not protocol contention.
  • DevTool and the user Frontend are both Vite React shells. Keep both structures compact and Bootstrap-first unless explicitly requested otherwise.
  • Prefer warnings over input blocking in the devtool UI. Bad values are useful for protocol testing and wallet-side debugging.
  • Multiple output asset rows with the same asset are acceptable in GCScript tx builder flows; the builder can sum them. Do not add app-side aggregation unless there is a concrete reason.
  • For bundled Cart open transactions, same-policy beacon mints should be grouped under one mint entry/consumer/witness, but grouping is not a substitute for valid reference-script deployment constants.
  • If Plutus evaluation reports a missing mint script witness, compare the selected deployment constants with the known-good branch and live chain reference-script UTxOs before rewriting transaction composition. The preprod one-way-swap deployment used by NeonSoup should be verified against the live official references from 7b613fc2481a93b950f3bf48f8fbc5c49d6decce126a3572fab428feb73ed5b0#0 for beacons and #1 for the spending validator before preserving or changing those constants.
  • Blockchain asset quantities, lovelace, token amounts, price numerators, price denominators, and UTxO value fields are BigNum-domain values. All arithmetic on them must stay BigNum-based: use TypeScript bigint in app/domain code and GCScript/ISL *BigNum helpers in wallet scripts. Do not cast these values to number/normal integers for math; only convert to number after bounding the result to a small UI-only value such as a progress percentage.
  • Asset dataset keys must be deterministic canonical identifiers in the form policyId.assetNameHex. ADA is keyed as ada.ada; native assets with an empty asset name are keyed as policyId.. Do not use friendly aliases such as usdm as map keys.
  • Asset metadata fixes must use MKII or another primary/live provider source. If metadata such as decimals is missing or null, leave that configured asset untouched instead of guessing.
  • Prefer assetId for provider/GC asset identifiers such as lovelace or policyId + assetNameHex; do not use the older provider label in app code.
  • Do not add ad hoc localStorage migrations for old persisted app state shapes. Use the centralized version mismatch banner and update/reset flow.
  • Persisted UI flags may be repurposed only when visible labels and centralized selector semantics make the new behavior explicit. Avoid ad hoc migrations unless the stored shape actually changes.
  • Normalize transactions, offers, assets, and users in reusable helpers before rendering any table. Keep the row model stable and derived from the normalized domain layer.
  • Keep cart swap quantities and execution-history filtering centralized in the shared domain layer. Panels should render normalized rows and labels only, rather than recomputing swap accounting or history predicates locally.
  • Execution/history filters should be centralized in shared state or domain helpers, not recomputed in panels.
  • Keep on-chain/API parsing and categorization centralized in reusable domain helpers by purpose. Table and UI components should render normalized rows/data only and should not decide protocol action, ownership meaning, or identity.
  • Wrap refresh handlers in closures when wiring UI events. Do not pass the raw click event through to loading or reconciliation code, or state loaders can receive array/event-shaped garbage such as extraPendingHashes is not iterable.
  • Ownership badges must be derived from explicit stake-key comparison against the current wallet. Missing You badges are a normalization bug, not a rendering preference.
  • Keep provider contracts normalized around shared chain transaction shapes so MKII and Blockfrost differ only in transport and mapping, not row semantics or classification behavior.

Common Pitfalls

  • Accidentally making src/core/** depend on React, browser globals, devtool state, Bootstrap, or CSS.
  • Accidentally making src/common/** depend on shell-specific DevTool or Frontend modules, styles, routes, or component assumptions.
  • Splitting GCScript composers into many small helpers until humans cannot audit the produced script shape.
  • Adding wrapper scripts or cache isolation around Cart composition and moving cache.myAddress out of root scope.
  • Refetching wallet address per group/item instead of once at the Cart root.
  • Treating wallet receipts, returned tx hashes, or provider-visible hashes as confirmed chain data.
  • Reintroducing direct single-intent execution state instead of using Cart composition for all Open/Fill/Close paths.
  • Changing route-bar math or denominators to hide a booked-UTxO bug. Compare against DevTool and docs/SWAP.md first.
  • Passing raw click events into refresh/reconciliation handlers. Wrap handlers in closures so loader arguments cannot receive event-shaped garbage.
  • Treating a browser/devtools visual issue as actual layout overflow without measuring scrollWidth and element bounds.
  • Changing src/intents/ signatures while trying to refactor UI/core adapters.
  • Forgetting legacy snapshot defaults such as utxo-ask-quantity: "0".
  • Leaking provider raw response shapes into components/reducers.
  • Using friendly labels or aliases as identity keys.
  • Converting token/lovelace/price arithmetic to JavaScript number.
  • Adding regression tests for code that is intentionally removed.
  • Silently requiring live provider/network checks as unit tests.

Devtool UI Notes

Goal

DevTool is meant to give developers enough debugging context to debug, audit and try new features prior shipping them into the user-facing frontend. It shares same core and common UI components and helpers with user-facing frontend, so actually both shells share same relevant code underneath.

  • Keep the devtool pair-driven: the asset pair selector is the first step for protocol views and should remain visually prominent.
  • Action buttons from tables should be context-aware: navigate to Trade, select the relevant pair/order/asset, and prepare the matching action.
  • Keep Open, Fill, Close, Orders, Activity, User, Options, and Developer views compact and Bootstrap-first.
  • Use Bootstrap alert tones correctly: danger for errors, warning for risky or invalid-but-allowed input, success for completed actions, and info for neutral status.
  • Copy buttons must copy the value they label. For UTxOs, copy <txHash>#<index>, not just the transaction hash.
  • Transaction/activity rows should expose useful explorer links, currently Cardanoscan by current network.
  • The Developer view should keep captured wallet return data and app state easy to inspect through the reusable JSON viewer.
  • Theme updates should preserve the former single-file app feel: compact panels, dark-first palette, restrained Bootstrap surfaces, and readable JSON boxes.
  • Every Open/Fill/Close execution uses the Cart-item composition pipeline. Direct actions are transient one-item compositions; persisted Cart runs use the same bundle/parallel builders.
  • The Connect wallet intent is independent from the Cart/composability system and should keep working as a direct wallet public-data flow.
  • The current app design is composability-first. Direct Open, Fill, and Close actions should be treated as transient one-item Cart compositions, while Cart runs remain the persisted multi-item composition path. Reuse the same builders, wallet launcher, and receipt flow for both.
  • Do not reintroduce a legacy single-intent execution path, separate single-run state, or a second argument snapshot model. The form state is preparation state only; CartItem.args is the immutable execution snapshot.
  • Keep wallet-launch helpers purpose-agnostic. Any explicit GCScript code should be launchable through the generic wallet transport, including Connect Wallet and future special intents. Do not create purpose-specific wallet launch helpers.
  • When composing GCScript for wallet execution, keep root-scoped values flat and stable. Fetch root wallet values once, pass them through explicitly, and avoid adding extra cache isolation around composition wrappers unless the protocol fragment itself requires it. Example: the cart bundling bug came from group-local wrapper scripts changing cache shape instead of the fragment code itself.
  • Close intents should preserve the expected final output shape exactly. The last output should be the unfilled offer; a redundant ask-side row can make the generated close tx fail even when the input UTxO is live.
  • When a submission flow must keep going after rejected transactions, use submitTxs with extras: true and noFail: true, then reconcile from txsExtended instead of stopping at the first error. In NeonSoup, use this only as wallet submission evidence; chain/provider confirmation remains authoritative for final Cart status.
  • Cart default view should show draft items only; the history toggle should show non-draft statuses while preserving old persisted field names until a deliberate migration.

Landing Page Notes

  • The landing page is a standalone source shell under src/landing.
  • Keep one primary CTA only. Do not add secondary CTAs unless explicitly requested.
  • Public Alpha/Mainnet disclaimers must stay visible in page content near the CTA, not hidden behind a modal or dialog.
  • Avoid negative crypto/social-media slang such as "cooked"; use soup/kitchen prose only when precise and useful.
  • The P2P soup separator is a landing-only asset unless explicitly requested elsewhere. It keeps the full pot/fluid/payload animation on large screens; on medium, small, and mobile screens it must remain phrase-only for performance.

User Frontend Notes

Goal:

The user-facing Frontend goal is a simplified, friendly, AMM-like DEX UX over the order book. Hide order-book complexity without pretending it is an AMM pool.

  • Hide developer-facing JSON views, raw wallet-return panels, provider debug panels, bulk-open tooling, and technical notices from the user frontend.
  • Avoid developer jargon in user-facing toasts, notices, cards, and transaction detail modals. Do not show messages about internal wallet-return export plumbing when the user only needs connection or execution status.
  • Swap is the primary user action. Offer means create/open a new maker offer from Markets or Portfolio when the wallet has the asset balance.
  • The Offer/Open view exists as a distinct page from Swap and should always be visible in user navigation with the label Offer. Options also exists as both a hidden route/page and the modal opened by the ... button.
  • User-facing forms must block invalid actions with Bootstrap alerts and disabled CTAs. DevTool may remain more permissive for protocol debugging.
  • Incognito Mode allows Swap/Open-style execution without a connected wallet only when wallet-side GCScript can request the missing address data. User copy must make clear that balances are unknown and the user is responsible for entering valid values.
  • Incognito execution must avoid retaining user activity traces. Do not add direct incognito actions to persisted Cart history, and clear queued incognito Cart items after wallet launch or return.
  • User-facing Options should not expose internal tuning such as toast auto-hide timeout, history fetch limit, or explorer URL template unless explicitly requested later.
  • Option dependencies must be visible in the UI: bundle size is muted unless best-effort parallel mode is off; pay-up premium is muted unless pay-up for lower contention is on; numeric option inputs should show units/suffixes such as % or actions.
  • Tooltips are required on complex user-facing controls, including every Options parameter, wallet connect/disconnect/widget, Cart Mode, parallel mode, pay-up, slippage, route coverage, open/close order actions, collision badges, and any similar non-obvious control.
  • Use the shared rich tooltip system, not native title. Because modals and scroll containers can clip children, Frontend tooltips should use a portal/floating layer or equivalent clipping-safe implementation.
  • Use Bootstrap Icons consistently.
  • Keep prototype-inspired neon glows, hover borders, switch-button click animation, mobile-first responsive layout, and dark/light theme fidelity.
  • Important interactive cards and boundaries should show subtle standby borders and stronger hover glow/border treatment. Do not apply this treatment to every static card.
  • Light theme must keep prototype-like colors and readable modal/card surfaces; avoid translucent modal surfaces that reveal underlying content.
  • Asset icons should be normalized ticker-centered circles. Use asset-stack wherever an asset pair is shown, except inside plain messages.
  • Markets, Portfolio, My Orders, and History lists/tables should have clear column headers and user-meaningful columns, with larger readable amounts and zero/small amounts sorted after larger amounts.
  • Markets should list only registered asset pairs in the shape [all registered assets] / [coin | stablecoin | mainstream], avoiding unpopular or random provider-discovered markets.
  • Markets sorting preference is wallet-owned asset presence first, then more executable/available orders, then total matching orders, then balance amount as a tie-breaker.
  • Markets row actions should be wallet-context-aware. If the wallet owns either asset, Offer and Swap should use that owned asset as the offered/pay asset; if both are owned, choose the direction that best fits the listed pair and available liquidity.
  • Markets should not show a Your balance column. Keep action columns stable with reserved slots/placeholders when an action button is unavailable.
  • Transaction hashes and UTxO references should render short forms such as abcd...1234 with compact copy-to-clipboard icon buttons.
  • Hide or disable market-data cards until provider-backed market data exists; do not consume API calls for unavailable market data.
  • Do not use fake/mock data or generic prototype labels in the final frontend. Use real useful data/wording or render nothing.
  • The Frontend should use centralized design-system assets through src/assets exposed at /assets by dev/build plumbing. Keep contextual UI_ASSETS mappings for wallet connect/disconnect, Cart, Swap/route, Open offers, network, Options, History, alerts, and toasts in frontend code.
  • Use CyberNekos more often than kitchen assets for friendly production DEX help. Kitchen assets are only for cases where the filename/content best matches the UI action or idea.
  • Do not use the P2P soup separator unless explicitly requested.
  • Tooltip, page-title, and help copy should be friendly and protocol-accurate. Swap copy must explain live order offers without implying AMM pool liquidity.
  • open-hero and swap-hero are desktop/tablet affordances and should be hidden on small/mobile screens.
  • Mobile cards must preserve enough top padding for page titles; titles should not touch card borders.
  • Alert read-more modals must reuse the same graphical asset as the originating alert.
  • Modal cards must be vertically centered in the viewport, fit mobile width/height, use internal scrolling, keep helper art at the bottom-left with enough padding, and avoid body/page overflow.
  • When diagnosing overflow or responsiveness, render the app locally and measure document/body/modal dimensions such as scrollWidth and element bounds. Do not fix unrelated layout rules without evidence.
  • Network selection is centralized in app state/config. The current default is preprod, and the footer network pill should stay minimal with a tooltip.
  • User-facing Options must keep internal technical defaults hidden unless requested. Defaults, thresholds, booleans, providers, percentages, factors, theme, fee config, timeout values, and network defaults belong in centralized APP_CONFIG.
  • Invalid same-asset pairs or incomplete pair context from Markets/Portfolio actions must reset the second asset to unselected, show a clear alert, and disable CTAs.
  • User-facing warnings should block truly impossible or risky trades such as insufficient live liquidity, but must not expose advanced remainder/min-UTxO internals as end-user copy.
  • User-facing Swap must distinguish typed pay amount, final executable pay amount, round-up amount, received amount, balance used, and service fee. Warning color belongs only where the final executable amount differs from the typed amount or another real risk threshold is crossed.
  • Stats/amount UI must remain readable on medium/large screens and stack on mobile; verify with rendered screenshots or concrete dimensions after visual changes.
  • Frontend slippage policy is config-driven: warn at tolerance * warningSlippageMultiplier, block with danger severity at tolerance, block zero tolerance, and block tolerances at or above the configured maximum.

Frontend Cart And Routing Notes

  • Cart Mode ON queues operations and opens the wallet only from Cart Run.
  • Cart Mode OFF launches the wallet per operation, but still creates Cart items so they remain as operation history.
  • In Cart Mode, action buttons feeding the cart should visibly indicate add-to-cart behavior with a plus and cart icon.
  • In Cart Mode, open the Cart modal only when the cart was empty and the user just added the first item. Do not auto-open on every subsequent add.
  • Booked Cart source UTxOs must be excluded from subsequent Swap/Open/Close routing before all calculations. This affects price calculation, amount totals, route segment bar, slippage/impact, validation, and cart-item generation.
  • Keep Cart state as the only source of truth for booked source UTxOs.
  • Add reducer/domain-level collision protection for duplicate source UTxOs. UI validation alone is insufficient because stale state or repeated clicks can dispatch duplicates.
  • Existing collision badges remain useful as diagnostics for old persisted state, but new operations should not create collisions.
  • When fixing route bars, compare against DevTool behavior and route math before editing. Excluding booked UTxOs should be equivalent to quoting against a manually prefiltered book; do not change denominator or segment rules to mask an exclusion bug.
  • Remember the route-bar fix: exclude booked source UTxOs before quote math while preserving DevTool route semantics, denominator, leftovers, round-up, and segment categories.
  • A typed amount that leaves an invalid/tiny maker remainder must be handled by route math using round-up/unrouted semantics, not exposed as a developer warning or arbitrary CTA block when the DevTool-compatible route can execute safely.
  • Route-bar denominator and segment visibility must remain stable while the amount increments. Only newly included offers or a final unavailable segment should visibly add/jump; leftover/change segments must not disappear in ways that shrink previous segments.
  • Semantic route segments are distinct: normal fills, maker-remainder/change, clean-execution round-up/min-remainder absorption, and truly unavailable input. Only truly unavailable input should use unavailable/striped treatment.
  • Cart collision badges are diagnostic; correct routing and reducer/domain guards should prevent new collisions by excluding already booked source UTxOs before quote/cart-item creation.
  • Cart item labels and metadata must derive from each item's immutable protocol args, not from a selected or stale source offer. Cart rows should render the normalized asset pair icon/label primitives when asset metadata is available.
  • Routing math, route behavior, segment logic, and segment styling are easy to break. Be extra careful with route changes and ask before changing route-bar semantics when the requested fix is ambiguous.
  • Direct actions and Cart runs should share one composition, launch, and receipt path when wallet semantics match. Cart Mode changes whether launch is immediate or queued.
  • Copy-to-clipboard buttons on wallet-launching CTAs exist so users can eject from the NeonSoup Frontend and execute the same wallet action on the device they choose. They also give users and developers a way to audit, fork, and redeploy these actions outside NeonSoup. In Incognito Mode, user-agnostic action URLs can be executed by any user on any device. Some action URLs may not be reusable today, but reusable wallet action URLs are technically possible and may be implemented later.

Frontend History And Wallet Notes

  • Frontend History should show wallet transactions matching all recognized pairs, not only the currently selected Swap pair.
  • History rows should be ordered newest first and include a created-at datetime.
  • History fetches must be capped internally by app-state defaults, but those caps should not be exposed in user Options.
  • History refresh should use bounded MKII address-transaction queries for all recognized wallet activity, not only the selected pair. Avoid unbounded overfetch that can trigger 30s provider timeouts.
  • Remove evidence/debug columns from user-facing History.
  • Transaction details should use body-backed data only. Do not infer operation values, prices, or fees from metadata.
  • Details modals should show meaningful amounts/prices prominently, and render inputs/outputs/UTxO refs as small audit labels with copy buttons.
  • Do not render obvious zero-value boxes, such as an absent ask side when opening an offer.
  • Use Cardanoscan links from centralized defaults, but do not expose the URL template in user Options.
  • Avoid duplicate transaction entries and avoid promoting wallet-return hashes to trusted confirmations before provider/chain evidence exists.
  • History should dedupe wallet-return hints against chain-backed rows and show all recognized pairs for the wallet.
  • The connect wallet widget should reuse the same internal connection state/return handling as DevTool, including app-state reload/refresh, history cleanup to avoid reprocessing the same return payload on navigation, connected shape, disconnect button, disconnected shape, and connect button.
  • When connected, show wallet name, truncated address, and wallet type pill when provided.
  • Show a contiguous disconnect button with a cross icon only when connected.
  • When not connected, the connect button should be primary/CTA-colored.
  • Wallet tooltips and related docs should explain that GameChanger Wallet can connect through supported wallet types including CIP-30 browser extension wallets, hardware wallets, seed phrase wallets, QR wallets, and burner wallets. This prevents the misconception that a user must import a wallet into GameChanger before using GameChanger dapps.
  • Remove or hide developer-facing wallet-return messages such as “wallet return captured” when they are not useful to end users.
  • On wallet disconnect, purge Cart items, loaded transactions, open offers, and other user-specific cached traces to protect privacy.

Data Normalization Goals

The shared app layer should centralize API-to-app-state parsing and categorization in reusable helpers instead of scattering it across tables, components, reducers, or provider call sites.

  • Transaction rows, bundle summaries, and execution labels must be derived from reusable transaction-domain helpers.
  • Offer/order rows must be normalized once and reused across every table or view that renders live protocol state.
  • Asset rows, balances, and holdings must share one canonical asset-key and quantity normalization path.
  • User-scoped views must use explicit ownership semantics from helpers rather than ad hoc booleans.
  • Table components should render normalized domain objects only; they should not decide protocol action, ownership meaning, or row identity.
  • Preserve current working behavior when refactoring, especially live offer ownership display, frontend/devtool rendering, cart composition, and intent execution.
  • When a blockchain-backed source of truth exists, prefer it over wallet-return payloads, metadata, or UI state snapshots for categorization.
  • For financial table sorting, prefer explicit rank fields over raw numeric sort when rows meeting multiple user-relevant criteria should go first, such as wallet balance presence before amount and then available order count.
  • For iconized transaction/cart rows, reuse normalized asset/pair primitives instead of rebuilding per-row icon or label logic.

Consuming APIs

  • When using MKII API for transaction-detail queries, introspect input/output types before assuming datum availability. Some MKII deployments expose value on TransactionInput but not datum; use available relations or output-side fields rather than querying nonexistent input fields.
  • For P2P DeFi transaction classification, fetch only body-backed evidence needed for proof: inputs, outputs, values/tokens, datums when exposed, included time, validity/contract evidence, and transaction hash/index identities.
  • Metadata must not be used as authoritative evidence for protocol action, price, amount, or ownership. It can be displayed only as untrusted metadata if the product explicitly wants that.
  • History views should avoid duplicate rows across wallet-return hints and chain-backed rows. Treat returned hashes, pending local entries, and provider-confirmed chain transactions as different evidence levels.
  • Preserve docs/STATE.md as the live validator/liquidity report format for mainnet/preprod validator hash, deployed UTxO, UTxO count, addresses, ADA locked, token counts, and token samples.

Design Principles

  • Keep categorization logic reusable, deterministic, and canonical.
  • Separate current live state from historical transaction history.
  • Treat bundled executions as bundles, not as multiple accidental single-row actions.
  • Use explicit semantic names for ownership concepts instead of overloading You or similar UI labels.
  • Keep new parsing and categorization code centralized in a few focused domain helpers rather than duplicated in each table.

Protocol Notes

  • One-way swap fill is permissionless and uses the swap/fill redeemer branch. Do not accidentally route fills through owner/stake-approved branches.
  • Close/update owner flows must use the owner stake key hash consistently.
  • There is a suspected close-intent or close-frontend-integration bug around a misinterpreted stake credential being used for the operation. Investigate the exact signer hash, owner stake hash, selected offer owner, and generated requiredSigners before changing shared protocol code.
  • Close intent debugging needs to treat mint and spend witnesses separately. In this repo the close fragment needs both beaconRedeemer for the mint witness and emptyRedeemer for the spend witness; removing either breaks the generated close script even if the other redeemer is correct. A beacon-policy trace like This redeemer can only be used to register the beacon script usually means the mint redeemer branch is wrong, not that the spending validator or address scope is wrong. Inspect the built JSON and generated cache paths when one witness fails, because the runtime error surface does not always identify which node is missing.
  • A missing cache.myAddress usually means the address was introduced at the wrong scope, not that getCurrentAddress was omitted. Fetch it once at the root and pass it into imported intents as a shared root value.
  • ADA uses GameChanger's coin convention: policyId: "ada" and either assetName: "ada" or assetNameHex: "ada".
  • GCScript/ISL does not have normal imperative conditionals. Existing normalization-map patterns are intentional and should be preserved unless a simpler protocol-safe approach is clearly available.
  • Service fee outputs are network-specific and reusable-data-driven. Metadata may mention fee type and amount, but execution correctness must come from the transaction body outputs.
  • utxo-ask-quantity is a required compatibility arg for fill/close value accounting and must default to "0" across providers, intent args, top-level wrappers, and Cart snapshots.
  • src/intents/lib/common.gcscript.jsonc may own shared selectors such as assetKind; swap and close own value movement. Do not put remainingADA, continuingAsk, or close return math in common.
  • Do not store role quantities in one asset-keyed object because ADA/tADA can collide as ada-ada.
  • Close of partially filled orders must return accumulated ask value.
  • Current open is not update; do not add consumed-UTxO preservation math to open.
  • Explain P2P DeFi UTxO contention explicitly in product copy when relevant: each open offer UTxO can be consumed by only one transaction, while many users may route against the same global order book in parallel. In NeonSoup-like clients without a backend batcher, the user device and wallet flow handle contention locally.
  • Bundle mode, best-effort parallel mode, and contention premium/pay-up routing are contention-management strategies, not AMM mechanics. Bundle mode favors atomicity and lower cost; parallel mode allows partial completion; pay-up mode may skip the cheapest contested orders for slightly worse local prices.
  • Cart-booked source UTxOs must be treated as unavailable for subsequent local routing. Excluding booked source refs must happen before quote/routing math and must affect available totals, route segments, slippage/impact, executable amounts, and generated cart items.
  • The Cart state should remain the single source of truth for booked source UTxOs. Avoid separate redundant booked-UTxO state that can diverge from Cart items.
  • Excluding booked UTxOs from routing should be equivalent to pre-filtering the order book. Do not change route math or route-bar semantics to compensate for exclusions.
  • Do not use developer-facing warning-threshold logic to disable user-facing CTAs unless the route is truly at or over the user maximum. Do not reintroduce old extreme-multiplier severity rules in the frontend.
  • Preserve route-bar segment semantics: normal fill, round-up/clean-execution remainder, maker remainder/change, and final unavailable input are separate concepts. Only truly unavailable input should use unavailable/striped treatment; clean-execution round-up and maker-remainder segments should not look like normal fills.

Protocol Migrations

  • The current protocol target is audited CardanoSwaps v1 one-way at commit 9ec41e7619f5ba9d3dd46dd194e2146098093721.
  • Protocol-version config, source intents, parser defaults, and generated artifacts must stay aligned while audited v1 is active.
  • Audited v1 one-way BeaconRedeemer uses constructor 0 for CreateOrCloseSwaps minting/burning and constructor 1 for UpdateSwaps staking. Open and close mint/burn beacons, so their beacon mint consumer must use constructor 0; constructor 1 fails with Redeemer not used with staking execution.
  • Audited v1 one-way SwapDatum has one trailing option field after price: prev_input. Open offers set it to None; fill/swap continuing outputs set it to Some(input_ref). v1 datums must not include the old extra v2-era trailing option field.
  • While the active deployment is v1, keep src/intents/lib/swap.gcscript.jsonc with the v1 continuing datum fields in place. The old v2-only extra trailing option field should stay commented out as an upgrade note, not selected by a runtime multi-protocol switch unless there is a coordinated migration.
  • Before switching back to a v2 or other validator set, re-check the source for BeaconRedeemer, SwapRedeemer, and SwapDatum constructor/field ordering, then update validatorInfo.protocolVersion, uncomment or change datum fields in src/intents/lib/open.gcscript.jsonc and src/intents/lib/swap.gcscript.jsonc, update src/intents/lib/close.gcscript.jsonc if needed, and rebuild any generated intent artifacts together.
  • When changing protocol versions, verify reference script UTxOs live per network and record both configured scriptSize and live MKII serialisedSize if they differ.

Debugging

When wallet execution fails, ask for or inspect:

  • final generated intent JSON;
  • exact args passed to the intent;
  • GameChanger wallet error payload and Plutus traces;
  • wallet memory/context dump;
  • generated tx inputs, outputs, mints, witnesses, datum hex, redeemer hex, and build index maps.

The wallet context dump usually mirrors the GCScript tree: root run keys become top-level cache entries, nested scripts keep nested cache, and macro-returned tx fragments show what reached buildTx.