100 Commits
Author SHA1 Message Date
zeekay 2c62ec960f SQLite without shared cache, so the explorer's reads cannot fail the indexer's writes
In shared-cache mode SQLite locks per table and answers a blocked write with
SQLITE_LOCKED, which busy_timeout does not wait out. The block write then
failed outright and every other write in indexBlock was dropped unseen.
2026-09-23 05:39:28 -07:00
zeekayandHanzo Dev 7c86195752 Report average block time in milliseconds, as the explorer page reads it
The page divides average_block_time by 1000, per Blockscout's /stats contract.
Seconds made every explorer show 0.0s.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-22 23:38:43 -07:00
zeekayandHanzo Dev f28079bd70 Build on Go 1.27
Moved the go directive to 1.27.1. go build ./... and go vet ./... are clean.
The e2e suite still fails in BeforeSuite because no local node is reachable; it
fails identically with this change reverted.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 13:34:58 -07:00
zeekayandHanzo Dev 05fdd58377 Typecheck on the native compiler, keep 6 for the tools
typescript 7.0.2 is the native compiler and exports no JavaScript API, and
next below 16.3 wants typescript/lib/typescript.js, which the native compiler
does not ship: the check fails and next shells out to `pnpm add typescript`
mid-build, which a frozen lockfile or an offline runner turns into a failed
build. So 7.0.2 comes in aliased as tsgo and does the typechecking, and
typescript stays on the 6 line for next. The docs site: 10 diagnostics before and after.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 00:39:33 -07:00
zeekayandHanzo Dev 5e4e7ae274 Ignore node_modules
The explorer app and the docs site install into the tree and nothing ignored
them, so an install left them stageable.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 00:27:01 -07:00
zeekayandHanzo Dev bda095be28 Compile the explorer app with TypeScript 7
typescript 7.0.2 is the native compiler, so this is a pin and not a second
toolchain. explorer/app (@luxfi/explorer-app, new typecheck script, tsc
--noEmit): 0 diagnostics before and after. Its lockfile was missing and is
committed with the pin.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 00:25:44 -07:00
zeekayandHanzo Dev 77e3759c2f Compile with TypeScript 7
typescript 7.0.2 is the native compiler, so this is a pin and not a second
toolchain. The docs site typecheck (new script, tsc --noEmit): 10 diagnostics
before and after. TS7 no longer resolves a bare CSS side-effect import, so
css.d.ts declares '*.css'; that is free on the old compiler.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 00:19:50 -07:00
zeekayandHanzo Dev 2d0c1c229f docs: describe hanzoai/base as an application framework
Co-authored-by: Hanzo Dev <[email protected]>
2026-09-11 00:31:17 -07:00
zeekayandHanzo Dev f0ef4624cc go 1.26.8
One toolchain across the estate. The tree carried 1.26.0, .1, .3, .4 and .5
at once, so which compiler built a binary depended on which repo it came
from.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 23:58:21 -07:00
zeekayandHanzo Dev f8907ec5de run on nothing but the binary
alpine carried a shell, a package manager, libc and sqlite-libs so that a
dynamically linked binary could find them. Linking statically removes the
reason for all four: the image is the binary and the TLS roots, on
distroless static, as nonroot.

The explorer serves its frontend from //go:embed, so nothing else was ever
needed at runtime.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 23:21:59 -07:00
zeekayandHanzo Dev 7a4566d4b3 chain letters are lowercase
The primary-network chains answer to either case; one spelling beats two.
/v1/chain/c/rpc, /v1/chain/p, /v1/chain/x across the estate.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 15:21:19 -07:00
zeekayandHanzo Dev 7e198c5359 ci: a runner label names the platform, not the org
hanzo-build-linux-amd64, lux-build-linux-amd64, zoo-build-linux-amd64 and
every other org-prefixed spelling all meant the same machine. Which org a
job belongs to does not change which host can build it, so the org bought
one pool name per org per arch and each could go stale on its own -- the
evo host alone answered to fourteen names for one label.

One vocabulary now: linux-amd64, linux-arm64, macos-arm64, win-amd64. The
generic labels every plain workflow uses (ubuntu-latest, self-hosted) are
untouched, so naive CI keeps working.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 12:09:44 -07:00
zeekayandHanzo Dev 2177ed3a0f Read transactions by block, so an imported chain keeps its history
A transaction hash resolves through an index the node builds as it accepts
transactions, not as it replays them. A chain restored from an RLP import
therefore serves blocks and receipts while eth_getTransactionByHash and
eth_getTransactionReceipt answer null for all of it.

The ingest path asked by hash, so it lost every transaction it read. Worse,
a null receipt unmarshalled into an empty struct rather than a miss, and the
empty transaction that fell out was written as a row keyed by the empty
hash — which every later miss then merged into. 220 transactions and 379
logs became 3 rows and 2.

Ask by block number instead. A block carries what the sender submitted, and
eth_getBlockReceipts carries what running it produced; together they are the
whole transaction, and neither needs the hash index. It also costs two round
trips per block where the old path cost two per transaction.

