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.
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 fromsrc/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/commonservices/adapters or shell-level adapters: popup/window behavior, return URL cleanup,localStorage,history.replaceState, URL param decoding, and wallet launch.
src/intents/*.gcscript.jsoncare top-level GameChanger Wallet intents.src/intents/lib/*.gcscript.jsonccontains composable intent fragments.src/intents/lib/common.gcscript.jsoncis 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/**andsrc/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/**orsrc/common/**must include matching updates in all consuming shells, especiallysrc/frontend/andsrc/devtool/. dist/index.htmlis generated by the landing-page build. Treatdist/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.jsonare built artifacts from the source intents.dist/is generated and ignored. Do not hand-edit deploy-critical files indist/; root deploy files must come from source files and build scripts.- Root landing deploy fallbacks are sourced from
src/landing/_redirectsandsrc/landing/.htaccess, then copied byscripts/build-landing.mjs. - Standalone shell deploy fallbacks live in
src/frontend/public/_redirectsandsrc/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/assetsand are copied todist/assetsby 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. Generateddist/intents/*.gcscript.jsonfiles may be rebuilt freely from unchanged sources. - Provider defaults are read through shared app config from Vite env variables.
Use
.env.exampleas the committed template and keep private.envfiles 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.
- 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
getTransactionscan supplyvalid_contract, inputs, outputs, token beacons, and parsed datumpreviousInputevidence. - 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.
- Use
rangeon 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.operationsbefore 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_byor filter shapes. - MKII open-offer normalization must hydrate
previousInputsource 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#indexfor UTxOs andpolicyId.assetNameHexfor 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
ChainTransactionhas 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.
- Build
Swapas 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.
- 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 canonicalpolicyId.assetNameHexasset keys. - Use
minExecutableOfferQuantityfor book-level open-order filtering andminMakerRemainderQuantityfor route-boundary remainder decisions. Both are base-unit integer strings;0disables 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, andSwapactions while the app layer handles price discovery and protocol-specific routing.
- 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:openpnpm run build:closepnpm 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.
- 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
Swapfeature 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.
- 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/assetsby hand when the raw source inassets/is the intended source of truth; regenerate viapnpm run assets:optimize. - Use
scripts/asset-policy.mjsas 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.pngandassets/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/assetsor/assetspaths. Avoid root-level or stale images such asbanner.jpg, rootlogo.png, rooticon.png, or rootfavicon.*. - 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, andVITE_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
gcWalletUrlPatternvalue. Route all wallet launches through the centralizedgcWallet.tsoption 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.jsonversion +VITE_NEONSOUP_BUILD_TAG, for example0.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
plutusDatafail beforebuildTxcan 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, orfinallystructures. Pass those values through GCScriptargs, propagate args explicitly into nested scripts, and resolve them with ISLget('args...'); useget('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.
isolateCacheis 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 likecache.myAddressorcache.intents.<index>.... Example: the cart bundling bug came from wrapping each group in its ownscript, which moved the wallet address out of the expected root cache. - Fetch
getCurrentAddressonce at the cart root and pass it through as a shared root value. Do not re-fetch it inside per-group wrappers. Example: themyAddressissue in cart execution was not thatgetCurrentAddresswas missing, but that it was introduced at the wrong scope, so imported intents could not reliably resolveoffer-addressagainstcache.myAddress. - Before changing close logic, verify the exact
txHash#indexagainst 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#0for beacons and#1for 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
bigintin app/domain code and GCScript/ISL*BigNumhelpers in wallet scripts. Do not cast these values tonumber/normal integers for math; only convert tonumberafter 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 asada.ada; native assets with an empty asset name are keyed aspolicyId.. Do not use friendly aliases such asusdmas 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
assetIdfor provider/GC asset identifiers such aslovelaceorpolicyId + 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
Youbadges 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.
- 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.myAddressout 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.mdfirst. - 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
scrollWidthand 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 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:
dangerfor errors,warningfor risky or invalid-but-allowed input,successfor completed actions, andinfofor 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.argsis 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
submitTxswithextras: trueandnoFail: true, then reconcile fromtxsExtendedinstead 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.
- 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.
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.
Swapis the primary user action.Offermeans 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
%oractions. - 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,
OfferandSwapshould 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 balancecolumn. 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...1234with 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/assetsexposed at/assetsby dev/build plumbing. Keep contextualUI_ASSETSmappings 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-heroandswap-heroare 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
scrollWidthand 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 attolerance, block zero tolerance, and block tolerances at or above the configured maximum.
- 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 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.
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.
- When using MKII API for transaction-detail queries, introspect input/output
types before assuming datum availability. Some MKII deployments expose
valueonTransactionInputbut notdatum; 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.mdas the live validator/liquidity report format for mainnet/preprod validator hash, deployed UTxO, UTxO count, addresses, ADA locked, token counts, and token samples.
- 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
Youor similar UI labels. - Keep new parsing and categorization code centralized in a few focused domain helpers rather than duplicated in each table.
- 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
requiredSignersbefore changing shared protocol code. - Close intent debugging needs to treat mint and spend witnesses separately. In
this repo the close fragment needs both
beaconRedeemerfor the mint witness andemptyRedeemerfor the spend witness; removing either breaks the generated close script even if the other redeemer is correct. A beacon-policy trace likeThis redeemer can only be used to register the beacon scriptusually 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.myAddressusually means the address was introduced at the wrong scope, not thatgetCurrentAddresswas 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 eitherassetName: "ada"orassetNameHex: "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-quantityis 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.jsoncmay own shared selectors such asassetKind; swap and close own value movement. Do not putremainingADA,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.
- 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
BeaconRedeemeruses constructor0forCreateOrCloseSwapsminting/burning and constructor1forUpdateSwapsstaking. Open and close mint/burn beacons, so their beacon mint consumer must use constructor0; constructor1fails withRedeemer not used with staking execution. - Audited v1 one-way
SwapDatumhas one trailing option field after price:prev_input. Open offers set it toNone; fill/swap continuing outputs set it toSome(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.jsoncwith 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, andSwapDatumconstructor/field ordering, then updatevalidatorInfo.protocolVersion, uncomment or change datum fields insrc/intents/lib/open.gcscript.jsoncandsrc/intents/lib/swap.gcscript.jsonc, updatesrc/intents/lib/close.gcscript.jsoncif needed, and rebuild any generated intent artifacts together. - When changing protocol versions, verify reference script UTxOs live per
network and record both configured
scriptSizeand live MKIIserialisedSizeif they differ.
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.