typescript 7.0.2 is the native compiler and exports no JavaScript API, so
next 15.5 cannot read it — it dies in writeConfigurationDefaults. It comes in
aliased as tsgo and the typecheck script calls it; typescript stays on the 6
line for next. 0 diagnostics after, identical under both compilers; the one
baseline diagnostic was an unresolved @hanzo/event/react that the install fixed.
Co-authored-by: Hanzo Dev <[email protected]>
One label per runner, spelled <os>-<arch>. The aliases these jobs named
were removed, and a job naming a label no runner carries waits instead of
failing.
Co-authored-by: Hanzo Dev <[email protected]>
One label per runner, spelled <os>-<arch>. The aliases these jobs named
were removed, and a job naming a label no runner carries waits instead of
failing.
Co-authored-by: Hanzo Dev <[email protected]>
One label per runner, spelled <os>-<arch>. The aliases these jobs named
were removed, and a job naming a label no runner carries waits instead of
failing.
Co-authored-by: Hanzo Dev <[email protected]>
Ranges drift once nothing moves them: a caret that has not been rewritten
since it was written pins whatever was current that day, and an exact pin
cannot even take a patch. Both are updated here.
One line per package, so a surface reads the same components, the same
tokens and the same face as every other one.
The hook decided whether to make a request and the read decided which rows to
ask about, and both spelled the same question out for themselves. `unnamed`
answers it in one place, so the two cannot drift apart.
Co-authored-by: Hanzo Dev <[email protected]>
The id was recovered only from the chain's own Transfer logs, so the field the
indexer is about to start writing would have landed unused and every screen
would have kept paying for eth_getLogs to learn what it was already being told.
The indexer declares the id in two places on a transfer — token_id on the row
and token_id inside total — and neither is populated yet. Both are read, in
that order, the way the indexer itself falls through column spellings. idFor
asks the row before the log join, so one function answers "which item moved"
for every screen and nothing above it knows which source replied. The log read
is asked only about rows that arrive unnamed and does not run when there are
none, so it retires itself rather than being switched off.
/tokens/{addr}/instances is read for the same reason. It answers 200 and empty
for every collection today, so the collection page still recovers ids from one
page of transfers and says so — "50 of 149 items", not a slice presented as a
collection. When the resource fills, that list is preferred, whole, and carries
each item's own name and image; the item page takes its metadata from the same
record instead of fetching a gateway URI.
Verified against a mocked indexer carrying token_id: the ids render with the
chain RPC blocked entirely.
Co-authored-by: Hanzo Dev <[email protected]>
The import had no collection behind it. There are two, both live: Lux Genesis
(LUXNFT, 50 tokens, 28 holders) on Ethereum mainnet, and CryptoZoo (ANML, 5840
tokens, 2676 holders) on BSC. Every figure enumerated over eth_call today, with
the command beside it.
Records what an import must carry that a snapshot alone does not: lux.town has
lost its delegation, so all 100 Ethereum URIs are dead; the video is recovered
on cdn.lux.network but the metadata was a dynamic route and needs a renderer,
not a rehost. The Media contract cannot be repaired on Ethereum — only the 28
holders can rewrite their own tokens.
Weighs the three routes. The bridge corridor's contract half exists and its Go
half does not; the snapshot-and-mint is already planned and verified but leaves
the Ethereum originals alive, which the record states rather than hides.
Co-authored-by: Hanzo Dev <[email protected]>
`trailingSlash` made the router report `/support/` for a link written
`/support`, so comparing the two raw strings matched nothing and every item in
the support nav read inactive on its own page.
One function normalises both sides, and it matches exactly rather than by
prefix. Every link in both navs is a leaf, and a prefix match lights the wrong
ones — `/support` under `/support/projects`, and `/launch` under `/launches`.
Co-authored-by: Hanzo Dev <[email protected]>
The indexer's holders resource serves one row per HOLDING. On an ERC-20 that
is one row per holder; on a collection it is one row per held item, so Lux
Genesis came back as three rows for two addresses and the Uniswap position
manager as fifty rows that are all the same address. The site printed "3
holders" and "73 holders" and drew one address fifty times.
explorer.holders() folds the rows by address, which is what makes the answer
countable. The collection page counts the folded list and reads 2 for Genesis,
which agrees with ownerOf. The resource pages at 50 and reports no next page on
the page that fills, so a full page says "at least" rather than a flat count.
holders_count arrives as Token.holdings, named for what it counts, and only a
screen that knows the standard prints it.
Nothing is ranked any more. Collection cards carried a position, 1 through 10,
over the order the indexer happens to answer in — which is by holder count — on
a site whose own launches page says a default that puts one project on top is a
recommendation it is not entitled to make. Collections arrive by name.
Also read against the chain and corrected: /launches said the indexer reports
decimals as 0 for Zoo tokens that carry 18. Those contracts return 0 from
decimals() themselves, so the claim was false and the reason for omitting
supply is the real one. /support/projects claimed collection-level data was
complete, including holder count and full transfer history; it is a page of
transfers and a holdings count, and now says so.
An application no longer requires a session to compose. A filled-in form
produced nothing at all without Hanzo IAM, directly under a paragraph saying
signing in was not a condition of applying. Signed in it names the applicant;
signed out the record says the reply goes to the address it arrives from.
/item threw during render on ?id=abc and took the page to a blank screen.
Holdings drew a chain whose read had FAILED as no holdings at all. usd()
rounded a pool holding forty cents to $0, the same thing an empty pool prints.
The header lit both Launches and Apply on /launches, on a shared prefix.
The launches table read the indexer itself, with its own token type and its own
fetch; it uses lib/explorer like every other screen.
trailingSlash, because the export wrote support.html beside a support/
directory and a host that prefers the directory served a listing where the page
belongs. Every route is now its own index.html and needs no rewrite rules.
Removed from public/: reservoir's logos, 80 chain icons for chains Lux does not
run, ten foreign chain logos, and og-image.png — a Reservoir Explorer marketing
card advertising Bored Ape floor prices, sitting in the social-preview slot of a
site whose whole point is that it quotes no prices. The two radix packages whose
only consumers were the deleted listing dialogs go with them.
Co-authored-by: Hanzo Dev <[email protected]>
formatDate, formatNumber, formatCurrency, formatPercent, isAddress,
debounce and absoluteUrl had no callers left after the rebuild. cn and
shortenAddress do.
Co-authored-by: Hanzo Dev <[email protected]>
ERC-2981 quotes an amount against a sale price rather than publishing a
rate, so the rate is read by asking for one whole token and dividing.
Doing that in floating point printed 250 basis points as
2.4999999999999996%. rateLabel scales by 10,000 in bigint first, so the
integer the contract stores comes back out intact.
Co-authored-by: Hanzo Dev <[email protected]>
The market advertised a marketplace it does not have and could not open
a single collection. Every card linked to /collection/{chainId}/{address}
through a field the indexer does not send, into a route that did not
exist; the homepage printed four Genesis tiers nothing had read and named
Seaport and LSSVM as its protocols, neither of which is deployed on any
Lux chain; the activity feed rendered forty-five identical rows of
"NFT #?".
WHICH CHAINS EXIST IS NOW READ, NOT DECLARED. lib/chains.ts held a list
that had drifted twice — it missed Osage and it offered SPC, whose RPC
404s and which the indexer's registry has never served, and it pointed
Pars at an /ext/ path that 404s. All of it is gone. readChains() reads
/v1/explorer/admin/chains and every screen carries a whole chain record:
slug for the indexer, id for a contract, coin for a label, explorer for a
link out. Two things the registry does not hand over cleanly are handled
once, in one place: its `rpc` is the indexer's own in-cluster route and
the browser's arrives as `public_rpc`, and its order is a Go map's, so
the switcher sorts rather than reshuffling on every load.
THE ITEM ID IS RECOVERED FROM THE CHAIN. The indexer writes the ERC-20
shape into every ERC-721 transfer and discards topics[3], which is why
the feed had nothing to distinguish one row from the next. Its log_index
is the EVM log index, so (block, logIndex) joins a row to its log
exactly; lib/logs.ts does that join in bounded windows and rows outside
the budget say the id was not read. Forty-five anonymous rows are now
forty-five named items.
/genesis WAS PROBING A CONTRACT WITH NO CODE. The collection is alive at
0x9e04fc57.. and answers everything: three minted, three billion LUX
bonded, a 2.5% royalty declared through ERC-2981, and a `market` address
of zero, which is the collection saying nothing is wired to sell it. It
also keeps a record per token, the only per-item metadata on any Lux
chain, so each token shows its real name, tier and bonded amount.
NEW: /collection and /item, the two routes a marketplace cannot be
without. They take the chain and address in the query because a static
export cannot render a path segment for a contract deployed after the
build. /pools reports the AMM — 32 pools on Lux, 20 on Zoo — with totals
read from the factory entity rather than summed over a partial page.
A footer reaches applying, support, terms and privacy from every page.
GONE: ListingForm, OfferForm, TraitFilter, the IMarket ABI and the LSSVM
pair ABI. Market.sol is deployed nowhere, so a listing form against it is
a button that lies. They come back when an address does.
Failed reads now throw instead of resolving to an empty page, because a
list cannot otherwise tell "there are none" from "we could not ask" — and
those mean opposite things. Every screen names the chain it read.
Co-authored-by: Hanzo Dev <[email protected]>
@hanzo/gui writes its own tokens at runtime — `:root.t_dark { --color: …;
--background: … }` plus `body { background: var(--background); color:
var(--color) }` inside a prefers-color-scheme block. `--background` is a
name both it and @luxfi/ui/tokens.css use, for different things, and the
injected rule is later in the cascade and more specific, so bg-background
resolved to hsla(0,0%,97%,1) on a page whose cards were #0A0A0A. Card
text measured 1.03:1 against its own card; AA wants 4.5:1.
Read the ground from `--color-bg-body` instead — the token set's own name
for the page ground, which nothing else defines and which tracks the same
rungs `--background` does, n-0 in light and n-1000 in dark. A rename at
the point of use, not a second opinion about the colour. Measured after:
body 20.12:1, card 18.97:1.
Co-authored-by: Hanzo Dev <[email protected]>
Next 15.5.22 reads tsconfig paths and typechecks through the TypeScript
JavaScript compiler API. TS 7's native compiler does not expose it, so
`next dev` refused to start, `next build` could not resolve the `@/*`
alias declared in tsconfig.json, and next.config.ts failed to load at all
(the comment in next.config.mjs records the third symptom).
6.0.3 is the newest release Next can drive. Dev server, build and alias
resolution all come back with it.
Co-authored-by: Hanzo Dev <[email protected]>
It calls hanzoai/.github/.github/workflows/workflow-sanity.yml@main, and that
file does not exist — the repo holds docker-build, platform-build and promote
and nothing else. Every push has opened a run that fails before a step, and
this repo now has one pipeline address, in .hanzo/workflows/cicd.yml.
publish.yml computed and pushed a version tag on ubuntu-latest; the two
sync-*-branches jobs auto-merged dev into staging, polygon and vpn on
ubuntu-latest, a branch model that came with the fork and that this repo does
not use. The image build is untouched — docker.yml on lux-build and
release-matrix.yml on the luxfi-* pools both stay.
publish.yml computed and pushed a version tag on ubuntu-latest; the two
sync-*-branches jobs auto-merged dev into staging, polygon and vpn on
ubuntu-latest, a branch model that came with the fork and that this repo does
not use. The image build is untouched — docker.yml on lux-build and
release-matrix.yml on the luxfi-* pools both stay.
publish.yml computed and pushed a version tag on ubuntu-latest; the two
sync-*-branches jobs auto-merged dev into staging, polygon and vpn on
ubuntu-latest, a branch model that came with the fork and that this repo does
not use. The image build is untouched — docker.yml on lux-build and
release-matrix.yml on the luxfi-* pools both stay.
The marketplace shipped with no telemetry of any kind — no pageviews, no
sessions, no errors — so there was no way to say whether anyone uses it, or
which collection they came for. It is one of seven Lux surfaces going dark in
the same way, and all seven report through @hanzo/event, which posts to our own
door at api.hanzo.ai and to nobody else. A marketplace's traffic is not a thing
to hand to a third party, so no tag manager and no vendor script.
The provider wraps the client tree at the root, which is what makes every route
counted from one place. It sits outside the dynamically loaded wallet and
account providers on purpose: that chunk carries wagmi, IAM and the gui engine,
and a visitor whose wallet never arrives is exactly the visitor worth hearing
about. The provider counts the first load and usePageview counts every
navigation after it, so a single-page app stops looking like one visit that
never moves.
The ingest key is publishable — it attributes a write and can read nothing, so
it belongs in the bundle the way a public address does. An env override lets a
deploy swap it; the literal is what makes the site report today, since the
image is built with no arguments.
Co-authored-by: Hanzo Dev <[email protected]>
Every docker.yml run in this repo has died at 0s with "workflow file issue"
and zero jobs created, through two different reusable targets
(hanzoai/.github, then luxfi/.github). The YAML was never the problem.
luxfi/market is a PUBLIC repository; luxfi/.github and hanzoai/.github are
both PRIVATE. GitHub will not resolve a private reusable workflow for a
public caller and fails the run before any job starts. Positive control:
luxfi/finance is private, and the byte-identical call starts and builds.
workflow_dispatch cannot be used to probe this — the dispatch API answers
422 "Actions has been disabled for this user" — so the tag push is the only
trigger, and the only signal, available.
So the build is inline: buildx on the luxfi ARC scale set (runs-on takes the
SET NAME as its label), GITHUB_TOKEN login to ghcr.io, one linux/amd64 image
pushed as :<tag-version> and :latest. That unblocks shipping the /genesis
live read, which has been stuck behind this since 1.0.31 on 2026-06-25.
Co-authored-by: Hanzo Dev <[email protected]>
/genesis reads totalMinted() and totalLuxLocked() live and says so when the
contract does not answer. That landed in 9b7c25f. It has never reached a
browser: a page-load trace of https://app.lux.market/genesis right now still
renders the hard-coded tier table —
Genesis Permanently Locked 1,000,000,000 LUX Max Supply: 10
Validator Permanently Locked 100,000,000 LUX Max Supply: 100
Mini Permanently Locked 10,000,000 LUX Max Supply: 1,000
Nano Permanently Locked 1,000,000 LUX Max Supply: 10,000
— 40B LUX of "permanently locked" behind 0x004287c4..76c6, which has NO CODE:
eth_getCode on https://api.lux.network/v1/bc/C/rpc returns "0x" at block
1098195, positive controls WLUX 0x4888E4a2.. -> 3462 hex chars and Multicall3
0xd25F88CB.. -> 2592.
The live pods run ghcr.io/luxfi/market:1.0.31-amd64, built 2026-06-25. Tags
v1.0.32 through v1.1.4 produced no image at all: docker.yml carried a
top-level `permissions:` block alongside `secrets: inherit` on a `uses:`,
which fails the run at 0s with "workflow file issue" before any job starts —
and it targeted `["self-hosted","linux","amd64"]`, a label array that names
hanzoai-org scale sets not visible from luxfi. Both fixed by using the shape
that is actually building luxfi/finance: same-org reusable, no permissions
block, ARC scale-set name as a scalar label.
- next.config.ts -> next.config.mjs: Next 15's TS config loader calls
ts.sys.fileExists off the app's own typescript, which TypeScript 7 does not
export, so `next build` died before the first request. A config file that
annotates one object literal does not need types.
- next 15.5.0 -> 15.5.22, @luxfi/ui 7.4.9 -> 7.4.10 (patch only).
Co-authored-by: Hanzo Dev <[email protected]>
TypeScript 7 is the native Go compiler; the npm package is a thin shim
that resolves a platform-specific native binary.
Safe here because the build does not emit through tsc — the bundler does,
so tsc is typecheck-only and the built artifact is unaffected. Gated on a
measured comparison of both compilers against this same config: TS7
introduces no errors TS 5.9 did not already report.
typescript now ^7.0.2
Co-authored-by: Hanzo Dev <[email protected]>
CHAIN_INFO carried a `color` per chain — five hexes lifted wholesale from
GitHub's dark-syntax palette: #7ee787 (entity green), #d2a8ff (entity),
#ffa657 (variable — the literal tan the owner called out), #ff7b72 (keyword),
plus a cyan #55ccff for Lux itself. They were painted as inline
`style={{ color }}` in seven places on a surface whose every other pixel is
achromatic, and an inline hex is unreachable by white-label-by-hostname no
matter what value it holds.
Deleted rather than re-tokenised: the field existed to make five chains
distinguishable and the chain NAME already does that. The active chip in the
header was `text-black` over that hex, which stopped meeting contrast the
moment the fill stopped being a light pastel — it is the accent pair
(bg-primary/text-primary-foreground) now, which flips correctly with the mode.
@luxfi/ui 7.4.5 -> 7.4.9, which is also what un-breaks this app: two physical
copies of @tanstack/react-query left it rendering "Application error: a
client-side exception has occurred" with "No QueryClient set, use
QueryClientProvider to set one" in the console, on every route.
After: warm 0%, tinted-neutral 0.009%, mean chroma 0.448 at 1440x900 and
390x844, light and dark.
Co-authored-by: Hanzo Dev <[email protected]>
The header offered "Sign in with lux.id" and the IdP sent the user back to
https://lux.market/auth/callback — a route that did not exist, so the login
could START and never FINISH. Next answered 404 and the code was dropped.
Same fix as the duplication above it: `@luxfi/ui/[email protected]` owns the flow, so
the route is `<AuthCallback/>` and providers.tsx is `<AuthProvider>` instead of
a verbatim copy of lux/mpc/dashboard's `configureIam` + `startLogin` + `logout`.
The market has nothing of its own to do with a fresh session.
`tsc --noEmit` clean.
Co-authored-by: Hanzo Dev <[email protected]>
TypeScript 7 reports TS2882 for a stylesheet side-effect import with no type
declaration; TS 5.x accepted them silently. Bundlers resolve stylesheets through
their own loaders, so the import never reaches the TypeScript module resolver —
this declares them legitimate rather than giving them a shape.
MEASURED, not assumed: this repo reports TS2882 under tsc 7 without the file and
zero with it. Repos where a framework already ships ambient CSS declarations
(Next's next-env.d.ts) were left alone — the declaration is inert there and was
not added.
Co-authored-by: Hanzo Dev <[email protected]>
Ten files imported `lucide-react` directly. The vintage happened to match what
@luxfi/ui resolves today, but a direct dependency is a second place the version
can move, and it has moved before — the surfaces ranged over 0.312, 0.474, 0.577,
1.26 and 1.27, which are different drawings under the same names. `Search` at
0.312 and `Search` at 1.27 are not the same glyph.
So the dependency goes and the import path becomes `@luxfi/ui/icons`, which is
Lucide as @hanzo/gui standardises on it. One package resolves the set, for every
Lux surface, and drift stops being expressible here.
Import-path change only — `git diff src` is ten single-line edits. `next build`
is clean, 6 routes prerendered.
Not screenshotted: this surface does not currently render past hydration. It
throws "No QueryClient set, use QueryClientProvider to set one" from wagmi, in
50f5b65 (refactor(market): one chrome — a wallet is not an account), which this
change does not touch. Same error, same 127-byte body, with these ten edits
reverted.
Co-authored-by: Hanzo Dev <[email protected]>
lux.market had a header of its own: its own sticky bar, its own link styling, a
nav with no phone presentation at all, and a Connect button standing in for an
account menu the surface did not have. Two identities meet here and they are not
the same thing — the WALLET signs a listing, the ACCOUNT is who you are and
which org you act in — and only the first existed.
The chrome is now `AppNav` from @luxfi/ui: brand, org switcher, links, settings
and the user menu, identical to the MPC dashboard and the bootnode console, with
the links collapsing into the same gui Sheet the switchers use. The chain
selector and the wallet button stay, in the nav's free slot, because those are
this surface's own controls and not chrome.
Identity comes from IdentityProvider (IAM); brand, theme, issuer and IAM client
come from the Host header, so lux.market and a zoo-branded market are one image.
The gui engine's bundler wiring comes from `withLuxUi`, not a hand-rolled
transpile list.
`next build` passes: 6 static routes exported.
Co-authored-by: Hanzo Dev <[email protected]>
The page rendered four hard-coded tiers claiming 10x1B + 100x100M +
1000x10M + 10000x1M LUX "permanently locked", plus per-tier "10x/5x/2x/1x
rewards". None of it came from anywhere:
- The collection 0x004287c47efc912fec391979154454a8017a76c6 has NO CODE at
head. Measured on https://api.lux.network/v1/bc/C/rpc at block 1098191:
eth_getCode -> "0x", totalSupply() -> "0x" (revert). Positive control
WLUX 0x4888e4a2.. -> 3462 hex chars and a live totalSupply.
- The supplies contradicted the tokenomics reference: NFT-COLLECTION.md
has Genesis Validator count 100 (not 10) and records that the
Validator/Mini/Nano hierarchy is "tokenomics-designed but not issued".
- GenesisNFTs.sol has no reward-multiplier concept at all. The 10x/5x/2x/1x
figures were invented outright.
Now the page calls totalMinted() and totalLuxLocked() — both real reads,
GenesisNFTs.sol:829 and :214 — and renders whatever chain returns. When the
call reverts, as it does today, it says supply and bonded LUX are
unavailable and names the address, rather than substituting a number.
Also drops src/lib/wagmi.ts: a second createConfig that nothing imported,
shadowing the live one in app/providers.tsx.
Co-authored-by: Hanzo Dev <[email protected]>
lux.market drew Lucide at 0.511 while the console drew it at 1.27 and lux.vote
at 0.312. Same set, three vintages — and Lucide redrew a lot between 0.3x and
1.x, so the same named glyph came out a different shape depending on which
surface you were looking at. One vintage now, matching the console and matching
the geometry the explorer's sprite is generated from.
All eleven glyphs the marketplace uses (ArrowRight, Check, ChevronDown,
ChevronRight, ChevronUp, Circle, ExternalLink, LogOut, Search, Wallet, X) exist
under the same names in 1.27; `next build` exports all six routes clean.
Co-authored-by: Hanzo Dev <[email protected]>
app.lux.market rendered "No NFT collections found on Lux yet" unconditionally:
src/hooks/useNFTData.ts and src/lib/explorer.ts returned {items:[]} literals, so
no request was ever made. The URLs they would have used were dead anyway.
Measured against live mainnet 96369 and fixed:
- lib/chains.ts: EXPLORER_API pointed at https://explore.lux.network/api/v2 —
404 (that host serves the Next.js frontend, not an API). The live read API is
the unified indexer at https://api-explore.lux.network/v1/indexer/<slug>,
slugs from its own /v1/explorer/admin/chains. CORS is `*`.
- lib/chains.ts: all five rpcUrls 404'd (`/mainnet/ext/bc/...`). Public form is
`/v1/bc/C/rpc`; the four reachable ones now verified via eth_chainId. SPC has
no public RPC and is marked as such.
- lib/chains.ts: Pars was chainId 17471; it is 494949 (eth_chainId 0x78d65).
- lib/chains.ts: CHAIN_INFO gained `color`, read by three pages that had been
typing it as a missing property.
- lib/explorer.ts: real typed fetchers, one data-access layer. The indexer
ignores `?type=`, so the ERC-721/ERC-1155 filter is applied client-side.
- hooks/useNFTData.ts: thin react-query wrappers over those fetchers.
- app/activity: dropped its private copy of the fetch (used `tx_hash`; the API
field is `transaction_hash`) in favour of the shared hook.
- ActivityRow: derives the explorer URL from the chain object instead of string
surgery on the API base.
- contracts.ts: MARKET_ABI was `[]`. Filled from the real interface at
standard/contracts/nft/Market.sol:37-48. ERC721_ABI gained the `approve` entry
ListingForm calls.
- ListingForm/OfferForm addressed `contracts.markets`; the field is `market`.
- OfferForm sent paymentToken=address(0) plus native `value`. makeOffer is
non-payable and reverts on address(0) (Market.sol:418) — every offer would
have reverted. Now denominated in WLUX, verified on-chain (1730 bytes, 18dp).
- next.config.ts: ignoreBuildErrors was hiding all 40 of the above type errors.
Turned off; `pnpm build` is green with checking on.
CONTRACTS stays empty: Market.sol is deployed on no Lux chain. The recorded
mainnet address (addresses.json markets/Markets 0xefba7dbf…) has no code — so
list/buy/offer remain unreachable by design until it is redeployed.
Co-authored-by: Hanzo Dev <[email protected]>
Replaces non-canonical scale-set / org-prefixed labels with the
existing labels every arcd host registers with. Matches evo for
amd64 and spark for arm64. No new labels added.
Was using canonical workflow defaults (hanzoai-scoped labels) which
do not dispatch for luxfi org. Switched to round-robin pool labels:
runner-amd64: luxfi-amd64 (evo)
runner-arm64: luxfi-arm64 (spark + dbc, round-robin)
runner-deploy: lux-deploy-linux-amd64 (DO ARC pool)
Project already had pnpm-lock.yaml; yarn.lock was stale. Pinning the
packageManager field locks the local toolchain to pnpm so commit
hooks and CI agree.
- Replace inline chain/contract/hook implementations with @luxfi/* imports
- All 6 lib/hook files now thin re-exports from shared packages
- Standalone market at github.com/luxfi/market (removed from exchange monorepo)
Removed searchParams usage that forces dynamic rendering. For static
export (GitHub Pages), server-side searchParams is not available.
Client components now use useSearchParams hook where needed.
- Converted from pnpm workspace catalog references to fixed versions
- Added GitHub Actions workflow for GitHub Pages deployment
- Configured for static site export