One receipt parser now serves both the block-keyed and hash-keyed reads, and
the hash-keyed one reports a miss as a nil transaction.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-05 19:31:05 -07:00
zeekay 54b7ff2563 ci(release): publish indexer for arm64 as well as amd64
The lab runs both architectures, so a tag carrying one of them deploys
on half the fleet. One job now builds both platforms under a single
manifest list, with binfmt registered for the arm64 leg: the Dockerfile
compiles indexerd with CGO against sqlite-dev, so that leg runs inside
the target-arch container rather than cross-compiling on TARGETARCH. The
timeout covers the emulated pass instead of aborting part-way through it.
2026-09-05 19:04:39 -07:00
zeekay e2c128f306 feat(indexer): update RPC endpoints to /v1/chain 2026-09-04 14:42:46 -07:00
zeekay 9483d71f42 every Hanzo dependency is the version npm serves
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.
2026-09-01 16:08:04 -07:00
zeekay 234f8e4110 reach chains at /v1/chain; the node deleted /v1/bc 2026-08-30 20:44:13 -07:00
zeekay 5f5211929a docs: chain routes are /v1/chain
luxd serves chain APIs under one segment from constants.ChainAliasPrefix
(constants v1.6.4, node v1.36.181):

  /v1/bc/<alias>/...   -> /v1/chain/<alias>/...
  /ext/bc/<alias>/...  -> /v1/chain/<alias>/...

Observations keep their original addresses: a dated note or a recorded status
code is a fact about when it was written, not an instruction to follow.
2026-08-30 16:41:57 -07:00
zeekayandHanzo Dev 3d6388d6ab An NFT transfer says which NFT
The indexer had the item id all along and threw it away at the edge.
formatTokenTransfer wrote every row in the ERC-20 shape — an amount and a
null decimals — so the three Lux Genesis mints, which moved ids 0, 1 and 2,
came back as three identical rows and no page could name what had moved.
The id now rides in `total`, whose shape follows the token type the way
luxfi/explore's Erc721TotalPayload / Erc1155TotalPayload already read it.

/tokens/{addr}/instances was a hardcoded empty list and /instances/{id} was
an unregistered route, so a collection had no items and an item had no page.
Both are real now. The item list comes from the transfer table rather than
the balance table: balances hold only what someone holds now, so a burned
item would vanish from a collection that still minted it, while its last
transfer always exists and names its current owner.

ERC-1155 was not indexed at all. It is now, single and batch — a batch log
yields one row per (id, amount) pair, so no id is folded away.

Underneath, one decoder. There were two, and they disagreed: the daemon's
path read ERC-20 and ERC-721 only, spelled types "ERC-721", and stored the
raw amount hex; the adapter's path also read ERC-1155 and ERC-404 but
spelled types "ERC721" and stored ids two different ways depending on which
event carried them. decodeTransfers is now the only place a log becomes a
transfer, with one spelling, decimal amounts, and one stored id shape.
applyERC20BalanceDelta and applyERC721BalanceDelta collapse into move(),
which is one function because the three token kinds differ only in what the
id column holds.

An item's tokenURI is read on chain and recorded; the document behind it is
not fetched. An indexer that waits on an IPFS gateway stops indexing, and
the document is not ours and can change. uri_state distinguishes the three
answers that were previously one blank: the chain gave a URI, the chain
names none, or we have not asked. image_url and metadata stay null rather
than assert media nobody read.

Verified against C-Chain 96369: Lux Genesis
(0x9e04fc57c20b2ee45627c4aa280eb471f2ca6ea5, totalSupply 3) indexed from the
live RPC now reports ids 0, 1, 2 on its transfers and lists three items with
their owners, matching eth_getLogs topics[3] and ownerOf.

migrations/004_nft_marketplace.sql stays unapplied and now says why: no
marketplace contract is deployed on any Lux chain, so its tables could only
ever be empty. LLM.md's EVM parity list claimed Seaport/LooksRare/Blur/
X2Y2/Rarible/Zora coverage for a package nothing imports; it now says what
runs.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-29 13:35:20 -07:00
zeekayandHanzo Dev abb63c79b4 An event topic is the hash of its signature, and now a test says so
Seventeen of the twenty-three marketplace topics in nft.go did not hash from
the signature written beside them. The wrong ones were not typos. Zora's
AskCreated and AskCancelled were the same sixty-three characters with a
different first byte. Foundation's settled topic was "e6" thirty-two times.
Blur's cancel matched the real hash for eighteen characters and then diverged —
a remembered prefix with the rest filled in. The six that were right were
Seaport's and LooksRare v1's, the ones anybody could have copied.

Fourteen are corrected by derivation. Three name a struct — LibPart.Part,
Item, Ask — and a topic hashes the canonical signature, where a struct is
written out as a tuple, so those cannot be derived from the text beside them.
They are marked unverified rather than guessed at, because replacing a
fabricated value with a confident one is the same mistake twice.

A wrong topic does not fail. It matches no log, so an indexer built on it
reports that a marketplace has never traded, and from outside that is
indistinguishable from a quiet market. The test derives every topic it can and
refuses the two shapes the bad ones had: a repeating unit, and a long head
shared with a sibling.

Nothing imports this package yet, which is the only reason it never showed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-29 12:15:47 -07:00
zeekayandHanzo Dev 64519916bf the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 13:01:04 -07:00
zeekay 62a5d63bf6 Name Zen in the hero card's font stack
The social card asked for Inter first. That is not a face this estate
licenses, ships or ever drew, so a renderer with Inter installed painted
the wrong face and one without it silently fell through to system-ui.
Zen leads now; the same tail follows it.
2026-08-28 11:41:46 -07:00
zeekay 0d4a268c3a style(docs): swap the geist-named font tokens for Zen 2026-08-23 15:02:00 -07:00
zeekayandHanzo Dev 984494b3a8 Take the sqlite C under the name nothing else claims
The wrapper this reached for registers the driver as "sqlite", and so does
modernc.org/sqlite, which arrives here with hanzoai/replicate. Two packages
under one name is a panic in init, before main runs.

