Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CSP: add vercel.live to script-src to unblock Vercel feedback widget - views: guard against empty message to stop /api/views 400 errors - reactions: fix GET endpoint to scan all reactions:v2:* keys when no markeeAddress supplied — hook was always getting empty array on load - detail page: remove Names tab, rename Funding→Funds Added and Messages→Message History, merge message+name events into single time-sorted combined history without strikethrough old values Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Markee Cooperative, Gardens, Clawchemy, and Superfluid are in the legacy OI factory's getLeaderboards() list but had no KV meta, so they were landing in Unverified with status:pending. Hardcode their verified status, URLs, and logos directly in the OI leaderboards API so they always appear in the Verified section without a KV write. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The SF migration leaderboard (0xb6CC...) is a partner leaderboard migrated from v0.1, not a new Superfluid project. It comes through the SF API, but the ecosystem aggregator strips status from all SF entries, so it landed in Unverified. Override that one address to platform:website + status:verified so it joins Cooperative, Gardens, and Clawchemy in the Verified section. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reactions: replace kv.scan (unreliable in @vercel/kv v2) with a set-index approach — POST writes sadd reactions:markees, GET reads smembers + hgetall per markee; no more 500 on page load - CSP: add vercel.live to frame-src to stop iframe violation warning - detail page: wrap history tabs in ModeratedContent so flagged markees blur the full message history for non-admin users Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TokenBalance gated on wagmi isConnected which lags behind Privy's authenticated state. Adding the Privy authenticated check means the balance hides as soon as Privy logs out, matching ConnectButton behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New /api/eth-price endpoint (CoinGecko + 5-min KV cache) - useEthPrice hook for client-side price lookups - formatUsd utility for consistent dollar formatting - EthBalance component in header showing ETH balance + USD value - FixedPriceModal: USD-primary price + balance, Fund with card button - TopDawgModal: USD-primary preset buttons + balance, Fund with card button - Home page and create-a-markee stats show USD primary with ETH secondary Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Superfluid points: - Add legacy TopDawg (FundsAddedToMarkee) scan from block 41984295 - Add dedicated SF migration leaderboard scan with own KV block key - Give each address family its own last-processed block so late additions don't silently skip historical events - uniqueId (txHash-logIndex) deduplication makes all scans safe to re-run GitHub delimiter grandfathering: - Add GITHUB_NEW_TO_OLD map in linkedFiles.ts covering all 11 v1.0→v1.1 GitHub leaderboard migrations from markee-migrations.csv - verify-markee-file and register-markee now accept old-address delimiters as verified when the new-address delimiter is absent - update-markee-file uses migrate-on-write: if only old delimiters are found it replaces them with the new-address markee block on next update Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The hardcoded v1.0→v1.1 map doesn't cover files from before the v1.0
migration (e.g. delimiter address 0x56c5f6ce... in honeyswap-interface).
verify-markee-file and register-markee now accept any
<!-- MARKEE:START:0x{40} --> / <!-- MARKEE:END:0x{40} --> pair as valid,
regardless of which address is embedded. Push access to the repo was
already verified on registration so this doesn't weaken the security model.
update-markee-file gains a matching last-resort regex pass: it finds any
address-shaped delimiter pair and replaces the block with the current
leaderboard's content + new-address delimiters (migrate-on-write).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Partners with leaderboardAddress=null (Bread Cooperative, RevNets, Juicebox, Giveth, Flow State) had their old TopDawg strategy addresses fall through as strategyAddress in TopDawgModal. Those old strategies route to RevNet v5 (project 119, compromised terminal) so any purchase would send a portion of ETH there. Changes: - Hide "Buy a Message" and "Be the First!" buttons when leaderboardAddress is null, showing "Purchases coming soon" instead - Remove the strategyAddress fallback to partner.strategyAddress in TopDawgModal (now passes undefined when no leaderboard, preventing any purchase call) Completing migration for these 5 partners requires deploying new v1.1 leaderboards via the OpenInternet factory and setting leaderboardAddress in PARTNERS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Superfluid factory deployed early leaderboards from a v1.0 Leaderboard implementation that hardcodes the deprecated RevNet v5 terminal (0x2dB6d704...) — routing 38% of every purchase to it. Read VERSION() on each individual leaderboard page and gate all purchase buttons behind isLegacyContract check (VERSION !== '1.1.0'). Legacy leaderboards show a warning banner instead of Buy a Message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rboards Deploys a new v1.1 Superfluid LeaderboardFactory and migrates all 96 existing v1.0 leaderboards from the old factory (0x45Ce642d...). Per-leaderboard: creates v1.1 clone via new factory, removes automation seed, runs migrateFromLegacy() for each markee, transfers admin back to original creator. Resolves original admins from old factory's LeaderboardCreated event logs rather than admin() (which returns zero on the deployed pre-v1.0 bytecode). Outputs JSON and CSV rows for follow-up frontend updates. Fund automation wallet 0x000D356bB4D1E81CAb7cFA47D8707cdBDfEDE5af with ~0.05 ETH before running live. After running, update FACTORIES.SUPERFLUID and the two API routes per the script's NEXT STEPS output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deploys new v1.1 LeaderboardFactory for Superfluid (0x1E1b0C22e2C6C7b46ABb0F25231c7eecD4f0A2d8) and migrates all 96 v1.0 leaderboards to v1.1 counterparts with correct RevNet routing. - Update SUPERFLUID_FACTORY_ADDRESS and FACTORY_DEPLOY_BLOCK across leaderboards API and points cron to point at new factory - Add old→new redirect map in [address]/page.tsx so legacy URLs transparently redirect to v1.1 leaderboard addresses - Update addresses.ts and CLAUDE.md with new factory address - Record new factory address in migrations CSV Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces hardcoded MIGRATIONS array with auto-loading from /tmp/migration-sf-factory-results.json so all 149 markee address pairs from the v1.0→v1.1 factory migration are migrated in one run. Switches SET → INCRBY so re-runs accumulate safely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Superfluid markee addresses Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…views route Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion, and health endpoint - Add topMarkeeAddress to API fields docs - Add view tracking section: proxy route + useRef-gated POST on first display - Add moderation section: proxy route + flagged Set + per-tab filtering behavior - Add optional health endpoint route for integration dashboard verification - Update platform/moderation banner copy (no longer mentions @vercel/kv) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…factory SUPERFLUID_FACTORY_ADDRESS now points to FACTORIES.SUPERFLUID (v1.1, 0x1E1b0C22e2C6C7b46ABb0F25231c7eecD4f0A2d8) instead of the old 0x45Ce642d factory. Both the writeContract call and the receipt log parser use the same constant, so both are fixed. GitHub and create-a-markee factories were already correct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- FACTORIES.GITHUB: 0xb1E2dC95b50b4Ee3b1BD4F4F87e6B07b41bE4e07 was wrong, corrected to 0xb1E2dC9582810124Fed3Cdb4B8Bb944A5495D85a (matches GitHub platform page and user-confirmed address) - Fixed-price CSV rows: old addresses moved to v10_address column, current addresses.ts values added to v11_address column Note: Superfluid factory row (line 4) was already correct in the CSV. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- addresses.ts: populate REVNET_V6_CONFIG (terminal 0x27da30..., project 152); correct OI factory labels (0x3f9f7C = v1.0, 0xb9922E = v1.1) - abis.ts: expand LeaderboardV11ABI with full admin write surface (setRevNetTerminal, setRevNetProjectId, setPercentToBeneficiary, setRevNetEnabled, setPlatformFeeReceiver, setBeneficiaryAddress, setAdmin) and routing view getters - scripts/enable-revnet.mjs: generates Safe Transaction Builder batch JSONs for all 20 v1.1 leaderboards — Coop multisig batch (4) and Paul's wallet batch (16 OI + GitHub). Run: node scripts/enable-revnet.mjs - MIGRATION.md: fix factory version labels, fill in real terminal/projectId in checklist, mark completed Superfluid work, scope v1.0 exclusions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… enabled BuyMessageModal and TopDawgModal now conditionally show a MARKEE token count card when revNetEnabled is true on the leaderboard contract. Revenue split updates from 100% to 62%/38% (beneficiary / Markee Cooperative) accordingly. Token count uses the live phase schedule (50k/ETH through 2026-06-21). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The initial enable-revnet batch used the wrong JB terminal address. The correct terminal for RevNet project 152 on Base is 0x2dB6d704... (confirmed from a successful on-chain pay tx to the same project). Coop multisig needs to run the fix-terminal-coop.json Safe batch to correct the 4 already-enabled leaderboards. Paul's 16 leaderboards have not yet been enabled so they are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Multicall3 from EOA doesn't preserve msg.sender, so onlyAdmin calls would revert. This script sends 64 sequential cast send transactions (4 per leaderboard) directly from the EOA private key instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ACCOUNT=revnet-admin is now preferred over raw PRIVATE_KEY. Create keystore once with: cast wallet import revnet-admin --interactive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 3s sleep between cast send calls to avoid replacement-tx-underpriced errors. Adds revNetEnabled check before each leaderboard so re-runs are safe and idempotent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v1.2 fixes a governance hole where individual leaderboard admins could redirect the 38% RevNet cut to any project. Terminal and project ID are now stored on the factory (Coop-only setter) and read by all leaderboards at pay-time — one tx updates every leaderboard simultaneously. - New contracts: Leaderboard v1.2, LeaderboardFactory v1.2, Markee v1.2 - 3 factories deployed (OI, GitHub, Superfluid) with RevNet v6 baked in - 21 leaderboards migrated, 107 Markees cloned, 4 handed to Coop multisig - All frontend address references updated to v1.2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After fetching GitHub repo traffic, the traffic route reads the top markee
slot address from chain and writes max(current, githubCount) to
views:total:{topSlotAddr} in KV. The syncedViews count is returned to the
client which updates the local viewsMap so Total Views reflects it immediately.
…to Name this Markee
JB terminal: 0x2dB6d704...0361846 → 0x130f5Dd2...7f53 MARKEE token: 0xee3027f1...b38 → 0xF6627cF1...42F Updated across all 9 files: addresses.ts, MIGRATION.md, ARCHITECTURE.md, contracts/README.md, LeaderboardFactory.sol (natspec), enable-revnet.mjs, enable-revnet-eoa.sh, deploy-v13-factories.sh, how-it-works/page.tsx (Gardens URL).
…nabled load state
|
|
||
| // Fetch all platform leaderboards | ||
| const [sfData, ghData, oiData] = await Promise.all([ | ||
| fetch(`${origin}/api/superfluid/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), |
| // Fetch all platform leaderboards | ||
| const [sfData, ghData, oiData] = await Promise.all([ | ||
| fetch(`${origin}/api/superfluid/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), | ||
| fetch(`${origin}/api/github/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), |
| const [sfData, ghData, oiData] = await Promise.all([ | ||
| fetch(`${origin}/api/superfluid/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), | ||
| fetch(`${origin}/api/github/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), | ||
| fetch(`${origin}/api/openinternet/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), |
|
|
||
| // Fetch all platform leaderboards (uses their cached responses where available) | ||
| const [sfData, ghData, oiData] = await Promise.all([ | ||
| fetch(`${origin}/api/superfluid/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), |
| // Fetch all platform leaderboards (uses their cached responses where available) | ||
| const [sfData, ghData, oiData] = await Promise.all([ | ||
| fetch(`${origin}/api/superfluid/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), | ||
| fetch(`${origin}/api/github/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), |
| const [sfData, ghData, oiData] = await Promise.all([ | ||
| fetch(`${origin}/api/superfluid/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), | ||
| fetch(`${origin}/api/github/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), | ||
| fetch(`${origin}/api/openinternet/leaderboards`).then(r => r.ok ? r.json() : null).catch(() => null), |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Gaston Review
Verdict: Comment
Score: ██████░░░░ 6/10
Pull Request Summary
This PR merges main back into dev, bringing in ~22,000 lines across 100+ files: new v1.2 and v1.3 smart contracts (Leaderboard, LeaderboardFactory, Markee), numerous admin KV migration endpoints, a major rewrite of the home/marketplace/owners/create-a-markee pages, new ETH price and embed APIs, network-wide view tracking, and factory address updates.
Review Summary
🟡 Warning — Markee.sol: ETH permanently locked when beneficiary is address(0)
In Markee.pay(), when beneficiary == address(0), the code sets beneficiaryAmount = msg.value but then skips the transfer (guard: beneficiary != address(0)). Meanwhile revNetAmount stays 0, so _routeRevNet is a no-op. The ETH sits in the contract with no withdraw function and no receive()/fallback(). setBeneficiaryAddress() allows setting to address(0) with no validation, so this state is reachable. Even if it requires an admin mistake, in a financial contract this is a footgun worth closing.
🟡 Warning — Markee.sol: tx.origin in event emissions is unreliable
setMessage() and setName() emit events with tx.origin instead of msg.sender. tx.origin can differ from the actual caller in meta-transaction, account-abstraction, and multi-hop contract call scenarios. This produces misleading event data. The Leaderboard contract already knows the actual caller (msg.sender in updateMessage/updateName) — the Markee should log that instead.
🟡 Warning — Reactions bulk-fetch endpoint has no pagination limit
GET /api/reactions with no params loads ALL markee addresses from KV (smembers), then fires a parallel hgetall for each one. As the dataset grows, this will hit memory and timeout limits. Should add pagination or a cap on the number of markees loaded.
The architecture direction is solid — centralizing RevNet config at the factory level is a good move. But the Markee.sol payment logic has a latent fund-locking bug when beneficiary is address(0), and the use of tx.origin in events is a code smell that should be cleaned up. The frontend rewrites are massive but largely cosmetic. For a merge-back PR of this size, the real risk is in the contracts — those deserve focused scrutiny before deployment.
📌 6 inline comments
🟡 Warning: 3 · 🔵 Suggestion: 3
🔍 Reviewed by Gaston
| uint256 revNetAmount; | ||
|
|
||
| if (!strategy.revNetEnabled() || beneficiary == address(0)) { | ||
| beneficiaryAmount = msg.value; |
There was a problem hiding this comment.
🟡 Warning: When beneficiary == address(0) and revNetEnabled is true, the entire msg.value is assigned to beneficiaryAmount but the transfer is skipped (line 108 guard), and revNetAmount remains 0. ETH is permanently locked. Consider: (1) require beneficiary != address(0) in pay(), or (2) route 100% to RevNet when beneficiary is unset, or (3) add a zero-address check in setBeneficiaryAddress().
| function setMessage(string calldata _message) external { | ||
| require(msg.sender == pricingStrategy, "Only pricing strategy can set message"); | ||
| message = _message; | ||
| emit MessageChanged(_message, tx.origin); |
There was a problem hiding this comment.
🟡 Warning: tx.origin is used here and in setName() for event emission. This is unreliable under account abstraction, meta-transactions, and EIP-3074. Since the Leaderboard's updateMessage() already validates msg.sender == markee.owner(), pass the caller as a parameter to setMessage() instead of relying on tx.origin.
| /// migrateFromLegacy() which also creates a fresh v1.3 clone. | ||
| function initializeHistory(address[] calldata _markees) external onlyAdmin { | ||
| require(!historyInitialized, "History already initialized"); | ||
| historyInitialized = true; |
There was a problem hiding this comment.
🔵 Suggestion: If offset >= markees.length, end - offset underflows (Solidity 0.8 reverts with a generic panic). Add if (offset >= markees.length) return new address[](0); for a cleaner caller experience. Same issue in LeaderboardFactory.getLeaderboards().
| } | ||
|
|
||
| function percentToBeneficiary() external view override returns (uint256) { | ||
| return ILeaderboardFactory(factory).percentToBeneficiary(); |
There was a problem hiding this comment.
🔵 Suggestion: setBeneficiaryAddress allows setting to address(0) without validation, which would trigger the fund-locking scenario in Markee.pay(). Consider adding require(_newBeneficiary != address(0)) to match the pattern used for setAdmin.
| return NextResponse.json({ reactions }) | ||
| } | ||
|
|
||
| // No markeeAddress — load all reactions via the markee index set |
There was a problem hiding this comment.
🟡 Warning: This bulk-fetches ALL reactions across ALL markees in parallel with no pagination. As the dataset grows, smembers returns unbounded results and Promise.all(hgetall...) fires unbounded concurrent KV calls. This will eventually hit Vercel function timeouts or memory limits. Consider adding a limit parameter or paginating the index.
| return NextResponse.redirect(`${fallbackBase}?error=invalid_state`) | ||
| } | ||
|
|
||
| let returnTo = '' |
There was a problem hiding this comment.
🔵 Suggestion: The origin stored in KV comes from new URL(request.url).origin in the connect route. In standard Vercel/Next.js deployments this reflects the server's own origin, so it's safe. But if the app ever sits behind a reverse proxy that rewrites Host headers, this becomes an open redirect. Consider validating siteOrigin against an allowlist of known deployment origins before redirecting.
There was a problem hiding this comment.
Gaston Review
Verdict: Comment
Score: ██████░░░░ 6/10
Pull Request Summary
This is a massive merge-back from main to dev, pulling in cumulative production changes: new v1.2 and v1.3 contract versions (Leaderboard, LeaderboardFactory, Markee) with factory-level RevNet routing, all frontend and API routes migrated to v1.3 addresses, a Superfluid Season 6 system with boosted 5x multipliers, gamed-leaderboard filtering, multiple one-shot admin migration endpoints, USD price computation, embed widget rewrite, and extensive UI overhauls across the account, ecosystem, marketplace, and create pages.
Review Summary
🟡 Warning — ETH locked when beneficiary is address(0) and revNet is enabled
In Markee v1.2 and v1.3 pay(), when revNetEnabled == true and beneficiaryAddress == address(0), the condition !revNetEnabled || beneficiary == address(0) is true, so beneficiaryAmount = msg.value and revNetAmount = 0. Then the beneficiary transfer is skipped (guard: beneficiary != address(0)), and _routeRevNet receives 0 and does nothing. The ETH is stuck in the Markee clone with no receive(), fallback(), or withdrawal function. The v1.3 factory doesn't enforce non-zero _beneficiaryAddress in createLeaderboard(), and setBeneficiaryAddress() also has no zero-check. Admin-only blast radius, but still a path to permanent fund loss.
🟡 Warning — Duplicated GAMED_ADDRESSES set across two API routes
The same 13-address gamed-leaderboard blocklist is hardcoded identically in ecosystem/leaderboards/route.ts and superfluid/leaderboards/route.ts. When a new gamed address is discovered, both files must be updated in lockstep. This will drift — extract to a shared constant.
🟡 Warning — Track endpoint trusts client-supplied leaderboardAddress for boost multiplier
POST /api/superfluid/track now applies a 5x points multiplier based on the leaderboardAddress field in the request body. This value is entirely client-supplied and not verified on-chain. An attacker can submit any boosted leaderboard address alongside their real txHash to inflate their points by 5x. The cron job has the same multiplier logic but derives the leaderboard address from on-chain event logs, making it accurate.
🔵 Suggestion — tx.origin used in Markee events
Both v1.2 and v1.3 Markee contracts emit MessageChanged and NameChanged events with tx.origin as the changedBy parameter. While not a security issue (it's only in events, not access control), it can be unreliable with account abstraction and meta-transactions. Consider using a parameter passed from the Leaderboard instead.
The contracts are well-structured with proper access controls, and the migration from per-leaderboard to factory-level RevNet config is a sound design. I see one edge-case fund-locking path in the Markee payment logic, duplicated gamed-address lists that will drift, and the track endpoint accepting unverified boosted multiplier claims. Since this is a merge of already-deployed code, these are worth tracking rather than blocking.
📌 5 inline comments
🟡 Warning: 3 · 🔵 Suggestion: 2
🔍 Reviewed by Gaston
| uint256 beneficiaryAmount; | ||
| uint256 revNetAmount; | ||
|
|
||
| if (!strategy.revNetEnabled() || beneficiary == address(0)) { |
There was a problem hiding this comment.
🟡 Warning: When revNetEnabled == true and beneficiaryAddress == address(0), this condition routes the full msg.value to beneficiaryAmount but then skips the beneficiary transfer (line 108 guard). revNetAmount stays 0, so _routeRevNet is a no-op. The ETH is permanently stuck — this contract has no receive(), fallback(), or rescue function. Consider: (1) requiring non-zero beneficiary in createLeaderboard and setBeneficiaryAddress, or (2) when beneficiary is zero and revNet is enabled, route everything to RevNet instead.
| // ─── Markee registry ────────────────────────────────────────────────────── | ||
|
|
||
| address[] public markees; | ||
| mapping(address => uint256) private markeeIndex; |
There was a problem hiding this comment.
🔵 Suggestion: If offset >= markees.length, then end gets clamped to markees.length, and end - offset underflows (Solidity 0.8 will revert). Safe but confusing — add if (offset >= markees.length) return new address[](0); before the subtraction.
| const points = ethToPoints(amountWei) | ||
| let points = ethToPoints(amountWei) | ||
|
|
||
| // Apply 5x multiplier if this leaderboard is boosted |
There was a problem hiding this comment.
🟡 Warning: The leaderboardAddress is taken directly from the client request body and used to determine the 5x boosted multiplier. Nothing stops a caller from submitting a boosted address that doesn't match their actual transaction. The cron job correctly reads the address from on-chain event logs. Consider either verifying the leaderboard address on-chain against the txHash, or removing the boost from this endpoint and only applying it via the cron.
| // Excluded from totalPlatformFunds — leaderboards with clearly recycled/gamed ETH | ||
| const GAMED_ADDRESSES = new Set([ | ||
| '0x1b4eb52953d865e0dde1c856c2ead826581e2904', | ||
| '0xb5451c1cb790367d0ddbcbf1249de22b9014ecdc', |
There was a problem hiding this comment.
🟡 Warning: This gamed-addresses set is duplicated verbatim in superfluid/leaderboards/route.ts. Extract to a shared module (e.g. lib/gamed-addresses.ts) to avoid the two lists drifting when new entries are added.
| event RevNetEnabledChanged(bool enabled); | ||
| event PlatformFeeReceiverChanged(address indexed oldReceiver, address indexed newReceiver); | ||
| event PercentToPlatformFeeReceiverChanged(uint256 oldPercent, uint256 newPercent); | ||
| event HistoryInitialized(uint256 markeeCount); |
There was a problem hiding this comment.
🔵 Suggestion: Same getMarkees offset-underflow edge case as v1.3: when offset > markees.length, the array allocation underflows and reverts. Add an early return for offset >= markees.length.
No description provided.