+ {/* CURRENT MESSAGE */}
+
+
+
{lb.topMessage || No message yet}
@@ -350,21 +387,39 @@ function TableRow({ lb, views, ethPrice, onBuy }: { lb: Leaderboard; views: numb
-
+ {isStreaming ? (
+
+ ) : (
+
+ )}
)
@@ -380,6 +435,8 @@ export default function MarketplacePage() {
const [ecoStats, setEcoStats] = useState({ markees: 0, messages: 0, usd: 0 })
const [buyModal, setBuyModal] = useState
(null)
+ const [streamCreate, setStreamCreate] = useState(null)
+ const [streamTarget, setStreamTarget] = useState<{ board: `0x${string}`; markee: StreamTarget } | null>(null)
const [search, setSearch] = useState('')
const [factory, setFactory] = useState('all')
@@ -459,10 +516,11 @@ export default function MarketplacePage() {
const bp = priceToOvertake(b)
return (ap > bp ? 1 : ap < bp ? -1 : 0) * dir
}
- // raised (default)
- const af = BigInt(a.totalFundsRaw || '0')
- const bf = BigInt(b.totalFundsRaw || '0')
- return (af > bf ? 1 : af < bf ? -1 : 0) * dir
+ // default (raised) — rank by cumulative funds: fixed boards' lump-sum total, streaming boards'
+ // total streamed-in. One cumulative-$ axis so the "Total raised" column and its sort are honest.
+ const at = BigInt(a.totalFundsRaw || '0')
+ const bt = BigInt(b.totalFundsRaw || '0')
+ return (at > bt ? 1 : at < bt ? -1 : 0) * dir
})
}, [filtered, sortKey, sortDir, viewsMap])
@@ -481,8 +539,8 @@ export default function MarketplacePage() {
// Top leaderboard for featured hero (by totalFunds, must have a message)
const featured = useMemo(() => leaderboards.filter(lb => lb.topMessage).sort((a, b) => {
- const af = BigInt(a.totalFundsRaw || '0'), bf = BigInt(b.totalFundsRaw || '0')
- return bf > af ? 1 : bf < af ? -1 : 0
+ const at = BigInt(a.totalFundsRaw || '0'), bt = BigInt(b.totalFundsRaw || '0')
+ return bt > at ? 1 : bt < at ? -1 : 0
})[0] ?? null, [leaderboards])
const featuredViews = featured?.topMarkeeAddress
@@ -618,6 +676,7 @@ export default function MarketplacePage() {
views={viewsMap.get((lb.topMarkeeAddress || '').toLowerCase()) ?? 0}
ethPrice={ethPrice}
onBuy={() => setBuyModal(lb)}
+ onStream={() => setStreamCreate(lb)}
/>
))
)}
@@ -647,6 +706,28 @@ export default function MarketplacePage() {
onSuccess={() => setBuyModal(null)}
/>
)}
+
+ {streamCreate && (
+ setStreamCreate(null)}
+ onCreated={(addr, message, name) => {
+ const board = streamCreate.address as `0x${string}`
+ setStreamCreate(null)
+ setStreamTarget({ board, markee: { address: addr, message, name } })
+ }}
+ />
+ )}
+
+ {streamTarget && (
+ setStreamTarget(null)}
+ onSuccess={() => setStreamTarget(null)}
+ />
+ )}
)
}
diff --git a/frontend/app/owners/page.tsx b/frontend/app/owners/page.tsx
index 492e0f3d..a48bf23f 100644
--- a/frontend/app/owners/page.tsx
+++ b/frontend/app/owners/page.tsx
@@ -68,7 +68,7 @@ function PhasesViz() {
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index 579c31fc..b6d4980d 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -10,12 +10,12 @@ import { HeroBackground } from '@/components/backgrounds/HeroBackground'
import { useFixedMarkees } from '@/lib/contracts/useFixedMarkees'
import { useFixedViews } from '@/hooks/useFixedViews'
import { useEthPrice } from '@/hooks/useEthPrice'
-import { V13_LEADERBOARDS } from '@/lib/contracts/addresses'
import { formatUsd } from '@/lib/utils'
import { FixedPriceModal } from '@/components/modals/FixedPriceModal'
import type { FixedMarkee } from '@/lib/contracts/useFixedMarkees'
import { RevnetBuyWidget } from '@/components/widgets/RevnetBuyWidget'
import { BuyMessageModal } from '@/components/modals/BuyMessageModal'
+import { StrategyBadge } from '@/components/StrategyBadge'
const MONO = "var(--font-jetbrains-mono), 'JetBrains Mono', monospace"
const MARKETPLACE_TEASER_COLS = '190px 110px 1fr 74px 120px'
@@ -340,8 +340,8 @@ export default function Home() {
if (!data) return
const leaderboards: { address: string; platform: string; topFundsAddedRaw: string; totalFundsRaw: string; markeeCount: number; isLegacy?: boolean; [key: string]: any }[] = data.leaderboards ?? []
const active = leaderboards.filter(lb => BigInt(lb.topFundsAddedRaw ?? '0') > 0n)
- // Top 5 by totalFundsRaw across all platforms (API already sorts descending); exclude gamed leaderboards
- setTop5Eco(leaderboards.filter(lb => !lb.gamed && BigInt(lb.totalFundsRaw ?? '0') > 0n).slice(0, 5))
+ // Top 5 by effectiveRate across all strategies (API already sorts descending); exclude gamed
+ setTop5Eco(leaderboards.filter(lb => !lb.gamed && BigInt(lb.effectiveRateRaw ?? '0') > 0n).slice(0, 5))
setTop5EcoLoading(false)
const messages = active.reduce(
(sum, lb) => sum + (lb.isLegacy ? lb.markeeCount : Math.max(0, lb.markeeCount - 1)),
@@ -511,6 +511,7 @@ export default function Home() {