Skip to content

Commit d03ac5d

Browse files
authored
Merge pull request #18 from 1Hive/fix/embed-integration-polish
Fix embed prompt: stop treating an unverified board as nonexistent
2 parents c42ebb3 + bceccdb commit d03ac5d

5 files changed

Lines changed: 67 additions & 22 deletions

File tree

‎frontend/app/api/streaming/leaderboards/route.ts‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,28 @@ export async function GET(request: Request) {
8181
return results.flat()
8282
}
8383

84+
// topMarkee()/topRate() -- the contract's own ENFORCED top, not getTopMarkees(1), which ranks
85+
// live by current aggregate rate. Those two diverge whenever ranking has drifted (someone's rate
86+
// just increased and outranks the enforced #1, but claimTop() hasn't run yet to promote them --
87+
// the streaming-keeper cron heals this periodically, it isn't instantaneous). Using getTopMarkees
88+
// here meant this API -- and everything downstream of it (marketplace, hero cards, embeds) --
89+
// could report a "top message" that wasn't actually the one being paid, which is what the
90+
// contract's own topMarkee/topRate state actually governs.
8491
const metaCalls = addresses.flatMap(addr => [
8592
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'leaderboardName' as const },
8693
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'totalLeaderboardFunds' as const },
8794
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'markeeCount' as const },
8895
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'beneficiaryAddress' as const },
8996
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'admin' as const },
90-
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'getTopMarkees' as const, args: [1n] },
97+
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'topMarkee' as const },
98+
{ address: addr, abi: StreamingLeaderboardABI, functionName: 'topRate' as const },
9199
])
92-
const CALLS_PER_BOARD = 6
100+
const CALLS_PER_BOARD = 7
93101
const metaResults = await chunkedMulticall(metaCalls as Parameters<typeof client.multicall>[0]['contracts'])
94102

95103
const topMarkeeAddresses: (`0x${string}` | null)[] = addresses.map((_, i) => {
96-
const topResult = metaResults[i * CALLS_PER_BOARD + 5]?.result as [string[], bigint[]] | undefined
97-
return (topResult?.[0]?.[0] ?? null) as `0x${string}` | null
104+
const addr = metaResults[i * CALLS_PER_BOARD + 5]?.result as `0x${string}` | undefined
105+
return addr && addr !== '0x0000000000000000000000000000000000000000' ? addr : null
98106
})
99107