csqlite is the same house C under "sqlite3" — the name every sql.Open here
already names, and one nothing else in this graph takes.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 15:12:22 -07:00
zeekayandHanzo Dev 6920479c85 One sqlite in the binary
Two packages compiled the same amalgamation: upstream mattn/go-sqlite3 here,
and hanzoai/csqlite by way of hanzoai/sqlite, which luxfi/graph moved to. A
binary linking both — the explorer links all three of us — defines every
sqlite3_* symbol and every cgo trampoline twice and fails at link. It fails on
darwin; linux is laxer about duplicates, which is the only reason a build ever
got through.

hanzoai/sqlite is the house driver and csqlite registers both "sqlite" and
"sqlite3", so nothing but the import line changes: every sql.Open here still
names the driver it always named.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 15:08:06 -07:00
zeekayandHanzo Dev fe9cf8d4f7 evm: notice when the chain under us has been rebuilt from genesis
The zoo chain was relaunched and its index never followed. It is serving
24,607 blocks of a chain that no longer exists: at height 5000 the index
holds 0xd798ca91 where the chain now has 0xa6c56d04, and its newest block
is dated two days ago while the live chain sits at 9,564.

indexNewBlocks resumes from MAX(number)+1 and loops while that is <= the
head. A chain rebuilt from genesis reports a head far below everything
already stored, so the resume point lands past it, the loop body never
runs, and the cursor cannot move again. There is no recovery from this by
waiting: the further the new chain gets, the longer it stays behind a
cursor left on the old one.

Deciding this on height alone would be wrong in the other direction. One
backend of a load-balanced set lagging its peers reports the same shape as
a relaunch, and erasing a chain because a node was slow is worse than
being stale. So the height is only used to decide when to ask a question
that has a real answer: block 0's hash. Every node knows which chain it is
on however far behind it is, so a changed genesis means a different chain
and an unchanged one settles it. The streak before the probe keeps the
common case free of extra RPCs.

Each chain is indexed into its own database, so forgetting one reaches
nothing else. The hash is recorded on the first poll that can reach the
chain, because a relaunch cannot be recognised without knowing what came
before it, and an unreadable or unrecorded genesis is not evidence of
anything, so both keep the index.

The graph indexer has resolved this for its own cursor since v1.2.x. This
gives the block indexer the same footing.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-11 12:46:30 -07:00
zeekayandHanzo Dev 7c85b79a62 explorer: name the block's transaction count what the SPA reads
Block rows and block details rendered the Txn label with nothing after it.
Lux block 1106999 has 4 transactions by RPC and the page showed no number.

formatBlock emitted the column's own name, tx_count. The SPA's Block type
declares transactions_count and every render site reads it — BlocksTableItem,
BlocksListItem, LatestBlocksItem, BlockDetails — so the value arrived under a
key nobody looked up and each cell fell back to undefined. Rows that arrived
over the realtime channel DID show a count, which is what made this look like a
data problem rather than a naming one: that path already renames the field
(lib/socket/sse.ts toApiBlock), so the same block read correctly when pushed
and blank when fetched.

Renamed rather than aliased: nothing reads a block's tx_count, so a second
spelling would only be a second thing to keep in step. formatPlatformBlock gets
the same name — P-Chain rows go through the same block widgets.

The suite asserted the old key by PRESENCE, in three places, which is why a
rename is what it takes to notice. TestBlockFieldValues now pins the value:
want 3, got <nil> on the old name.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-08 06:17:16 -07:00
zeekayandHanzo Dev d8b8f8fa23 explorer: fold address case at the lookup boundary, and emit the token contract the SPA reads
Two defects that both fail silently, which is worse than erroring.

Every wallet emits EIP-55 checksummed addresses. The hex pattern accepts
mixed case, so a checksummed address passed validation and then matched no
row — the route answered 404, and the page rendered WLUX, a contract holding
149M LUX, as an empty EOA. The stored hash is lowercase, so the two spellings
have to fold to one key. That fold now happens once, in j()/csvHandler(),
which every route already passes through; handlers keep doing lookups and
stay in their lane. Only {hash} and {addr} fold — {id}, {pair}, {prefix} and
{item} are not hex and are case-significant.

