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
3 changes: 3 additions & 0 deletions lib/contracts/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export const PLOT_TOKEN = (IS_TESTNET
/** Human-readable label for the reserve token */
export const RESERVE_LABEL = "PLOT";

/** PLOT max supply (bonding curve cap) — used for FDV calculation */
export const PLOT_MAX_SUPPLY = 1_000_000;

// ---------------------------------------------------------------------------
// Supported Zap input tokens (Base)
// ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "plotlink",
"version": "0.1.34",
"version": "0.1.35",
"private": true,
"workspaces": [
"packages/*"
Expand Down
14 changes: 7 additions & 7 deletions src/app/api/airdrop/status/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,28 @@ export async function GET() {
}
const totalParticipants = uniqueAddresses.size;

// Milestone status
const currentMcap = latestPrice?.mcap_usd ?? 0;
// Milestone status — mcap_usd column now stores FDV (price × max supply)
const currentFdv = latestPrice?.mcap_usd ?? 0;
const milestones = {
bronze: {
mcap: AIRDROP_CONFIG.MILESTONES.BRONZE.mcap,
pct: AIRDROP_CONFIG.MILESTONES.BRONZE.pct,
reached: currentMcap >= AIRDROP_CONFIG.MILESTONES.BRONZE.mcap,
reached: currentFdv >= AIRDROP_CONFIG.MILESTONES.BRONZE.mcap,
},
silver: {
mcap: AIRDROP_CONFIG.MILESTONES.SILVER.mcap,
pct: AIRDROP_CONFIG.MILESTONES.SILVER.pct,
reached: currentMcap >= AIRDROP_CONFIG.MILESTONES.SILVER.mcap,
reached: currentFdv >= AIRDROP_CONFIG.MILESTONES.SILVER.mcap,
},
gold: {
mcap: AIRDROP_CONFIG.MILESTONES.GOLD.mcap,
pct: AIRDROP_CONFIG.MILESTONES.GOLD.pct,
reached: currentMcap >= AIRDROP_CONFIG.MILESTONES.GOLD.mcap,
reached: currentFdv >= AIRDROP_CONFIG.MILESTONES.GOLD.mcap,
},
diamond: {
mcap: AIRDROP_CONFIG.MILESTONES.DIAMOND.mcap,
pct: AIRDROP_CONFIG.MILESTONES.DIAMOND.pct,
reached: currentMcap >= AIRDROP_CONFIG.MILESTONES.DIAMOND.mcap,
reached: currentFdv >= AIRDROP_CONFIG.MILESTONES.DIAMOND.mcap,
},
};

Expand All @@ -72,7 +72,7 @@ export async function GET() {
timeRemainingDays: Math.ceil(remainingMs / (1000 * 60 * 60 * 24)),
timeElapsedPercent: totalMs > 0 ? Math.min(100, Math.round((elapsedMs / totalMs) * 100)) : 0,
poolAmount: AIRDROP_CONFIG.POOL_AMOUNT,
currentMcap,
currentFdv,
latestPriceUsd: latestPrice?.price_usd ?? null,
milestones,
totalPointsEarned,
Expand Down
11 changes: 6 additions & 5 deletions src/app/api/cron/airdrop-price/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { formatUnits } from "viem";
import { createServerClient } from "../../../../../lib/supabase";
import { getPlotUsdPrice } from "../../../../../lib/usd-price";
import { publicClient } from "../../../../../lib/rpc";
import { PLOT_TOKEN } from "../../../../../lib/contracts/constants";
import { PLOT_TOKEN, PLOT_MAX_SUPPLY } from "../../../../../lib/contracts/constants";
import { erc20Abi } from "../../../../../lib/price";

function verifyCron(req: Request): boolean {
Expand Down Expand Up @@ -67,20 +67,21 @@ export async function GET(req: Request) {
return NextResponse.json({ skipped: true, reason: "Supply fetch failed" }, { status: 200 });
}

const mcapUsd = priceUsd * supplyFormatted;
// Store FDV (price × max supply) instead of MCap for milestone tracking
const fdvUsd = priceUsd * PLOT_MAX_SUPPLY;

const { error } = await supabase.from("pl_daily_prices").insert({
recorded_at: todayUtc,
price_usd: priceUsd,
supply: supplyFormatted,
mcap_usd: mcapUsd,
mcap_usd: fdvUsd, // column stores FDV (price × max supply)
});

if (error) {
console.error("[airdrop-price] Insert failed:", error.message);
return NextResponse.json({ error: "Insert failed" }, { status: 500 });
}

console.info(`[airdrop-price] Snapshot recorded: date=${todayUtc} price=${priceUsd} supply=${supplyFormatted} mcap=${mcapUsd}`);
return NextResponse.json({ recorded: true, date: todayUtc, priceUsd, supply: supplyFormatted, mcapUsd });
console.info(`[airdrop-price] Snapshot recorded: date=${todayUtc} price=${priceUsd} supply=${supplyFormatted} fdv=${fdvUsd}`);
return NextResponse.json({ recorded: true, date: todayUtc, priceUsd, supply: supplyFormatted, fdvUsd });
}
6 changes: 3 additions & 3 deletions src/app/token/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default function TokenPage() {
<div className="border-border rounded border p-5">
<h3 className="text-foreground text-sm font-bold mb-4">Token Information</h3>

{/* Stats Grid — Price + Market Cap */}
{/* Stats Grid — Price + FDV */}
<div className="grid grid-cols-2 gap-3 mb-4">
<div className="border-border bg-surface rounded border p-3">
<div className="text-muted text-[10px] uppercase tracking-wider mb-1">Price</div>
Expand All @@ -142,12 +142,12 @@ export default function TokenPage() {
)}
</div>
<div className="border-border bg-surface rounded border p-3">
<div className="text-muted text-[10px] uppercase tracking-wider mb-1">Market Cap</div>
<div className="text-muted text-[10px] uppercase tracking-wider mb-1">FDV</div>
{tokenInfoLoading ? (
<div className="bg-border h-6 animate-pulse rounded" />
) : tokenInfo ? (
<div className="text-foreground text-sm font-bold">
${formatNumber(tokenInfo.marketCap)}
${formatNumber(tokenInfo.fdv)}
</div>
) : (
<div className="text-muted text-sm">—</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/airdrop/CampaignHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface StatusData {
timeRemainingDays: number;
timeElapsedPercent: number;
poolAmount: number;
currentMcap: number;
currentFdv: number;
latestPriceUsd: number | null;
milestones: {
bronze: { mcap: number; pct: number; reached: boolean };
Expand Down
2 changes: 1 addition & 1 deletion src/components/airdrop/MilestoneTrack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { formatUsdValue } from "../../../lib/usd-price";

interface StatusData {
poolAmount: number;
currentMcap: number;
currentFdv: number;
latestPriceUsd: number | null;
milestones: {
bronze: { mcap: number; pct: number; reached: boolean };
Expand Down
2 changes: 1 addition & 1 deletion src/components/airdrop/WeeklySnapshots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function WeeklySnapshots() {
<span className="text-foreground">{snap.newReferrals}</span>
</div>
<div className="text-muted">
MCap:{" "}
FDV:{" "}
<span className="text-foreground">
{formatUsdValue(snap.mcapStart)} → {formatUsdValue(snap.mcapEnd)}
</span>{" "}
Expand Down
8 changes: 4 additions & 4 deletions src/hooks/useTokenInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useQuery } from "@tanstack/react-query";
import { formatEther } from "viem";
import { browserClient } from "../../lib/rpc";
import { PLOT_TOKEN, MCV2_BOND, HUNT } from "../../lib/contracts/constants";
import { PLOT_TOKEN, MCV2_BOND, HUNT, PLOT_MAX_SUPPLY } from "../../lib/contracts/constants";

const USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" as const;
const ONEINCH_SPOT_PRICE_AGGREGATOR = "0x00000000000D6FFc74A8feb35aF5827bf57f6786" as const;
Expand Down Expand Up @@ -44,7 +44,7 @@ const ERC20_SUPPLY_ABI = [

export interface TokenInfo {
price: number;
marketCap: number;
fdv: number;
totalSupply: number;
priceChange24h: number | null;
}
Expand Down Expand Up @@ -88,7 +88,7 @@ export function useTokenInfo() {
]);

const totalSupply = Number(formatEther(supply));
const marketCap = price * totalSupply;
const fdv = price * PLOT_MAX_SUPPLY;

// 24h price change via block diff (~43200 blocks = 1 day on Base @ 2s)
let priceChange24h: number | null = null;
Expand All @@ -115,7 +115,7 @@ export function useTokenInfo() {
// Token may not have existed 24h ago
}

return { price, marketCap, totalSupply, priceChange24h } satisfies TokenInfo;
return { price, fdv, totalSupply, priceChange24h } satisfies TokenInfo;
},
staleTime: 5 * 60 * 1000,
refetchInterval: 5 * 60 * 1000,
Expand Down
Loading