mirror of
https://github.com/luxfi/market.git
synced 2026-09-24 00:25:57 +00:00
fix(market): quote the royalty from basis points, not a float
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]>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
import { ExternalLink } from '@luxfi/ui/icons'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { Suspense, useEffect, useMemo } from 'react'
|
||||
import { formatUnits } from 'viem'
|
||||
import { useReadContracts } from 'wagmi'
|
||||
import { ItemCard } from '@/components/ItemCard'
|
||||
import { Page } from '@/components/Page'
|
||||
@@ -12,7 +11,7 @@ import { Stat } from '@/components/Stat'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import type { ChainState } from '@/hooks/chain'
|
||||
import { useCollection, useCounters, useHolders, useTransfers, useTransferIds } from '@/hooks/queries'
|
||||
import { ERC165_ABI, ERC2981_ABI, ERC721_ABI, INTERFACE, ONE } from '@/lib/contracts'
|
||||
import { ERC165_ABI, ERC2981_ABI, ERC721_ABI, INTERFACE, ONE, rateLabel } from '@/lib/contracts'
|
||||
import { idFor } from '@/lib/logs'
|
||||
import { addressUrl, type Chain } from '@/lib/registry'
|
||||
import { shortenAddress } from '@/lib/utils'
|
||||
@@ -68,7 +67,7 @@ function Declares({ chain, address }: { chain: Chain; address: string }) {
|
||||
</p>
|
||||
{rate ? (
|
||||
<p className="leading-relaxed text-muted-foreground">
|
||||
royaltyInfo quotes {Number(formatUnits(rate[1], 18)) * 100}% of a sale to{' '}
|
||||
royaltyInfo quotes {rateLabel(rate[1])} of a sale to{' '}
|
||||
<code className="font-mono text-xs">{rate[0]}</code>. That is what the contract would
|
||||
enforce on a marketplace that honours it; none is deployed on Lux, so nothing enforces it
|
||||
today.
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
GENESIS_ABI,
|
||||
GENESIS_TOKEN_ABI,
|
||||
ONE,
|
||||
rateLabel,
|
||||
} from '@/lib/contracts'
|
||||
import * as links from '@/lib/links'
|
||||
import { addressUrl, type Chain } from '@/lib/registry'
|
||||
@@ -72,7 +73,7 @@ function Collection({ chain, address }: { chain: Chain; address: `0x${string}` }
|
||||
/>
|
||||
<Stat
|
||||
label="Royalty"
|
||||
value={rate ? `${Number(formatUnits(rate[1], 18)) * 100}%` : null}
|
||||
value={rate ? rateLabel(rate[1]) : null}
|
||||
note="declared through ERC-2981"
|
||||
/>
|
||||
<Stat
|
||||
@@ -90,8 +91,7 @@ function Collection({ chain, address }: { chain: Chain; address: `0x${string}` }
|
||||
Mini, 1M for Nano, and each token below reports its own.
|
||||
</p>
|
||||
<p className="text-muted-foreground">
|
||||
The collection declares a {rate ? `${Number(formatUnits(rate[1], 18)) * 100}%` : ''}{' '}
|
||||
royalty through ERC-2981, which a marketplace has to read and honour. There is no
|
||||
The collection declares a {rate ? rateLabel(rate[1]) : ''} royalty through ERC-2981, which a marketplace has to read and honour. There is no
|
||||
marketplace contract on any Lux chain to read it, and this collection’s own{' '}
|
||||
<code className="font-mono text-xs">market</code> address is zero.
|
||||
</p>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useQuery } from '@tanstack/react-query'
|
||||
import Link from 'next/link'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { Suspense, useMemo } from 'react'
|
||||
import { formatUnits } from 'viem'
|
||||
import { useReadContracts } from 'wagmi'
|
||||
import { GenesisMeta } from '@/components/Genesis'
|
||||
import { Page } from '@/components/Page'
|
||||
@@ -13,7 +12,7 @@ import { Source } from '@/components/Source'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import type { ChainState } from '@/hooks/chain'
|
||||
import { useCollection, useTransferIds, useTransfers } from '@/hooks/queries'
|
||||
import { ERC165_ABI, ERC2981_ABI, ERC721_ABI, GENESIS, INTERFACE, ONE } from '@/lib/contracts'
|
||||
import { ERC165_ABI, ERC2981_ABI, ERC721_ABI, GENESIS, INTERFACE, ONE, rateLabel } from '@/lib/contracts'
|
||||
import * as links from '@/lib/links'
|
||||
import { idFor } from '@/lib/logs'
|
||||
import { addressUrl, txUrl, type Chain } from '@/lib/registry'
|
||||
@@ -203,7 +202,7 @@ function Body({ state, slug, address, id }: { state: Ready; slug: string; addres
|
||||
{rate ? (
|
||||
<div>
|
||||
<span className="text-muted-foreground">Royalty </span>
|
||||
{Number(formatUnits(rate[1], 18)) * 100}% to{' '}
|
||||
{rateLabel(rate[1])} to{' '}
|
||||
<span className="font-mono text-xs">{rate[0]}</span>, declared through ERC-2981.
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -74,6 +74,14 @@ export const INTERFACE = {
|
||||
/** The unit royaltyInfo is quoted against, so the answer reads as a rate. */
|
||||
export const ONE = 10n ** 18n
|
||||
|
||||
/**
|
||||
* The quote for one whole token, as a percentage. Basis points are integers on
|
||||
* chain, so this rounds off the float that dividing by 1e18 introduces —
|
||||
* 250 bps came back as 2.4999999999999996 before.
|
||||
*/
|
||||
export const rateLabel = (quoted: bigint) =>
|
||||
`${Number((quoted * 10_000n) / ONE) / 100}%`
|
||||
|
||||
/**
|
||||
* GenesisNFTs.sol keeps a record per token — `mapping(uint256 => TokenMeta)
|
||||
* public tokenMeta` — and its generated getter answers on chain today. It is
|
||||
|
||||
Reference in New Issue
Block a user