formatToken emitted address/holders/trust_score while the SPA's TokenInfo
(luxfi/explore types/api/token.ts) reads address_hash/holders_count, so
TokensTable keyed every row on undefined and the page crashed. Renamed to
the contract rather than shimming the client, and fixed the second producer
of the same object in formatTokenTransfer, which had drifted to its own
spelling. trust_score had no consumer anywhere: TokenReputation is
'ok' | 'scam', a fraud flag, not the 0-100 completeness heuristic that
computed it — deriving one from the other would have called any token
lacking an icon a scam. reputation is null until there is a real oracle, and
the dead scorer is gone. Its toBool/toInt helpers were generic coercion
braided into a scoring file; they move to convert.go with their tests.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-07 09:00:19 -07:00
zeekayandHanzo Dev a6fa1e8268 Merge remote-tracking branch 'origin/main'
Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 18:37:52 -07:00
zeekayandHanzo Dev f4d8b7a114 rpc: finish the /v1/ sweep, and stop sending Avalanche to /v1/
luxd serves exactly one HTTP prefix -- server/http/server.go:33 sets
baseURL = "/v1" and every route derives from it -- so /ext/* 404s on any
luxd. Avalanche is the mirror image: api.avax.network really does serve
/ext/bc/C/rpc, and pointing it at /v1/ breaks it.

31a1ee7 converted README.md only. This finishes the job and corrects the
overshoot in the same pass:

  config/chains.yaml     sparklepony, testnet.sparklepony, pars,
                         testnet.pars  ->  /v1/   (luxd hosts)
                         avax stays on /ext/
  chains.example.yaml    avax stays on /ext/
  docs/configuration.mdx LUX_{C,P,X}_RPC and the c/p/x_chain block, all
                         127.0.0.1:9630 and :9650 luxd  ->  /v1/
  docs/index.mdx         localhost:9650 and api.lux.network  ->  /v1/

Measured, positive and negative control per host:
  api.lux.network/v1/bc/C/rpc  -> 200 0x17871    /ext -> 404
  api.pars.network/v1/bc/C/rpc -> 200 0x78d65    /ext -> 404
  api.lux-test.network/v1/...  -> 200 0x17870    /ext -> 404

evm/transport/transport_test.go is deliberately left at HEAD. Its
isLuxdEndpoint gate is literally strings.Contains(endpoint, "/v1/"), so
the table is /v1/ throughout by design.

go test ./evm/transport/... ok 15.429s. Both YAML files parse. This repo
does NOT build before this change -- cmd/indexerd fails at link with
duplicate sqlite3 symbols, verified identical with these changes stashed
-- so nothing here is tagged.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 18:35:56 -07:00
zeekayandHanzo Dev 31a1ee7d6e rpc: /ext/ -> /v1/ on our own hosts
luxd serves /v1/*; /ext/* 404s. Third-party chains keep their own paths.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-03 21:02:00 -07:00
zeekayandHanzo Dev f816a9e7fa build: bump Go builder images to 1.26.5, set GOTOOLCHAIN=auto
Currency bump: golang builder images -> 1.26.5 (latest stable).

Adds ENV GOTOOLCHAIN=auto to each Go builder stage. The official golang
images set GOTOOLCHAIN=local, so a builder image older than the module's
go directive is a hard mid-build failure. The pin stays the hermetic fast
path; auto is the safety net for the next go.mod bump.

No go.mod go directive was changed. Builder images move; directives stay.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 16:26:31 -07:00
zeekayandHanzo Dev e9498c93fb deps: drop go.sum lines that disagree with the checksum log
These hashes were recorded from direct VCS fetches made while GOPRIVATE covered
github.com/luxfi/*, which switches off the module proxy and checksum
verification together. Tags moved afterwards, so what is written down here no
longer matches what sum.golang.org holds and the build refuses to verify.

proxy.golang.org still serves the originally-published bytes and those verify
against the log, so dropping only the disagreeing lines and re-tidying restores
a verified module graph with no version change. Lines that agree are untouched;
this is not a regenerated go.sum.

The failure followed the machine rather than the repository — the same commit
built wherever the module cache was clean. Any machine or CI runner that
fetched luxfi modules under the old GOPRIVATE holds the same poisoned entries.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 09:38:27 -07:00
zeekayandHanzo Dev 5ce7dca138 fix(explorer): a zero that means "unavailable" is a lie
Every figure below was rendered as 0 on explore.lux.network while the
chain said otherwise. None of them was missing data — the queries asked
for columns that are not there, or read columns nothing writes.

  /addresses answered {"items":[]} while /stats reported 72 addresses:
  the ORDER BY named `transactions_count`, which is the Blockscout-legacy
  spelling; luxfi/indexer's evm_addresses spells it `tx_count`. SQLite
  raised "no such column", the handler swallowed it into an empty page,
  and it stayed empty forever. Same guess broke evm_tokens' address
  column. detectColumn now asks the database instead.

  Token "Holders 0" sat directly above a populated holder list:
  evm_tokens.holder_count is never written by the indexer. Holders and
  transfers are now counted from evm_token_balances / evm_token_transfers
  in the same statement that selects the token — one query, not N+1 — and
  the token list is ranked by that count instead of by a column of zeros.

  Address balance read 0 for every address, WLUX's 159,126,795.518 LUX
  included: evm_addresses.balance is written as the literal '0' on first
  sight and never updated. Current state belongs to the chain, not to an
  index of it, so balances now come from the node — one batched
  eth_getBalance per page. No node configured, or no answer, means the
  balance is unavailable, and unavailable is null.

  "Burnt fees 100%" on every block: burnt_fees was gas_used x base_fee,
  which is the Ethereum EIP-1559 burn. Lux does not burn it. creditTxFee
  credits the whole fee to the coinbase (0x0100..0000, no code, no key)
  unless the fee split is active, and it has never fired on mainnet.

  average_block_time was structurally 0 — subtracting two TEXT datetimes
  in SQLite yields 0 — and gas_used_today compared those TEXT timestamps
  lexically against datetime('now'), matching nothing. Both go through
  strftime('%s') now, and transactions_today is counted rather than
  hardcoded nil.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 14:25:21 -07:00
zeekay 69cc8f9adf chore: sync working tree
Commits 1 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:13:40 -07:00
zeekay 0b0a861363 fix(deps): reconcile go.sum against what the registry actually serves
Chasing these one at a time was whack-a-mole — every repair surfaced the next
moved tag (age, pq, threshold, zap, consensus, constants, sdk, keys…). This is
the systematic pass instead: every distinct luxfi/hanzoai module@version in the
workspace (692 of them) was resolved against proxy.golang.org + a direct fetch,
and any go.sum line disagreeing with what is actually served was corrected.

Not an attack, and checked rather than assumed: for each case the proxy and a
direct fetch AGREE with each other and differ from the recorded hash. Two
independent transports agreeing means nothing is rewriting bytes in flight —
the tags moved at source. sum.golang.org still holds the original, but
GOPRIVATE covers github.com/luxfi/* with GOSUMDB=off, so our own modules never
consult the checksum DB and a moved tag splits consumers silently instead of
failing at publish time.

Only the recorded hash changes. No selected version moves, and `go mod tidy`
was deliberately NOT run, so nothing is upgraded as a side effect.

Verified: `go list -m all` resolves with no checksum error.

The durable fix is upstream: treat a published version as immutable. Cut
x.y.z+1 instead of moving a tag — with GOSUMDB off, no consumer can detect it.
2026-07-26 04:16:27 -07:00
zeekay 01d674241a fix(deps): correct moved-tag hashes for luxfi modules in go.sum
Several luxfi versions carry TWO different contents across this workspace,
because published tags were moved instead of a new patch being cut:
[email protected], [email protected], [email protected], [email protected], [email protected].

Adjudicated before editing, since "checksum mismatch / SECURITY ERROR" is also
what a real supply-chain attack looks like. It is not one here: sum.golang.org
holds the OLD hash while proxy.golang.org and a direct fetch BOTH serve the same
NEW bytes. Two independent transports agreeing means nothing is rewriting
content in flight — the tag moved at source. The sumdb entry is a fossil:
GOPRIVATE covers github.com/luxfi/* with GOSUMDB=off, so our own modules never
consult the checksum DB and a moved tag splits consumers silently.

The old bytes are served by nothing now, so a stale pin can never build.
Corrected to the only content that exists, in BOTH line forms (h1: and
/go.mod h1:) — Go reports these one at a time, so a partial fix just relocates
the error.

Deliberately no `go mod tidy`: this changes no selected version, only the
recorded hash of versions already chosen.

Verified: `go list -m all` resolves with no checksum error.

The durable fix is upstream: never move a published tag, cut x.y.z+1 instead.
2026-07-26 02:42:49 -07:00
zeekayandHanzo Dev 755319c47c feat(platform): wire P-Chain block + validator indexing (additive)
Standalone explorer API now serves a P-Chain variant when a pchain_blocks
table is present: GET /blocks, /blocks/{id}, /main-page/blocks read the
linear block table, and a new GET /validators returns the synced validator
set (weight/stake, start/end time, uptime, delegation fee, BLS signer
pubkey+PoP, potential reward, connected, per-node delegator_count).

Adapter fixes required for the default SQLite query backend:
- SyncValidators no longer uses Postgres-only to_timestamp(); timestamps are
  bound as time.Time, which both the SQLite and Postgres drivers accept for a
  TIMESTAMP column (regression covered by a real RPC->SQLite test).
- Capture the validator BLS signer (publicKey + proofOfPossession); new
  bls_public_key / bls_proof_of_possession columns on pchain_validators.
- Fall back to weight when the RPC omits the deprecated stakeAmount.

EVM path untouched: block/main-page routes branch on the platform flag; all
other routes are unchanged.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-08 07:12:16 -07:00
zeekayandHanzo Dev e616aa286e chore: migrate luxd HTTP routes /ext -> /v1
Drop the Avalanche-heritage /ext prefix; /v1 is the single canonical route
surface (one way, no backward compat). The node's baseURL is the source of
truth; clients, SDKs, CLI, indexer, maker, genesis, netrunner, and the
k8s/compose/gateway/explorer configs are updated to match.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-01 11:40:21 -07:00
zeekayandHanzo Dev 8d5a0db0eb fix(evm/defi): rename duplicate TestEmptyLogs to subject-scoped names
Two distinct tests (ParseGovernanceEvents vs ParseTeleportEvents empty-log
handling) collided on the name TestEmptyLogs, breaking compilation of the
evm/defi test package. Rename to TestGovernanceEmptyLogs / TestTeleportEmptyLogs
— no coverage change, package compiles + both pass.

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 16:01:01 -07:00
zeekayandHanzo Dev 984a761b64 fix(deps): repair vanished pins / go.sum drift ( core-bump@latest [email protected]) + build green
Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 14:11:06 -07:00
zeekay 2303cb398a Merge branch 'bump/luxdb-v1.19.3' 2026-06-30 14:10:29 -07:00
zeekay bd887f0e2a docs(brand): add hero banner 2026-06-28 20:37:40 -07:00
zeekay 535a3f89dd chore(brand): dynamic hero banner 2026-06-28 20:37:39 -07:00
zeekayandGitHub 637b5c65db chore: bump luxfi/database v1.19.3 (#21)
Co-authored-by: zeekay <[email protected]>
2026-06-25 15:24:11 -07:00
zeekay d909d7f168 chore: bump luxfi/database v1.19.3 2026-06-25 13:06:36 -07:00
zeekay a534262058 scrub branding + corona→corona (OSS brand hygiene) 2026-06-10 23:41:10 -07:00
zeekay 22723298b0 ci: route to canonical native arcd labels [self-hosted, linux, <arch>]
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.
2026-06-10 20:22:29 -07:00
zeekay a84761fa68 chore: replace default coin symbol with LUX in explorer SPA
The 4 explorer page components hard-coded '' as the fallback
when VITE_COIN is unset. Per workspace rule the lux tree must not
default to the  token symbol — switch the fallback to LUX
(the actual Lux Network coin).
2026-06-10 12:19:33 -07:00
zeekay 41b923eb60 deps: bump Go to 1.26.4 across go.mod, Dockerfiles, GH Actions
Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6
(commit 121aca1fa9); this is the cross-repo catch-up.
2026-06-07 10:46:12 -07:00
zeekay 6967813390 go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:09:29 -07:00
zeekay ea02705ced WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:33:55 -07:00
zeekay bda50ca355 fix: gofmt -s across repo (CI format check) 2026-06-02 11:40:11 -07:00
zeekay f9f573e4e4 chore(deps): go get -u ./...
Updated dependencies to latest minor versions via go get -u + go mod tidy.
2026-06-02 11:21:43 -07:00
zeekay 4c1d22ed57 chore: brand-neutral cleanup — remove cross-tenant references 2026-05-26 11:42:03 -07:00
zeekay a21cf1b615 docs: scrub "subnet" language → L2 EVM / VMID terminology (Hanzo policy 2026-05-25) 2026-05-25 16:23:41 -07:00
zeekay 2457a33d9e chore: brand-neutral cleanup — remove cross-tenant references 2026-05-25 15:13:53 -07:00
zeekay ecebc29ca9 indexer: refresh defi/bridge + key/adapter 2026-05-20 16:24:48 -07:00
zeekay 96fcda82c2 ci: switch workflows to lux-build (luxfi-scoped ARC pool)
Cross-org runs-on dispatch failure: hanzo-build-linux-amd64 scale set is
registered to github.com/hanzoai and silently rejects luxfi/* jobs.
Switch to lux-build (githubConfigUrl=github.com/luxfi, max 20).
2026-05-19 13:21:10 -07:00
zeekay 60053d61f5 ci: fix runner labels (hanzo-build pool)
Imaginary runner label `lux-build-linux-amd64` does not exist. The live
amd64 pool is `hanzo-build-linux-amd64`.

- docker.yml: docker job
- docs.yml: build + deploy jobs
2026-05-19 08:03:51 -07:00
zeekayandGitHub 99213b8311 chore: bump Go toolchain to 1.26.3 (#5)
Pin Go version to 1.26.3 across go.mod, CI workflows, and Dockerfiles
for canonical alignment with the rest of the luxfi/* stack.
2026-05-16 16:43:32 -07:00
zeekayandGitHub b6176e0f51 Merge pull request #4 from luxfi/chore/drop-lux-env-prefix
e2e: drop LUX_ prefix from NODE_PATH env literal
2026-05-16 02:38:40 -07:00
zeekay 5728ba8e3b e2e: drop LUX_ prefix from NODE_PATH env literal
constants v1.5.4 + cli v1.99.23 renamed EnvNodePath to NODE_PATH.
Test was still reading LUX_NODE_PATH which no longer matches.
2026-05-16 02:38:17 -07:00
zeekayandGitHub 0e32acfe7f docs: add LICENSING.md pointer to canonical IP/licensing strategy (#3) 2026-05-15 17:34:21 -07:00
zeekay 97fcfd256e indexer: Corona → Corona identifier rename + fix stale xvm→aivm test refs
Two unrelated cleanups bundled:
1. quantum/* Corona* → Corona* identifier rename (audit consistency).
2. ai/adapter_test.go expected "xvm.getVertex"/"xvm.getRecentVertices"
   methods but ai/adapter.go was updated to emit "aivm.*" — stale test
   expectations fixed to match the live implementation.

Tests: indexer/ai PASS 0.363s, indexer/quantum PASS.
2026-05-15 02:45:52 -07:00
zeekay 5fe889d78b deps: luxfi/crypto v1.19.0 2026-05-13 12:00:22 -07:00
zeekay 39628c6f55 ci(docker): go build -mod=mod populates go.sum on the fly 2026-05-05 21:53:44 -07:00
zeekay 5e45f61254 ci: drop build.yml — Docker workflow handles cgo build via alpine 2026-05-05 21:51:05 -07:00
zeekay baef68894b ci: ARC has no_new_privs (no sudo); copy . . before mod download 2026-05-05 21:43:58 -07:00
zeekay 7ff22b0f57 ci: regenerate go.sum at build time (proxy cache inconsistency) 2026-05-05 21:34:05 -07:00
zeekay bcc185c186 ci: refresh go.sum (proxy hash drift), drop dead e2e tests
Three CI fixes for the lux-build ARC pool to actually pass:

- go.sum: hanzoai/[email protected] hash refreshed via go mod tidy
  against the live proxy (was 5Rl3…, now YxjIyn…). proxy.golang.org
  serves the current zip; sum.golang.org's stale record was rejecting
  on every build.
- e2e/pchain_test.go, e2e/xchain_test.go: deleted. They imported
  luxfi/indexer/{pchain,xchain} which were renamed long ago to
  platform/, dag/, etc. Tests have been failing since.
- build.yml: install gcc + libsqlite3-dev on the ARC runner so cgo
  works (mattn/go-sqlite3 needs it).
2026-05-05 21:12:27 -07:00
zeekay 39d3b7ab07 ci: align with luxfi org's actual ARC pool (lux-build-linux-amd64) 2026-05-05 21:04:03 -07:00
zeekay df82696d59 ci(docker): inline multi-arch build, drop cross-org reusable 2026-05-05 21:00:07 -07:00
zeekay d53353c17c docs(chains.example): point default to :8091, clarify shared schema 2026-05-05 20:52:42 -07:00
zeekay 0b8a01a244 ci: hanzo runner labels everywhere, drop duplicate ci.yml 2026-05-05 20:43:53 -07:00
zeekay 826c3d03da refactor: standalone indexerd, drop dead bundles
luxfi/indexer is now a single-purpose repo: the standalone indexerd
chain-indexer daemon. The unified Go binary (indexer + graph + embedded
SPA) lives in luxfi/explorer; the standalone graph daemon lives in
luxfi/graph. One way to do everything.

Changes:
- cmd/indexerd/    new entrypoint (replaces broken cmd/indexer/)
- daemon/          public package: Config, RunIndexers, MountIndexerAPI,
                   MountHealth, ListenAndServe — importable by
                   luxfi/explorer for the unified binary
- Dockerfile       builds only /usr/local/bin/indexerd (port 8091)
- Makefile         single target

Removed:
- cmd/explorer/, cmd/explorerd/  — moved to luxfi/explorer
- cmd/indexer/                   — broken (imports nonexistent
                                    {a,b,k,p,q,t,x,z}chain packages)
- cmd/multichain/, multichain/   — outdated multi-chain manager
- cmd/evmchains/                 — outdated EVM chain registry tool
- cmd/graphd/                    — moved to luxfi/graph
- Dockerfile.{indexer,orig,simple} — pre-canonical leftovers

Indexer config (daemon.Config) is intentionally minimal: data_dir,
http_addr, chains[]. Brand, networks, graph, token, and subgraph
customization is the unified-explorer concern (per-chain in chains.yaml,
read by luxfi/explorer). Extra YAML fields are silently ignored here.
2026-05-05 20:42:41 -07:00
zeekay e98acb1e9d chore(.gitignore): drop trailing rebuild-trigger comment, add logs/ 2026-05-05 20:07:59 -07:00
zeekayandGitHub 9b7c7a2882 Merge pull request #2 from luxfi/ci/multi-arch-native
ci: build multi-arch (amd64 + arm64) on native runners
2026-05-05 19:22:09 -07:00
zeekay 9ed3c09bfa ci: build multi-arch (amd64 + arm64) on native runners 2026-05-05 19:08:39 -07:00
zeekay 46acdccae3 scrub: remove explorer .env.liquidity (lux repo hard rule)
Tenant env files belong in the tenant ops repo, not in upstream lux/indexer.
2026-05-05 17:22:45 -07:00
zeekay 95c1f0f091 indexer: scrub Avalabs IP brand refs 2026-05-05 16:23:12 -07:00
zeekay 828aeddc14 fix(cmd/explorer): wait for evm_blocks before mounting API (race fix)
The standalone API server's detectTables() runs once on construction and
caches the table name (evm_blocks vs blocks). If main.go mounts the API
before runChain has called idx.Init() on the EVM indexer, detectTables
sees no evm_blocks and falls back to the bare 'blocks' default — which
never exists, so every query returns empty.

Verified locally: with the table-wait fix, multi-chain mode indexes all
five mainnet chains correctly. After ~50s of run time:
  cchain  blocks=845 max=844 txs=1
  zoo     blocks=833 max=832 txs=52   (fully synced)
  hanzo   blocks=47  max=46  txs=58   (fully synced)
  spc     blocks=11  max=10  txs=2    (fully synced)
  pars    blocks=29  max=28  txs=42   (fully synced)

/v1/indexer/{slug}/blocks returns chain-specific data; /v1/explorer/*
legacy alias works; /v1/indexer/cchain/* default-chain alias works.
2026-05-02 17:08:02 -07:00
zeekay 0ca18e0436 deploy: per-network rollout script for unified explorer
Runs the per-network manifests in universe/k8s/lux-explorer, pins the
freshly built ghcr.io/luxfi/indexer:main image, restarts each deployment,
and patches IngressRoutes so testnet/devnet hostnames stop sharing the
mainnet pod's data.

Idempotent — safe to rerun whenever a new image is published.
2026-05-02 09:36:15 -07:00
zeekay 67ab48f053 fix(Dockerfile): drop legacy cmd/indexer build, build only cmd/explorer
cmd/indexer was kept around as the per-chain legacy entrypoint; cmd/explorer
is now the unified single-binary entrypoint that subsumes it. The legacy
build step was failing in CI because cmd/indexer hasn't been kept compiling
through recent refactors. Drop it — the unified binary covers single-chain
mode via --rpc as well as multi-chain via --config.
2026-05-02 09:31:14 -07:00
zeekay 3d065443f3 fix(go.sum): keep upstream replicate hash (proxy.golang.org canonical)
Local re-download went via GitHub directly and got a different commit
content for the v0.6.0 tag. Restore the proxy.golang.org-canonical hash
so CI builds (which fetch via the module proxy) succeed.
2026-05-02 09:29:41 -07:00
zeekay 9049904013 ci(docker): build directly on ubuntu-24.04 (skip ARC reusable workflow)
Reusable workflow at hanzoai/.github expects lux-* ARC runners that
aren't provisioned for the luxfi org, so every push fails with a
startup_failure. Drop the indirection and build amd64 with QEMU on a
hosted runner. Pushes ghcr.io/luxfi/indexer:{sha,branch,latest}.

Trade-off: amd64-only for now. Can re-add arm64 once the per-org ARC
scale-set is provisioned.
2026-05-02 09:28:22 -07:00
zeekay f1110b6895 fix(cmd/explorer): mount API after DB+schema ready, surface init errors
The 30-second mount cap was firing before the EVM indexer wrote its first
block — for chains whose RPC was slow or whose subnet had only a handful of
blocks, the standalone API never mounted and requests fell through to the
SPA placeholder. Three fixes:

1. Pre-init the EVM schema in runChain *before* starting the indexing loop.
   Run() also calls Init(), so this is idempotent — but doing it eagerly
   means the API server can detect tables on its first probe instead of
   waiting on a slow first GetLatestBlock RPC roundtrip.

2. Replace the 30-attempt cap with retry-until-ctx-done and log the actual
   failure reason every five minutes. Bad RPC URLs and missing data dirs
   are now visible in the log instead of silently disappearing.

3. Mount each chain at both /v1/indexer/* and /v1/explorer/* (legacy alias)
   so existing Blockscout-fork frontends keep working without an ingress
   middleware. Default chain also gets a /v1/indexer/{slug}/* alias for
   hostname-based routing.

Also: PORT env (K8s convention) now feeds HTTP_ADDR if set, and
CHAIN_RPC_URL (LiquidExplorer operator) joins RPC_ENDPOINT as a single-chain
RPC source.
2026-05-02 09:26:39 -07:00
zeekay efa91b80d9 multichain: register ERC-3643 / ONCHAINID / OP_NET protocol types
Lets chain configs declare these protocols natively. Per-event decoding
already happens generically via evm/contracts/abi.go (any ABI uploaded to
verifier surfaces); the structured GraphQL ingestion for ERC-3643 lives in
luxfi/graph and is fed automatically once SecurityToken/IdentityRegistry/
ONCHAINID/etc. emit events on the indexed chain.
2026-04-28 18:34:08 -07:00
zeekayandGitHub 7f27efe556 ci: migrate to canonical docker-build reusable (#1)
- Add docker.yml using hanzoai/.github/.github/workflows/docker-build.yml@main
- Add workflow-sanity.yml to enforce canonical CI contract
- Remove bespoke docker build steps from existing workflows

Refs: hanzoai/.github canonical Docker CI contract.
2026-04-23 19:12:19 -07:00
zeekay b526b54c67 fix: WebSocket OOM + I-Chain indexer RPC method
- chain/dag/evm subscribers: add 1024-client semaphore, 64KB read limit,
  60s deadline, pong handler, default origin check (was open)
- platform_indexer: identity.Health → identity.getLatestBlock
2026-03-02 03:11:27 -07:00
zeekay 7d2c025b5b feat: unified explorer — chi refactor, graph merge, slug validation, /v1/explorer 2026-03-01 18:00:00 -08:00
zeekay f754b9af32 style: fix gofmt alignment in ChainDef struct 2026-03-02 11:03:00 -08:00
zeekay f54b337f2c fix: ensure EVM chain tables exist on evmchains startup
The evmchains process creates API servers but never created the
database tables they query. Add EnsureTables() that runs CREATE TABLE
IF NOT EXISTS for all 9 required tables (blocks, transactions,
addresses, tokens, token_transfers, logs, internal_transactions,
token_balances, extended_stats) with proper indexes and seeds the
stats row. Called on each chain startup before serving API requests.

Fixes: pq: relation "cchain_extended_stats" does not exist
2026-03-02 10:57:04 -08:00
zeekay 088d285887 feat: multi-chain EVM indexer with table prefix support
- Replace all hardcoded cchain_ table references with Tbl() prefix pattern
- Add chainSlug parameter to Repository, Adapter, Search, Stats, Charts
- New cmd/evmchains binary: runs all EVM chains in single process
- Config-driven via YAML (configs/evmchains.yaml)
- Management HTTP server on :9000 (/health, /chains)
- Shared PostgreSQL with per-chain table prefixes
- Update Dockerfile for multi-binary support
2026-03-02 10:57:04 -08:00
zeekay aca3b359bd chore: update Go to 1.26 in Dockerfile 2026-03-02 05:04:20 -08:00
zeekay ba02ee5eb6 chore: update Go to 1.26 2026-03-02 05:04:18 -08:00
zeekay 3bfbedda21 unify all sql images to :latest (PG18)
Single version policy: ghcr.io/hanzoai/sql:latest everywhere.
No version pinning, no backwards compat, always latest stable.
2026-03-01 21:09:00 -08:00
zeekay 3819d53b00 unify on hanzoai/sql:17-alpine, IngressClass ingress
Standardize all postgres images to ghcr.io/hanzoai/sql:17-alpine
and IngressClass to 'ingress' for white-label portability.
2026-03-01 20:54:38 -08:00
zeekay 767b39bb14 fix: PostgreSQL compatibility for all chain adapters
- AUTOINCREMENT → SERIAL (bchain, qchain, zchain)
- INSERT OR IGNORE → ON CONFLICT DO NOTHING (all chains)
- INSERT OR REPLACE → ON CONFLICT DO UPDATE (qchain, tchain)
- datetime('now') → NOW() (pchain)
- datetime(?, 'unixepoch') → to_timestamp(?) (pchain)
- Add K-chain to DAG indexer and CLI
2026-02-27 10:47:28 -08:00
zeekay b1ff299a80 feat: update EVM indexer and Dockerfile 2026-02-21 11:57:17 -08:00