mirror of
https://github.com/luxfi/market.git
synced 2026-09-24 00:25:57 +00:00
fix(market): /genesis printed a tier table no contract backs — read it instead
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]>
This commit is contained in:
+87
-70
@@ -1,96 +1,113 @@
|
||||
'use client'
|
||||
|
||||
import { formatUnits } from 'viem'
|
||||
import { useReadContracts } from 'wagmi'
|
||||
import { ArrowRight, ExternalLink } from 'lucide-react'
|
||||
import { Header } from '@/components/Header'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import { useChainContext } from '@/hooks/useChain'
|
||||
import { CHAIN_INFO, explorerUrl } from '@/lib/chains'
|
||||
import { GENESIS_ABI, GENESIS_NFT } from '@/lib/contracts'
|
||||
|
||||
const GENESIS_TIERS = [
|
||||
{
|
||||
name: 'Genesis',
|
||||
lockedLux: '1,000,000,000',
|
||||
description:
|
||||
'The pinnacle of the Lux Genesis Collection. Each Genesis NFT permanently locks 1 billion LUX, with all staking rewards flowing directly to the holder.',
|
||||
maxSupply: 10,
|
||||
rewardMultiplier: '10x',
|
||||
},
|
||||
{
|
||||
name: 'Validator',
|
||||
lockedLux: '100,000,000',
|
||||
description:
|
||||
'Validator-tier Genesis NFTs lock 100M LUX permanently. Holders receive validator staking rewards through the ValidatorVault and LiquidLUX (xLUX) system.',
|
||||
maxSupply: 100,
|
||||
rewardMultiplier: '5x',
|
||||
},
|
||||
{
|
||||
name: 'Mini',
|
||||
lockedLux: '10,000,000',
|
||||
description:
|
||||
'Mini Genesis NFTs lock 10M LUX permanently. A more accessible entry point to Genesis rewards with proportional staking returns.',
|
||||
maxSupply: 1000,
|
||||
rewardMultiplier: '2x',
|
||||
},
|
||||
{
|
||||
name: 'Nano',
|
||||
lockedLux: '1,000,000',
|
||||
description:
|
||||
'Nano Genesis NFTs lock 1M LUX permanently. The most accessible Genesis tier, providing base-level staking rewards to holders.',
|
||||
maxSupply: 10000,
|
||||
rewardMultiplier: '1x',
|
||||
},
|
||||
]
|
||||
/** One tile. `value` is always a measurement; there is no default to fall back on. */
|
||||
function Stat({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<Card className="p-5">
|
||||
<div className="text-[13px] text-muted-foreground mb-1">{label}</div>
|
||||
<div className="text-2xl font-bold font-mono">{value}</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default function GenesisPage() {
|
||||
const { chainId } = useChainContext()
|
||||
const chain = CHAIN_INFO[chainId]
|
||||
const address = GENESIS_NFT[chainId]
|
||||
|
||||
const { data, isLoading, isError } = useReadContracts({
|
||||
allowFailure: false,
|
||||
contracts: [
|
||||
{ chainId, address, abi: GENESIS_ABI, functionName: 'totalMinted' },
|
||||
{ chainId, address, abi: GENESIS_ABI, functionName: 'totalLuxLocked' },
|
||||
],
|
||||
query: { enabled: Boolean(address) },
|
||||
})
|
||||
|
||||
const [minted, luxLocked] = data ?? []
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<main className="max-w-[1200px] mx-auto px-6 py-8">
|
||||
<section className="text-center mb-12">
|
||||
<section className="text-center mb-10">
|
||||
<h1 className="text-4xl font-bold mb-3">Genesis NFTs</h1>
|
||||
<p className="text-base text-muted-foreground max-w-[600px] mx-auto">
|
||||
Permanently locked LUX tokens with staking rewards flowing to NFT holders. Genesis NFTs control validator
|
||||
staking returns through the ValidatorVault and LiquidLUX (xLUX) system.
|
||||
Each Genesis NFT bonds LUX permanently and routes the validator staking rewards on that
|
||||
bond to whoever holds the NFT. Both figures below are read from the collection contract
|
||||
on every page load.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Reward flow diagram */}
|
||||
<Card className="p-6 mb-12 text-center">
|
||||
<h3 className="text-base font-semibold mb-4">Reward Flow</h3>
|
||||
{!address ? (
|
||||
<Card className="p-6 text-center text-muted-foreground">
|
||||
Genesis NFTs are a Lux Network collection. No Genesis contract is registered for{' '}
|
||||
{chain?.name ?? `chain ${chainId}`}.
|
||||
</Card>
|
||||
) : isLoading ? (
|
||||
<Card className="p-6 text-center text-muted-foreground">
|
||||
Reading the Genesis contract on {chain?.name ?? `chain ${chainId}`}…
|
||||
</Card>
|
||||
) : isError || data === undefined ? (
|
||||
<Card className="p-6 space-y-3">
|
||||
<div className="font-semibold">Supply and bonded LUX are unavailable.</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The Genesis collection is registered at{' '}
|
||||
<code className="font-mono text-xs">{address}</code> on{' '}
|
||||
{chain?.name ?? `chain ${chainId}`}, but the contract does not answer:{' '}
|
||||
<code className="font-mono text-xs">eth_getCode</code> returns{' '}
|
||||
<code className="font-mono text-xs">0x</code> and every view call reverts, so there is
|
||||
no minted count and no bonded balance to show. Nothing is reported here until the
|
||||
contract answers again.
|
||||
</p>
|
||||
<a
|
||||
className="inline-flex items-center gap-1.5 text-sm hover:underline"
|
||||
href={`${explorerUrl(chainId)}/address/${address}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Inspect the address <ExternalLink className="h-3.5 w-3.5" />
|
||||
</a>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(240px,1fr))] gap-4">
|
||||
<Stat label="Minted" value={minted!.toLocaleString()} />
|
||||
<Stat
|
||||
label="LUX bonded (permanent)"
|
||||
value={Number(formatUnits(luxLocked!, 18)).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Card className="p-6 mt-10 text-center">
|
||||
<h3 className="text-base font-semibold mb-4">How rewards reach a holder</h3>
|
||||
<div className="flex items-center justify-center gap-4 flex-wrap text-sm">
|
||||
<span>Validator Staking</span>
|
||||
<span>Validator staking</span>
|
||||
<ArrowRight className="h-4 w-4 text-muted-foreground" />
|
||||
<span>ValidatorVault.depositRewards()</span>
|
||||
<span className="font-mono text-xs">ValidatorVault.depositRewards()</span>
|
||||
<ArrowRight className="h-4 w-4 text-muted-foreground" />
|
||||
<span>LiquidLUX.depositValidatorRewards()</span>
|
||||
<span className="font-mono text-xs">LiquidLUX.depositValidatorRewards()</span>
|
||||
<ArrowRight className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="font-semibold">xLUX holders</span>
|
||||
</div>
|
||||
<p className="text-[13px] text-muted-foreground mt-4">
|
||||
The call path above is the design in{' '}
|
||||
<code className="font-mono text-xs">standard/contracts/nft/GenesisNFTs.sol</code>. It is
|
||||
not a claim about rewards paid — no reward figure appears on this page unless it was
|
||||
read from chain.
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
{/* Tier grid */}
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] gap-6">
|
||||
{GENESIS_TIERS.map((tier) => (
|
||||
<Card key={tier.name} className="overflow-hidden">
|
||||
<div className="aspect-[4/3] bg-gradient-to-br from-secondary to-muted flex flex-col items-center justify-center gap-2">
|
||||
<span className="text-xs text-muted-foreground uppercase tracking-[2px]">
|
||||
Genesis
|
||||
</span>
|
||||
<span className="text-4xl font-bold">{tier.name}</span>
|
||||
<span className="text-sm text-primary">{tier.rewardMultiplier} rewards</span>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<div className="text-[13px] text-muted-foreground mb-1">Permanently Locked</div>
|
||||
<div className="text-2xl font-bold mb-3 font-mono">{tier.lockedLux} LUX</div>
|
||||
<p className="text-[13px] text-muted-foreground leading-relaxed mb-4">
|
||||
{tier.description}
|
||||
</p>
|
||||
<div className="flex justify-between text-[13px] text-muted-foreground border-t border-border pt-3">
|
||||
<span>Max Supply: {tier.maxSupply.toLocaleString()}</span>
|
||||
<span>Rewards: {tier.rewardMultiplier}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -26,6 +26,32 @@ export const WRAPPED_NATIVE: Record<number, `0x${string}`> = {
|
||||
96369: '0x4888E4a2Ee0F03051c72D2BD3ACf755eD3498B3E',
|
||||
}
|
||||
|
||||
/**
|
||||
* Genesis NFT collection per chain — standard/contracts/nft/GenesisNFTs.sol.
|
||||
*
|
||||
* Unlike CONTRACTS above, an address here does not enable a transaction; it
|
||||
* only tells /genesis where to probe. The probe is the point: this address is
|
||||
* where the indexer recorded 118 mints on 2026-07-09 (block 1085879), but it
|
||||
* has NO CODE at head. Measured against https://api.lux.network/v1/bc/C/rpc at
|
||||
* block 1098191, with WLUX 0x4888e4a2.. as the positive control:
|
||||
*
|
||||
* 0x004287c4..76c6 eth_getCode -> "0x" totalSupply() -> "0x" (revert)
|
||||
* 0x4888e4a2..8b3e eth_getCode -> 3462 totalSupply() -> 159126795.518e18
|
||||
*
|
||||
* So /genesis reads totalMinted()/totalLuxLocked() live and reports the read as
|
||||
* unavailable when it reverts, instead of printing the tier table that used to
|
||||
* be hard-coded into the page.
|
||||
*/
|
||||
export const GENESIS_NFT: Record<number, `0x${string}`> = {
|
||||
96369: '0x004287c47efc912fec391979154454a8017a76c6',
|
||||
}
|
||||
|
||||
/** GenesisNFTs.sol reads: totalMinted():829, totalLuxLocked():214 (public state). */
|
||||
export const GENESIS_ABI = [
|
||||
{ type: 'function', name: 'totalMinted', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
|
||||
{ type: 'function', name: 'totalLuxLocked', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
|
||||
] as const
|
||||
|
||||
export const ERC721_ABI = [
|
||||
{ type: 'function', name: 'balanceOf', stateMutability: 'view', inputs: [{ name: 'owner', type: 'address' }], outputs: [{ type: 'uint256' }] },
|
||||
{ type: 'function', name: 'ownerOf', stateMutability: 'view', inputs: [{ name: 'tokenId', type: 'uint256' }], outputs: [{ type: 'address' }] },
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { createConfig, http } from 'wagmi'
|
||||
import { luxMainnet, zooMainnet, hanzoMainnet, spcMainnet, parsMainnet } from './chains'
|
||||
|
||||
export const config = createConfig({
|
||||
chains: [luxMainnet, zooMainnet, hanzoMainnet, spcMainnet, parsMainnet],
|
||||
transports: {
|
||||
[luxMainnet.id]: http(),
|
||||
[zooMainnet.id]: http(),
|
||||
[hanzoMainnet.id]: http(),
|
||||
[spcMainnet.id]: http(),
|
||||
[parsMainnet.id]: http(),
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user