100108
const markeeCalls = topMarkeeAddresses.flatMap(addr =>
@@ -151,8 +159,8 @@ export async function GET(request: Request) {
151159
const markeeCount = (metaResults[b + 2]?.result as bigint) ?? 0n
152160
const beneficiary = (metaResults[b + 3]?.result as string) ?? ''
153161
const admin = (metaResults[b + 4]?.result as string) ?? ''
154-
const topResult = metaResults[b + 5]?.result as [string[], bigint[]] | undefined
155-
const topRate = topResult?.[1]?.[0] ?? 0n
162+
// b + 5 is topMarkee() (already extracted into topMarkeeAddresses above); b + 6 is topRate().
163+
const topRate = (metaResults[b + 6]?.result as bigint) ?? 0n
156164

157165
let topMessage: string | null = null
158166
let topMessageOwner: string | null = null

‎frontend/app/marketplace/page.tsx‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ interface Leaderboard {
5757
isLegacy?: boolean
5858
// Populated for boards with no inherent platform tag (the shared "For Sale" factory) so Served On
5959
// can be derived from actual verification instead of a creation-time tag.
60-
linkedFiles?: { verified: boolean }[]
60+
linkedFiles?: { verified: boolean; repoOwner?: string; repoFullName?: string }[]
6161
}
6262

6363
// ── Helpers ───────────────────────────────────────────────────────────────────
@@ -71,15 +71,22 @@ function extractDomain(url: string): string {
7171
try { return new URL(url).hostname.replace(/^www\./, '') } catch { return url }
7272
}
7373

74+
// GitHub-served boards show the repo org (matching ServedOnCell on /markee/[address]), not a flat
75+
// "GitHub" label -- falls back to that only if the verified linked file's repoOwner isn't available.
76+
function verifiedGithubOwner(lb: Leaderboard): string | null {
77+
return (lb.linkedFiles ?? []).find(f => f.verified)?.repoOwner ?? null
78+
}
79+
7480
function servedOnLabel(lb: Leaderboard): string {
75-
if (lb.platform === 'github') return 'GitHub'
7681
if (lb.platform === 'superfluid') return 'Superfluid'
7782
if (lb.verifiedUrl) {
7883
const domain = extractDomain(lb.verifiedUrl)
7984
const extra = (lb.verifiedUrls?.length ?? 1) - 1
8085
return extra > 0 ? `${domain} +${extra}` : domain
8186
}
82-
if ((lb.linkedFiles ?? []).some(f => f.verified)) return 'GitHub'
87+
const githubOwner = verifiedGithubOwner(lb)
88+
if (githubOwner) return githubOwner
89+
if (lb.platform === 'github' || (lb.linkedFiles ?? []).some(f => f.verified)) return 'GitHub'
8390
return lb.leaderboardName || lb.address.slice(0, 8) + '...'
8491
}
8592

‎frontend/components/StreamingBoardDetail.tsx‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,21 @@ export function StreamingBoardDetail({ board }: { board: Address }) {
147147
.finally(() => setViewsFetching(false))
148148
}, [markeeAddrKey])
149149

150+
// The board's ENFORCED top (topMarkee()/topRate(), via useTopSince) can differ from markees[0] --
151+
// markees is ordered by getTopMarkees, a LIVE ranking by current rate, while the contract only
152+
// actually promotes a new #1 (and starts streaming to them) once claimTop() runs, which the
153+
// streaming-keeper cron does periodically, not instantly. Using markees[0] here meant the hero
154+
// card (and the row highlighted as "featured" below) could show a message that looked like it was
155+
// winning by current rate but wasn't actually the one being paid. Falls back to markees[0] only
156+
// while topSince is still loading, so the hero isn't blank on first paint.
157+
const enforcedTopMarkee = topSince
158+
? markees.find(m => m.address.toLowerCase() === topSince.address.toLowerCase()) ?? markees[0]
159+
: markees[0]
160+
150161
// Track + increment a view for the top message, mirroring the fixed reader. The POST both
151162
// increments (rate-limited per IP server-side) and returns the current total for display.
152-
const topAddress = markees[0]?.address
153-
const topMessage = markees[0]?.message
163+
const topAddress = enforcedTopMarkee?.address
164+
const topMessage = enforcedTopMarkee?.message
154165
useEffect(() => {
155166
if (!topAddress || !topMessage) return
156167
fetch('/api/views', {
@@ -167,11 +178,11 @@ export function StreamingBoardDetail({ board }: { board: Address }) {
167178
.catch(() => {})
168179
}, [topAddress, topMessage])
169180

170-
const topMarkee = markees[0] ?? null
181+
const topMarkee = enforcedTopMarkee ?? null
171182
const topViews = topMarkee ? (viewsMap.get(topMarkee.address.toLowerCase()) ?? 0) : 0
172183
// "Total views" in the metrics bar is every message on this board, not just the current top one.
173184
const totalViewsSum = Array.from(viewsMap.values()).reduce((sum, v) => sum + v, 0)
174-
const topMonthlyWei = markees[0]?.rate ? ratePerSecToMonthly(markees[0].rate) : undefined
185+
const topMonthlyWei = enforcedTopMarkee?.rate ? ratePerSecToMonthly(enforcedTopMarkee.rate) : undefined
175186

176187
const streamedEthLabel = `${streamedEth.toFixed(6)} ETH`
177188
// Same "digit visibly ticks ~1x/sec" derivation as the claim card, applied to the USD display --
@@ -299,7 +310,11 @@ export function StreamingBoardDetail({ board }: { board: Address }) {
299310
key={m.address}
300311
markee={m}
301312
rank={i + 1}
302-
featured={i === 0}
313+
// Highlight the ENFORCED top (topSince.address, from topMarkee()), not just
314+
// whichever row currently ranks first by live rate -- see enforcedTopMarkee
315+
// above for why those can differ. Falls back to position 0 only while topSince
316+
// is still loading, so some row is highlighted on first paint.
317+
featured={topSince ? topSince.address.toLowerCase() === m.address.toLowerCase() : i === 0}
303318
board={board}
304319
boardAdmin={meta?.admin}
305320
topSince={topSince}

‎frontend/components/board-detail/shared.tsx‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,15 @@ Then fetch /api/markee/leaderboards in the trigger component.
15151515
Find the entry where address matches "${address}" (case-insensitive).
15161516
Fields: topMessage, topMessageOwner, topFundsAddedRaw, minimumPrice, topMarkeeAddress
15171517
1518+
This is the PUBLIC marketplace listing -- it only includes leaderboards that have completed Markee's
1519+
own "Verify Embed" check, as a spam filter for the public listing, not a directory of every
1520+
leaderboard that exists. A brand-new integration's address will not appear here yet -- that's
1521+
expected, not a sign the address is wrong. Have the trigger component fall back to a "be first!"
1522+
empty state when there's no match, not an error, and build the rest of this flow regardless.
1523+
Verification itself only checks that the data-markee-address attribute is present in your
1524+
server-rendered HTML (see the implementation notes below) -- it doesn't depend on this fetch
1525+
succeeding. Once verified, this same code starts resolving real data automatically.
1526+
15181527
### On-chain reads
15191528
Use wagmi useReadContract / useReadContracts for:
15201529
- minimumPrice, maxMessageLength (in both components or passed as props)

‎frontend/lib/embedPrompt/fragments.ts‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,9 @@ const FRAMEWORK_LABEL: Record<EmbedFramework, string> = {
3636
}
3737

3838
// ── Core identity ─────────────────────────────────────────────────────────────
39-
// The canonical-reference line is a deliberate steal from logo.dev's own quickstart prompt: a
40-
// cheap hedge against exactly the kind of drift that prompted this rewrite in the first place.
4139
export function coreIdentityFragment({ address, name, buyUrl }: { address: string; name?: string; buyUrl: string }): string {
4240
const displayName = name || address
43-
return `> **Canonical reference:** https://markee.xyz/docs
44-
> If anything below is outdated or contradicts the live contracts/API, fetch that URL first -- it is the source of truth. Tell me if something looks stale.
45-
46-
# Markee embed setup
41+
return `# Markee embed setup
4742
4843
Markee is a protocol where anyone can pay ETH to set the featured message on a leaderboard. The highest total funder holds the top spot; anyone can outbid them to take it.
4944
@@ -52,7 +47,18 @@ My leaderboard:
5247
- Address: ${address}
5348
- Fallback buy page (works from anywhere, not required for the embedded flow below): ${buyUrl}
5449
55-
Build a fully embedded flow -- visitors buy, edit, and add funds to messages without ever leaving this site. Do not fall back to an iframe.`
50+
Build a fully embedded flow -- visitors buy, edit, and add funds to messages without ever leaving this site. Do not fall back to an iframe.
51+
52+
**Before you conclude something is wrong, read this:** https://markee.xyz/api/ecosystem/leaderboards is
53+
the PUBLIC marketplace listing, and it deliberately only includes leaderboards that have completed
54+
Markee's own "Verify Embed" check -- it's a spam filter for the public listing, not a directory of
55+
every leaderboard that exists. A brand-new integration's address will not appear there yet, and that
56+
is expected, not a sign the address is wrong or stale. Build against the address above regardless --
57+
don't treat its absence from that endpoint as a reason to stop or ask before writing any code. Verification
58+
itself only checks that \`data-markee-address\` is present in your server-rendered HTML (see the
59+
implementation notes at the end of this prompt); it does not depend on this fetch succeeding. Once
60+
verified, the same code you're about to write starts resolving real data automatically -- nothing
61+
needs to change or redeploy.`
5662
}
5763

5864
// ── Trigger card & brand watermark ─────────────────────────────────────────────
@@ -276,7 +282,7 @@ export function strategyFragment(strategy: EmbedStrategy, address: string): stri
276282
const PUBLIC_API_URL = 'https://markee.xyz/api/ecosystem/leaderboards'
277283

278284
export function proxyRouteFragment(framework: EmbedFramework, address: string): string {
279-
const commonNote = `Browser fetches to markee.xyz are blocked by CORS on most setups, so this needs a server-side hop. Find your leaderboard by matching \`address\` (case-insensitive) against "${address}" in the response. Useful fields: \`topMessage\`, \`topMessageOwner\`, \`topFundsAddedRaw\`, \`minimumPrice\` (fixed) / \`streamedRateRaw\` (streaming), \`topMarkeeAddress\`.`
285+
const commonNote = `Browser fetches to markee.xyz are blocked by CORS on most setups, so this needs a server-side hop. Find your leaderboard by matching \`address\` (case-insensitive) against "${address}" in the response. Useful fields: \`topMessage\`, \`topMessageOwner\`, \`topFundsAddedRaw\`, \`minimumPrice\` (fixed) / \`streamedRateRaw\` (streaming), \`topMarkeeAddress\`. No match yet is expected for a brand-new, not-yet-verified leaderboard (see the note near the top of this prompt) -- have the trigger component fall back to a "be first!" empty state in that case, not an error.`
280286

281287
if (framework === 'nextjs') {
282288
return `## Data fetching

0 commit comments

Comments
 (0)