Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions lib/price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,18 @@ export interface TokenPriceInfo {
*/
export async function getTokenPrice(
tokenAddress: Address,
client?: typeof publicClient,
): Promise<TokenPriceInfo | null> {
const rpc = client ?? publicClient;
try {
const [priceRaw, totalSupplyRaw] = await Promise.all([
publicClient.readContract({
rpc.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "priceForNextMint",
args: [tokenAddress],
}),
publicClient.readContract({
rpc.readContract({
address: tokenAddress,
abi: erc20Abi,
functionName: "totalSupply",
Expand Down Expand Up @@ -181,19 +183,21 @@ const BLOCKS_PER_24H = BigInt(43200);
*/
export async function get24hPriceChange(
tokenAddress: Address,
client?: typeof publicClient,
): Promise<{ changePercent: number; currentPrice: bigint; previousPrice: bigint } | null> {
const rpc = client ?? publicClient;
try {
const currentBlock = await publicClient.getBlockNumber();
const currentBlock = await rpc.getBlockNumber();
const pastBlock = currentBlock - BLOCKS_PER_24H;

const [currentPrice, previousPrice] = await Promise.all([
publicClient.readContract({
rpc.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "priceForNextMint",
args: [tokenAddress],
}),
publicClient.readContract({
rpc.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "priceForNextMint",
Expand Down Expand Up @@ -236,9 +240,11 @@ const erc20DecimalsAbi = [
*/
export async function getTokenTVL(
tokenAddress: Address,
client?: typeof publicClient,
): Promise<{ tvl: string; tvlRaw: bigint; reserveToken: Address; decimals: number } | null> {
const rpc = client ?? publicClient;
try {
const result = await publicClient.readContract({
const result = await rpc.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "tokenBond",
Expand All @@ -248,7 +254,7 @@ export async function getTokenTVL(
const [, , , , reserveToken, reserveBalance] = result;
const reserveAddr = reserveToken as Address;

const decimals = await publicClient.readContract({
const decimals = await rpc.readContract({
address: reserveAddr,
abi: erc20DecimalsAbi,
functionName: "decimals",
Expand Down
3 changes: 2 additions & 1 deletion src/app/dashboard/writer/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useQuery, useQueryClient, useInfiniteQuery } from "@tanstack/react-quer
import { formatUnits } from "viem";
import { supabase, type Storyline, type Donation } from "../../../../lib/supabase";
import { getTokenTVL } from "../../../../lib/price";
import { browserClient } from "../../../../lib/rpc";
import { RESERVE_LABEL, STORY_FACTORY, EXPLORER_URL } from "../../../../lib/contracts/constants";
import { GENRES, LANGUAGES } from "../../../../lib/genres";
import { DeadlineCountdown } from "../../../components/DeadlineCountdown";
Expand Down Expand Up @@ -370,7 +371,7 @@ function DonationCount({ storylineId, tokenAddress }: { storylineId: number; tok
queryKey: ["donation-count", storylineId, tokenAddress],
queryFn: async () => {
const [tvlData, rows] = await Promise.all([
getTokenTVL(tokenAddress as Address),
getTokenTVL(tokenAddress as Address, browserClient),
supabase
? supabase.from("donations")
.select("amount")
Expand Down
23 changes: 17 additions & 6 deletions src/components/BatchTokenDataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,22 @@ import { browserClient } from "../../lib/rpc";

type BatchTokenDataMap = Map<string, BatchTokenEntry>;

const BatchTokenDataContext = createContext<BatchTokenDataMap>(new Map());
interface BatchTokenDataContextValue {
data: BatchTokenDataMap;
isReady: boolean;
}

const BatchTokenDataContext = createContext<BatchTokenDataContextValue>({
data: new Map(),
isReady: false,
});

export function useBatchTokenData(tokenAddress: string): BatchTokenEntry | undefined {
const map = useContext(BatchTokenDataContext);
return map.get(tokenAddress.toLowerCase());
export function useBatchTokenData(tokenAddress: string): {
entry: BatchTokenEntry | undefined;
isReady: boolean;
} {
const { data, isReady } = useContext(BatchTokenDataContext);
return { entry: data.get(tokenAddress.toLowerCase()), isReady };
}

/**
Expand All @@ -26,15 +37,15 @@ export function BatchTokenDataProvider({
tokenAddresses: Address[];
children: ReactNode;
}) {
const { data } = useQuery({
const { data, isLoading } = useQuery({
queryKey: ["batch-token-data", tokenAddresses.join(",")],
queryFn: () => getBatchTokenData(tokenAddresses, browserClient),
staleTime: 60000,
enabled: tokenAddresses.length > 0,
});

return (
<BatchTokenDataContext.Provider value={data ?? new Map()}>
<BatchTokenDataContext.Provider value={{ data: data ?? new Map(), isReady: !isLoading }}>
{children}
</BatchTokenDataContext.Provider>
);
Expand Down
8 changes: 4 additions & 4 deletions src/components/ClaimRoyalties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState, useCallback, useEffect, useRef } from "react";
import { useWriteContract } from "wagmi";
import { useQuery } from "@tanstack/react-query";
import { formatUnits, type Address } from "viem";
import { browserClient as publicClient } from "../../lib/rpc";
import { browserClient } from "../../lib/rpc";
import { mcv2BondAbi, getTokenTVL } from "../../lib/price";
import { MCV2_BOND, RESERVE_LABEL, EXPLORER_URL, PLOT_TOKEN } from "../../lib/contracts/constants";

Expand Down Expand Up @@ -37,7 +37,7 @@ export function ClaimRoyalties({ tokenAddress, plotCount, beneficiary }: ClaimRo
const { data: royaltyInfo } = useQuery({
queryKey: ["royalty-info", tokenAddress, beneficiary],
queryFn: async () => {
const [balance, claimed] = await publicClient.readContract({
const [balance, claimed] = await browserClient.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "getRoyaltyInfo",
Expand All @@ -51,7 +51,7 @@ export function ClaimRoyalties({ tokenAddress, plotCount, beneficiary }: ClaimRo
// Fetch reserve token decimals dynamically
const { data: tvlData } = useQuery({
queryKey: ["claim-decimals", tokenAddress],
queryFn: () => getTokenTVL(tokenAddress),
queryFn: () => getTokenTVL(tokenAddress, browserClient),
});
const decimals = tvlData?.decimals ?? 18;

Expand Down Expand Up @@ -92,7 +92,7 @@ export function ClaimRoyalties({ tokenAddress, plotCount, beneficiary }: ClaimRo
setTxHash(hash);

setTxState("pending");
await publicClient.waitForTransactionReceipt({ hash });
await browserClient.waitForTransactionReceipt({ hash });

setTxState("done");
} catch (err) {
Expand Down
10 changes: 5 additions & 5 deletions src/components/ReaderPortfolio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useAccount } from "wagmi";
import { useQuery } from "@tanstack/react-query";
import { formatUnits, type Address } from "viem";
import { formatPrice, formatSupply } from "../../lib/format";
import { browserClient as publicClient } from "../../lib/rpc";
import { browserClient } from "../../lib/rpc";
import { erc20Abi, mcv2BondAbi, get24hPriceChange, getTokenTVL } from "../../lib/price";
import { MCV2_BOND, RESERVE_LABEL, STORY_FACTORY } from "../../lib/contracts/constants";
import { supabase, type Storyline } from "../../lib/supabase";
Expand Down Expand Up @@ -45,7 +45,7 @@ export function ReaderPortfolio() {
args: [address],
}));

const balanceResults = await publicClient.multicall({ contracts: balanceCalls });
const balanceResults = await browserClient.multicall({ contracts: balanceCalls });

// Filter to only storylines with non-zero balance
const held = storylines
Expand All @@ -61,14 +61,14 @@ export function ReaderPortfolio() {
const balance = balanceResult.result as bigint;
try {
const [price, priceChangeResult, tvlResult] = await Promise.all([
publicClient.readContract({
browserClient.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "priceForNextMint",
args: [tokenAddr],
}),
get24hPriceChange(tokenAddr).catch(() => null),
getTokenTVL(tokenAddr).catch(() => null),
get24hPriceChange(tokenAddr, browserClient).catch(() => null),
getTokenTVL(tokenAddr, browserClient).catch(() => null),
]);

const priceBI = BigInt(price);
Expand Down
13 changes: 7 additions & 6 deletions src/components/StoryCardStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useQuery } from "@tanstack/react-query";
import { type Address } from "viem";
import { getTokenTVL, getTokenPrice } from "../../lib/price";
import { browserClient } from "../../lib/rpc";
import { RESERVE_LABEL } from "../../lib/contracts/constants";
import { useBatchTokenData } from "./BatchTokenDataProvider";

Expand All @@ -21,13 +22,13 @@ export function StoryCardStats({ tokenAddress }: { tokenAddress: string }) {

const { data: priceInfo } = useQuery({
queryKey: ["card-price", tokenAddress],
queryFn: () => getTokenPrice(addr),
queryFn: () => getTokenPrice(addr, browserClient),
staleTime: 60000,
});

const { data: tvlData } = useQuery({
queryKey: ["card-tvl", tokenAddress],
queryFn: () => getTokenTVL(addr),
queryFn: () => getTokenTVL(addr, browserClient),
staleTime: 60000,
});

Expand All @@ -49,15 +50,15 @@ export function StoryCardStats({ tokenAddress }: { tokenAddress: string }) {
/** TVL-only display for home page book cards.
* Uses batch context when available (home page), falls back to individual fetch. */
export function StoryCardTVL({ tokenAddress }: { tokenAddress: string }) {
const batchEntry = useBatchTokenData(tokenAddress);
const { entry: batchEntry, isReady } = useBatchTokenData(tokenAddress);
const addr = tokenAddress as Address;

// Fall back to individual fetch only if batch data not available
// Only fall back to individual fetch AFTER batch has settled
const { data: individualTvl } = useQuery({
queryKey: ["card-tvl", tokenAddress],
queryFn: () => getTokenTVL(addr),
queryFn: () => getTokenTVL(addr, browserClient),
staleTime: 60000,
enabled: !batchEntry,
enabled: isReady && !batchEntry,
});

const tvlData = batchEntry?.tvl ?? individualTvl;
Expand Down
6 changes: 3 additions & 3 deletions src/components/WriterTradingStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useQuery } from "@tanstack/react-query";
import { formatUnits, type Address } from "viem";
import { browserClient as publicClient } from "../../lib/rpc";
import { browserClient } from "../../lib/rpc";
import { mcv2BondAbi, getTokenTVL } from "../../lib/price";
import { MCV2_BOND, RESERVE_LABEL } from "../../lib/contracts/constants";
import { formatPrice } from "../../lib/format";
Expand All @@ -20,13 +20,13 @@ export function WriterTradingStats({ storyline }: WriterTradingStatsProps) {
queryKey: ["writer-stats", tokenAddress],
queryFn: async () => {
const [priceRaw, tvlData] = await Promise.all([
publicClient.readContract({
browserClient.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "priceForNextMint",
args: [tokenAddress],
}),
getTokenTVL(tokenAddress),
getTokenTVL(tokenAddress, browserClient),
]);
const decimals = tvlData?.decimals ?? 18;
return {
Expand Down
Loading