Skip to content

Commit

Permalink
[Dashboard] Remove v4 code on some Marketplace components
Browse files Browse the repository at this point in the history
 (#4128)

## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR exposes `max()` and `min()` utility methods for bigints and updates marketplace-related components to use these methods for better handling of large numbers.

### Detailed summary
- Exposes `max()` and `min()` utility methods for bigints in `thirdweb/utils`
- Updates components in `dashboard` related to marketplace to use `max()` and `min()` for handling large numbers efficiently

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/overview/components/MarketplaceDetails.tsx`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
  • Loading branch information
kien-ngo committed Aug 19, 2024
1 parent 15899ea commit ef9cc55
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 101 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-points-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Expose max() & min() util methods for bigints
Original file line number Diff line number Diff line change
@@ -1,87 +1,86 @@
import { Skeleton, Stack, Stat, StatLabel, StatNumber } from "@chakra-ui/react";
import {
useDirectListingsCount,
useEnglishAuctionsCount,
} from "@thirdweb-dev/react";
import type { MarketplaceV3 } from "@thirdweb-dev/sdk";
import { BigNumber } from "ethers";
import { useMemo } from "react";
import type { ThirdwebContract } from "thirdweb";
import { totalAuctions, totalListings } from "thirdweb/extensions/marketplace";
import { useReadContract } from "thirdweb/react";
import { Card } from "tw-components";

interface ListingStatsV3Props {
contract?: MarketplaceV3;
features: string[];
}

const TotalListingsStat: React.FC<{ contract?: MarketplaceV3 }> = ({
const TotalListingsStat: React.FC<{ contract: ThirdwebContract }> = ({
contract,
}) => {
const directListingsQuery = useDirectListingsCount(contract);
const englishAuctionsQuery = useEnglishAuctionsCount(contract);

const totalListings = useMemo(
() =>
BigNumber.from(directListingsQuery?.data || 0).add(
BigNumber.from(englishAuctionsQuery?.data || 0),
),
[directListingsQuery?.data, englishAuctionsQuery?.data],
const directListingsQuery = useReadContract(totalListings, {
contract,
});
const englishAuctionsQuery = useReadContract(totalAuctions, {
contract,
});
const combinedListingCount = useMemo(
() => (directListingsQuery.data || 0n) + (englishAuctionsQuery.data || 0n),
[directListingsQuery.data, englishAuctionsQuery.data],
);

return (
<Card as={Stat}>
<StatLabel mb={{ base: 1, md: 0 }}>Total Listings</StatLabel>
<Skeleton
isLoaded={
!contract ||
(directListingsQuery.isSuccess && englishAuctionsQuery.isSuccess)
directListingsQuery.isSuccess && englishAuctionsQuery.isSuccess
}
>
<StatNumber>{totalListings.toString()}</StatNumber>
<StatNumber>{combinedListingCount.toString()}</StatNumber>
</Skeleton>
</Card>
);
};

const DirectListingsStat: React.FC<{ contract?: MarketplaceV3 }> = ({
const DirectListingsStat: React.FC<{ contract: ThirdwebContract }> = ({
contract,
}) => {
const directListingsQuery = useDirectListingsCount(contract);
const directListingsQuery = useReadContract(totalListings, {
contract,
});

return (
<Card as={Stat}>
<StatLabel mb={{ base: 1, md: 0 }}>Direct Listings</StatLabel>
<Skeleton isLoaded={!contract || directListingsQuery.isSuccess}>
<StatNumber>{(directListingsQuery.data || 0).toString()}</StatNumber>
<Skeleton isLoaded={directListingsQuery.isSuccess}>
<StatNumber>{(directListingsQuery.data || 0n).toString()}</StatNumber>
</Skeleton>
</Card>
);
};

const EnglishAuctionsStat: React.FC<{ contract?: MarketplaceV3 }> = ({
const EnglishAuctionsStat: React.FC<{ contract: ThirdwebContract }> = ({
contract,
}) => {
const englishAuctionsQuery = useEnglishAuctionsCount(contract);
const englishAuctionsQuery = useReadContract(totalAuctions, {
contract,
});

return (
<Card as={Stat}>
<StatLabel mb={{ base: 1, md: 0 }}>English Auctions</StatLabel>
<Skeleton isLoaded={!contract || englishAuctionsQuery.isSuccess}>
<StatNumber>{(englishAuctionsQuery.data || 0).toString()}</StatNumber>
<Skeleton isLoaded={englishAuctionsQuery.isSuccess}>
<StatNumber>{(englishAuctionsQuery.data || 0n).toString()}</StatNumber>
</Skeleton>
</Card>
);
};

interface ListingStatsV3Props {
contract: ThirdwebContract;
hasDirectListings: boolean;
hasEnglishAuctions: boolean;
}

export const ListingStatsV3: React.FC<ListingStatsV3Props> = ({
contract,
features,
hasDirectListings,
hasEnglishAuctions,
}) => {
const hasDirectListings = features.includes("DirectListings");
const hasEnglishAuctions = features.includes("EnglishAuctions");

return (
<Stack spacing={{ base: 3, md: 6 }} direction="row">
{hasDirectListings && hasEnglishAuctions && (
{hasDirectListings && hasEnglishAuctions && contract && (
<TotalListingsStat contract={contract} />
)}
{hasDirectListings && <DirectListingsStat contract={contract} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@ import {
SkeletonText,
useBreakpointValue,
} from "@chakra-ui/react";
import { useContract } from "@thirdweb-dev/react";
import type { MarketplaceV3 } from "@thirdweb-dev/sdk";
import { ListingStatsV3 } from "contract-ui/tabs/listings/components/listing-stats";
import { useTabHref } from "contract-ui/utils";
import { BigNumber } from "ethers";
import { useV5DashboardChain } from "lib/v5-adapter";
import { useMemo } from "react";
import { getContract } from "thirdweb";
import type { ThirdwebContract } from "thirdweb";
import {
type DirectListing,
type EnglishAuction,
Expand All @@ -24,17 +20,10 @@ import {
totalListings,
} from "thirdweb/extensions/marketplace";
import { useReadContract } from "thirdweb/react";
import {
Badge,
Card,
Heading,
Text,
TrackedLink,
type TrackedLinkProps,
} from "tw-components";
import { max } from "thirdweb/utils";
import { Badge, Card, Heading, Text, TrackedLink } from "tw-components";
import { AddressCopyButton } from "tw-components/AddressCopyButton";
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
import { thirdwebClient } from "../../../../lib/thirdweb-client";

type ListingData =
| (Pick<
Expand All @@ -53,60 +42,45 @@ type ListingData =
});

type MarketplaceDetailsProps = {
contractAddress: string;
contractType: "marketplace" | "marketplace-v3";
contract: ThirdwebContract;
features: string[];
trackingCategory: TrackedLinkProps["category"];
trackingCategory: string;
};

interface MarketplaceDetailsVersionProps<T> {
contract: T;
trackingCategory: TrackedLinkProps["category"];
features: MarketplaceDetailsProps["features"];
}

export const MarketplaceDetails: React.FC<MarketplaceDetailsProps> = ({
contractAddress,
contractType,
contract,
trackingCategory,
features,
}) => {
const { contract } = useContract(contractAddress, contractType);

if (contractType === "marketplace" && contract) {
// no longer supported
return null;
}
return (
<MarketplaceV3Details
contract={contract as MarketplaceV3}
contract={contract}
trackingCategory={trackingCategory}
features={features}
/>
);
};

type ListingCardsSectionProps = {
contract: MarketplaceV3;
trackingCategory: TrackedLinkProps["category"];
contract: ThirdwebContract;
trackingCategory: string;
};

const DirectListingCards: React.FC<ListingCardsSectionProps> = ({
trackingCategory,
contract: v4Contract,
contract,
}) => {
const chain = useV5DashboardChain(v4Contract.chainId);
const contract = getContract({
client: thirdwebClient,
address: v4Contract.getAddress(),
chain: chain,
});
const directListingsHref = useTabHref("direct-listings");
const countQuery = useReadContract(totalListings, { contract });
const listingsQuery = useReadContract(getAllListings, {
contract,
count: 3n,
start: Math.max(BigNumber.from(countQuery?.data || 3)?.toNumber() - 3, 0),
start: Math.max(
Number(
max((countQuery?.data || 3n) - 3n, BigInt(Number.MAX_SAFE_INTEGER)),
),
0,
),
});
const listings = useMemo(
() =>
Expand All @@ -121,7 +95,7 @@ const DirectListingCards: React.FC<ListingCardsSectionProps> = ({
[listingsQuery?.data],
);

if (!countQuery.isLoading && BigNumber.from(countQuery.data || 0).eq(0)) {
if (!countQuery.isLoading && (countQuery.data || 0n) === 0n) {
return null;
}
if (!listingsQuery.isLoading && listings.length === 0) {
Expand Down Expand Up @@ -156,21 +130,19 @@ const DirectListingCards: React.FC<ListingCardsSectionProps> = ({

const EnglishAuctionCards: React.FC<ListingCardsSectionProps> = ({
trackingCategory,
contract: v4Contract,
contract,
}) => {
const chain = useV5DashboardChain(v4Contract.chainId);
const contract = getContract({
client: thirdwebClient,
address: v4Contract.getAddress(),
chain: chain,
});

const englishAuctionsHref = useTabHref("english-auctions");
const countQuery = useReadContract(totalAuctions, { contract });
const auctionsQuery = useReadContract(getAllAuctions, {
contract,
count: 3n,
start: Math.max(BigNumber.from(countQuery?.data || 3)?.toNumber() - 3, 0),
start: Math.max(
Number(
max((countQuery?.data || 3n) - 3n, BigInt(Number.MAX_SAFE_INTEGER)),
),
0,
),
});
const auctions = useMemo(
() =>
Expand All @@ -185,7 +157,7 @@ const EnglishAuctionCards: React.FC<ListingCardsSectionProps> = ({
[auctionsQuery?.data],
);

if (!countQuery.isLoading && BigNumber.from(countQuery.data || 0).eq(0)) {
if (!countQuery.isLoading && (countQuery.data || 0n) === 0n) {
return null;
}
if (!auctionsQuery.isLoading && auctions.length === 0) {
Expand Down Expand Up @@ -218,16 +190,28 @@ const EnglishAuctionCards: React.FC<ListingCardsSectionProps> = ({
);
};

const MarketplaceV3Details: React.FC<
MarketplaceDetailsVersionProps<MarketplaceV3>
> = ({ contract, trackingCategory, features }) => {
interface MarketplaceDetailsVersionProps {
contract: ThirdwebContract;
trackingCategory: string;
features: MarketplaceDetailsProps["features"];
}

const MarketplaceV3Details: React.FC<MarketplaceDetailsVersionProps> = ({
contract,
trackingCategory,
features,
}) => {
const hasDirectListings = features.includes("DirectListings");
const hasEnglishAuctions = features.includes("EnglishAuctions");

return (
<Flex gap={6} flexDirection="column">
<Heading size="title.sm">Listings</Heading>
<ListingStatsV3 contract={contract} features={features} />
<ListingStatsV3
contract={contract}
hasDirectListings={hasDirectListings}
hasEnglishAuctions={hasEnglishAuctions}
/>
{hasDirectListings && contract && (
<DirectListingCards
contract={contract}
Expand Down Expand Up @@ -275,7 +259,7 @@ const dummyMetadata: (idx: number) => ListingData = (idx) => ({
interface ListingCardsProps {
listings: ListingData[];
isLoading: boolean;
trackingCategory: TrackedLinkProps["category"];
trackingCategory: string;
isMarketplaceV1?: boolean;
}
const ListingCards: React.FC<ListingCardsProps> = ({
Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/src/contract-ui/tabs/overview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
(contractTypeData === "marketplace" ||
["DirectListings", "EnglishAuctions"].some((type) =>
detectedFeatureNames.includes(type),
)) && (
)) &&
contractV5 && (
<MarketplaceDetails
contractAddress={contractAddress}
contract={contractV5}
trackingCategory={TRACKING_CATEGORY}
contractType={contractTypeData as "marketplace"}
features={detectedFeatureNames}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
} from "@chakra-ui/react";
import { MediaCell } from "components/contract-pages/table/table-columns/cells/media-cell";
import { ListingDrawer } from "contract-ui/tabs/shared-components/listing-drawer";
import { BigNumber } from "ethers";
import {
type Dispatch,
type SetStateAction,
Expand All @@ -40,6 +39,7 @@ import type {
DirectListing,
EnglishAuction,
} from "thirdweb/extensions/marketplace";
import { max } from "thirdweb/utils";
import { Button, Text } from "tw-components";
import { AddressCopyButton } from "tw-components/AddressCopyButton";
import { LISTING_STATUS } from "./types";
Expand Down Expand Up @@ -162,8 +162,10 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
manualPagination: true,
pageCount: Math.max(
Math.ceil(
BigNumber.from(totalCountQuery.data || 0).toNumber() /
queryParams.count,
Number(
// To avoid overflow issue
max(totalCountQuery.data || 0n, BigInt(Number.MAX_SAFE_INTEGER)),
) / queryParams.count,
),
1,
),
Expand Down
5 changes: 5 additions & 0 deletions packages/thirdweb/src/exports/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,8 @@ export {
export type { NFTMetadata, NFTInput } from "../utils/nft/parseNft.js";

export { parseAbiParams } from "../utils/contract/parse-abi-params.js";

// ------------------------------------------------
// bigint
// ------------------------------------------------
export { max, min } from "../utils/bigint.js";
2 changes: 2 additions & 0 deletions packages/thirdweb/src/utils/bigint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { uint8ArrayToHex } from "./encoding/hex.js";
* @param a - The first BigInt value.
* @param b - The second BigInt value.
* @returns The smaller of the two BigInt values.
* @utils
* @example
* ```ts
* min(1n, 2n)
Expand All @@ -19,6 +20,7 @@ export function min(a: bigint, b: bigint) {
* @param a - The first BigInt value.
* @param b - The second BigInt value.
* @returns The larger of the two BigInt values.
* @utils
* @example
* ```ts
* max(1n, 2n)
Expand Down

0 comments on commit ef9cc55

Please sign in to comment.