From 130481d3157453f60195d8de15493d20fc7876d4 Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Wed, 22 Apr 2026 10:40:15 +0900 Subject: [PATCH] [#927] Update milestone tiers: add Diamond, adjust percentages, use emojis - Config: Add DIAMOND tier ($100M/100%), change GOLD from $70M/100% to $50M/50% - Status API: Return 4 milestones (bronze/silver/gold/diamond) - Points API: Add diamond to estimatedAirdrop - Results API: Add Diamond check, use emoji prefixes in milestone names - Finalize script: Add Diamond tier to determination logic - UserPoints: Display 4 tiers with emoji labels - CampaignHero/MilestoneTrack: Update StatusData interfaces for 4 tiers Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/airdrop/config.ts | 7 +++++-- package.json | 2 +- scripts/airdrop-finalize.ts | 9 ++++++--- src/app/api/airdrop/points/route.ts | 3 ++- src/app/api/airdrop/results/route.ts | 10 ++++++---- src/app/api/airdrop/status/route.ts | 5 +++++ src/components/airdrop/CampaignHero.tsx | 1 + src/components/airdrop/MilestoneTrack.tsx | 1 + src/components/airdrop/UserPoints.tsx | 15 ++++++++++----- 9 files changed, 37 insertions(+), 16 deletions(-) diff --git a/lib/airdrop/config.ts b/lib/airdrop/config.ts index e162e5e9..fe3baa4c 100644 --- a/lib/airdrop/config.ts +++ b/lib/airdrop/config.ts @@ -18,6 +18,7 @@ export interface AirdropConfig { readonly BRONZE: Milestone; readonly SILVER: Milestone; readonly GOLD: Milestone; + readonly DIAMOND: Milestone; }; readonly LOCKER_ID: string | null; readonly POINTS: { @@ -58,7 +59,8 @@ const TEST_CONFIG: AirdropConfig = { MILESTONES: { BRONZE: { mcap: 7_000, pct: 10 }, SILVER: { mcap: 10_000, pct: 30 }, - GOLD: { mcap: 50_000, pct: 100 }, + GOLD: { mcap: 35_000, pct: 50 }, + DIAMOND: { mcap: 50_000, pct: 100 }, }, LOCKER_ID: null, POINTS, @@ -73,7 +75,8 @@ const PROD_CONFIG: AirdropConfig = { MILESTONES: { BRONZE: { mcap: 1_000_000, pct: 10 }, SILVER: { mcap: 10_000_000, pct: 30 }, - GOLD: { mcap: 70_000_000, pct: 100 }, + GOLD: { mcap: 50_000_000, pct: 50 }, + DIAMOND: { mcap: 100_000_000, pct: 100 }, }, LOCKER_ID: null, POINTS, diff --git a/package.json b/package.json index 8262bb05..b3a5cfb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plotlink", - "version": "0.1.33", + "version": "0.1.34", "private": true, "workspaces": [ "packages/*" diff --git a/scripts/airdrop-finalize.ts b/scripts/airdrop-finalize.ts index f875b488..dda656fb 100644 --- a/scripts/airdrop-finalize.ts +++ b/scripts/airdrop-finalize.ts @@ -66,14 +66,17 @@ async function computeTwap(): Promise { function determineMilestone(twapMcap: number): { tier: string; pct: number } { const { MILESTONES } = AIRDROP_CONFIG; + if (twapMcap >= MILESTONES.DIAMOND.mcap) { + return { tier: "\uD83D\uDC8E Diamond", pct: MILESTONES.DIAMOND.pct }; + } if (twapMcap >= MILESTONES.GOLD.mcap) { - return { tier: "Gold", pct: MILESTONES.GOLD.pct }; + return { tier: "\uD83E\uDD47 Gold", pct: MILESTONES.GOLD.pct }; } if (twapMcap >= MILESTONES.SILVER.mcap) { - return { tier: "Silver", pct: MILESTONES.SILVER.pct }; + return { tier: "\uD83E\uDD48 Silver", pct: MILESTONES.SILVER.pct }; } if (twapMcap >= MILESTONES.BRONZE.mcap) { - return { tier: "Bronze", pct: MILESTONES.BRONZE.pct }; + return { tier: "\uD83E\uDD49 Bronze", pct: MILESTONES.BRONZE.pct }; } return { tier: "None", pct: 0 }; } diff --git a/src/app/api/airdrop/points/route.ts b/src/app/api/airdrop/points/route.ts index 04452fd9..524a4ae0 100644 --- a/src/app/api/airdrop/points/route.ts +++ b/src/app/api/airdrop/points/route.ts @@ -82,8 +82,9 @@ export async function GET(req: NextRequest) { bronze: Math.round((sharePercent / 100) * AIRDROP_CONFIG.POOL_AMOUNT * (AIRDROP_CONFIG.MILESTONES.BRONZE.pct / 100)), silver: Math.round((sharePercent / 100) * AIRDROP_CONFIG.POOL_AMOUNT * (AIRDROP_CONFIG.MILESTONES.SILVER.pct / 100)), gold: Math.round((sharePercent / 100) * AIRDROP_CONFIG.POOL_AMOUNT * (AIRDROP_CONFIG.MILESTONES.GOLD.pct / 100)), + diamond: Math.round((sharePercent / 100) * AIRDROP_CONFIG.POOL_AMOUNT * (AIRDROP_CONFIG.MILESTONES.DIAMOND.pct / 100)), } - : { bronze: 0, silver: 0, gold: 0 }; + : { bronze: 0, silver: 0, gold: 0, diamond: 0 }; return NextResponse.json({ address, diff --git a/src/app/api/airdrop/results/route.ts b/src/app/api/airdrop/results/route.ts index 9ebf7582..c93a4114 100644 --- a/src/app/api/airdrop/results/route.ts +++ b/src/app/api/airdrop/results/route.ts @@ -38,12 +38,14 @@ export async function GET() { // Determine milestone from distributed percentage const distributedPct = (distributedPlot / poolAmount) * 100; let milestone: string; - if (distributedPct >= AIRDROP_CONFIG.MILESTONES.GOLD.pct - 0.1) { - milestone = "Gold"; + if (distributedPct >= AIRDROP_CONFIG.MILESTONES.DIAMOND.pct - 0.1) { + milestone = "\uD83D\uDC8E Diamond"; + } else if (distributedPct >= AIRDROP_CONFIG.MILESTONES.GOLD.pct - 0.1) { + milestone = "\uD83E\uDD47 Gold"; } else if (distributedPct >= AIRDROP_CONFIG.MILESTONES.SILVER.pct - 0.1) { - milestone = "Silver"; + milestone = "\uD83E\uDD48 Silver"; } else if (distributedPct >= AIRDROP_CONFIG.MILESTONES.BRONZE.pct - 0.1) { - milestone = "Bronze"; + milestone = "\uD83E\uDD49 Bronze"; } else { milestone = "None"; } diff --git a/src/app/api/airdrop/status/route.ts b/src/app/api/airdrop/status/route.ts index 01618846..d89a14e8 100644 --- a/src/app/api/airdrop/status/route.ts +++ b/src/app/api/airdrop/status/route.ts @@ -59,6 +59,11 @@ export async function GET() { pct: AIRDROP_CONFIG.MILESTONES.GOLD.pct, reached: currentMcap >= 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, + }, }; return NextResponse.json({ diff --git a/src/components/airdrop/CampaignHero.tsx b/src/components/airdrop/CampaignHero.tsx index bd45889f..3ec33b31 100644 --- a/src/components/airdrop/CampaignHero.tsx +++ b/src/components/airdrop/CampaignHero.tsx @@ -15,6 +15,7 @@ interface StatusData { bronze: { mcap: number; pct: number; reached: boolean }; silver: { mcap: number; pct: number; reached: boolean }; gold: { mcap: number; pct: number; reached: boolean }; + diamond: { mcap: number; pct: number; reached: boolean }; }; totalPointsEarned: number; totalParticipants: number; diff --git a/src/components/airdrop/MilestoneTrack.tsx b/src/components/airdrop/MilestoneTrack.tsx index eeee42dc..f5840386 100644 --- a/src/components/airdrop/MilestoneTrack.tsx +++ b/src/components/airdrop/MilestoneTrack.tsx @@ -12,6 +12,7 @@ interface StatusData { bronze: { mcap: number; pct: number; reached: boolean }; silver: { mcap: number; pct: number; reached: boolean }; gold: { mcap: number; pct: number; reached: boolean }; + diamond: { mcap: number; pct: number; reached: boolean }; }; } diff --git a/src/components/airdrop/UserPoints.tsx b/src/components/airdrop/UserPoints.tsx index eb9f613e..062e4bde 100644 --- a/src/components/airdrop/UserPoints.tsx +++ b/src/components/airdrop/UserPoints.tsx @@ -25,7 +25,7 @@ interface PointsData { referredBy: string | null; referredUsersCount: number; }; - estimatedAirdrop: { bronze: number; silver: number; gold: number }; + estimatedAirdrop: { bronze: number; silver: number; gold: number; diamond: number }; } interface StatusData { @@ -109,12 +109,17 @@ function UserPointsInner({ address }: { address: string }) { {/* Estimated airdrop */}
- {(["bronze", "silver", "gold"] as const).map((tier) => { - const amount = data.estimatedAirdrop[tier]; + {([ + { key: "bronze", label: "\uD83E\uDD49 Bronze" }, + { key: "silver", label: "\uD83E\uDD48 Silver" }, + { key: "gold", label: "\uD83E\uDD47 Gold" }, + { key: "diamond", label: "\uD83D\uDC8E Diamond" }, + ] as const).map(({ key, label }) => { + const amount = data.estimatedAirdrop[key]; const usdVal = price && amount > 0 ? formatUsdValue(amount * price) : null; return ( -
- Est. if {tier.charAt(0).toUpperCase() + tier.slice(1)}:{" "} +
+ Est. if {label}:{" "} {amount.toLocaleString()} PLOT