Skip to content

feat: add MULTILENDBORROW rewards#290

Merged
antoncoding merged 2 commits intomasterfrom
feat/reward-type
Jan 12, 2026
Merged

feat: add MULTILENDBORROW rewards#290
antoncoding merged 2 commits intomasterfrom
feat/reward-type

Conversation

@antoncoding
Copy link
Owner

@antoncoding antoncoding commented Jan 12, 2026

Summary by CodeRabbit

  • New Features

    • Added support for multi-market campaign types and expansion into per-market entries.
    • Added single-token campaign support and opportunity identifiers.
  • Improvements

    • Centralized campaign type configuration for badges and action verbs.
    • More resilient campaign fetching with partial-failure handling and auto-refetch on focus.
    • Updated reward categorization for clearer supplier/borrower labels.
    • Tooltip detail text now preserves line breaks for improved readability.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Jan 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
monarch Ready Ready Preview, Comment Jan 12, 2026 0:07am

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Adds MULTILENDBORROW support and refactors Merkl campaign handling: centralizes campaign-type config, expands/normalizes fetched campaigns into SimplifiedCampaigns, updates types, and tweaks UI rendering and tooltip typings.

Changes

Cohort / File(s) Summary
Type System
src/utils/merklTypes.ts
Added MULTILENDBORROW to MerklCampaignType; introduced MerklMarketCampaignParams, optional markets on params, MerklOpportunity, and opportunityIdentifier on SimplifiedCampaign.
API & Utilities
src/utils/merklApi.ts
Added withOpportunity to fetch params; introduced isCampaignActive and getBaseCampaignFields; refactored simplifyMerklCampaign; added expandMultiLendBorrowCampaign to emit multiple SimplifiedCampaigns from one campaign.
Query Hook
src/hooks/queries/useMerklCampaignsQuery.ts
Switched to fetching multiple campaign types via CAMPAIGN_TYPES_TO_FETCH + Promise.allSettled; attaches type to results; expands MULTILENDBORROW via expandMultiLendBorrowCampaign; enabled refetchOnWindowFocus.
Components: Campaign UI
src/features/market-detail/components/campaign-modal.tsx, src/features/markets/components/rewards-indicator.tsx
Introduced CAMPAIGN_TYPE_CONFIG for badge/action mapping; replaced inline urlIdentifier/action derivation with config and getUrlIdentifier; changed reward role mapping (explicit MORPHOBORROW → borrower); deduplicated CampaignRow keys to ${campaign.campaignId}-${campaign.marketId}.
Shared UI/Typings
src/components/shared/tooltip-content.tsx
Consolidated React imports; changed onActionClick type from (e: React.MouseEvent) => void to (e: MouseEvent) => void; switched detail rendering CSS from whitespace-normal to whitespace-pre-line.

Sequence Diagram(s)

sequenceDiagram
    participant Component as CampaignModal / Rewards UI
    participant Query as useMerklCampaignsQuery
    participant API as merklApi Utils
    participant Server as Merkl API Server

    Component->>Query: request campaigns (params)
    activate Query
    Query->>Server: fetch campaigns for types (MORPHOSUPPLY, MORPHOBORROW, MULTILENDBORROW)
    Server-->>Query: raw campaign arrays
    alt MULTILENDBORROW present
        Query->>API: expandMultiLendBorrowCampaign(campaign)
        activate API
        API-->>Query: array of SimplifiedCampaigns (one per market)
        deactivate API
    else other campaign types
        Query->>API: simplifyMerklCampaign(campaign)
        activate API
        API-->>Query: SimplifiedCampaign
        deactivate API
    end
    Query-->>Component: consolidated SimplifiedCampaign list
    deactivate Query
    Component->>Component: render campaigns using CAMPAIGN_TYPE_CONFIG
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main feature being added: MULTILENDBORROW rewards support across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/reward-type

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added feature request Specific feature ready to be implemented ui User interface labels Jan 12, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/components/shared/tooltip-content.tsx:
- Line 1: The import pulls in the DOM MouseEvent instead of React's event type;
replace the imported MouseEvent usage with React's synthetic event type by
removing or changing the import and using React.MouseEvent (or the more specific
React.MouseEvent<HTMLAnchorElement>) in the type annotations for any onClick
handlers/props in tooltip-content (e.g., update the import line that currently
mentions MouseEvent and adjust any function signatures or prop types that
reference MouseEvent to use React.MouseEvent<HTMLAnchorElement>).
🧹 Nitpick comments (3)
src/hooks/queries/useMerklCampaignsQuery.ts (1)

14-19: Index-based type assignment is fragile.

Relies on Promise.all preserving order (which it does), but if fetchActiveCampaigns ever filters or rejects a type, the index alignment breaks silently.

Consider attaching type during fetch or using a more explicit mapping:

Safer approach
-const results = await Promise.all(CAMPAIGN_TYPES_TO_FETCH.map((type) => fetchActiveCampaigns({ type })));
-
-const allRawCampaigns = results.flatMap((campaigns, index) =>
-  campaigns.map((campaign) => ({
-    ...campaign,
-    type: CAMPAIGN_TYPES_TO_FETCH[index],
-  })),
-);
+const results = await Promise.all(
+  CAMPAIGN_TYPES_TO_FETCH.map(async (type) => {
+    const campaigns = await fetchActiveCampaigns({ type });
+    return campaigns.map((campaign) => ({ ...campaign, type }));
+  }),
+);
+
+const allRawCampaigns = results.flat();
src/features/market-detail/components/campaign-modal.tsx (1)

24-33: Magic number 42.

The slice extracts what appears to be an Ethereum address (0x + 40 hex = 42 chars). Consider a named constant for clarity.

const ETH_ADDRESS_LENGTH = 42;

Otherwise, logic is sound.

src/utils/merklApi.ts (1)

127-140: Silent empty result when markets is undefined/empty.

If a MULTILENDBORROW campaign should always have markets, returning an empty array silently hides bad data. Consider logging a warning:

if (markets.length === 0) {
  console.warn(`MULTILENDBORROW campaign ${campaign.campaignId} has no markets`);
}

Otherwise, logic is correct.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7536c82 and f529d94.

📒 Files selected for processing (6)
  • src/components/shared/tooltip-content.tsx
  • src/features/market-detail/components/campaign-modal.tsx
  • src/features/markets/components/rewards-indicator.tsx
  • src/hooks/queries/useMerklCampaignsQuery.ts
  • src/utils/merklApi.ts
  • src/utils/merklTypes.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-09T10:06:39.848Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 231
File: src/hooks/useDeployMorphoMarketV1Adapter.ts:3-3
Timestamp: 2025-12-09T10:06:39.848Z
Learning: In Wagmi v3, useConnection is the correct hook to obtain the connected wallet address, chainId, and connection status (isConnected). This replaces the useAccount hook from Wagmi v2. In your code under src/hooks, use: const { address, chainId, isConnected } = useConnection() from 'wagmi' to access the connected wallet information.

Applied to files:

  • src/hooks/queries/useMerklCampaignsQuery.ts
🧬 Code graph analysis (2)
src/utils/merklApi.ts (1)
src/utils/merklTypes.ts (2)
  • MerklCampaign (94-120)
  • SimplifiedCampaign (133-158)
src/features/market-detail/components/campaign-modal.tsx (2)
src/utils/merklTypes.ts (2)
  • MerklCampaignType (1-1)
  • SimplifiedCampaign (133-158)
src/utils/external.ts (1)
  • getMerklCampaignURL (57-60)
🔇 Additional comments (13)
src/utils/merklTypes.ts (4)

1-1: LGTM.

New campaign type added cleanly.


43-47: LGTM.

Type for nested market params is clear and matches usage in expandMultiLendBorrowCampaign.


81-92: LGTM.

Optional markets array and MerklOpportunity type support the new expansion logic well.


118-119: Good change from any to unknown.

Forces explicit type checks before use.

src/hooks/queries/useMerklCampaignsQuery.ts (1)

22-27: LGTM.

Conditional expansion with flatMap handles both cases correctly.

src/components/shared/tooltip-content.tsx (1)

52-52: LGTM.

whitespace-pre-line enables multi-line detail strings (like the rewards list using \n).

src/features/market-detail/components/campaign-modal.tsx (3)

11-16: LGTM.

Clean config-driven approach for campaign type display.


39-39: LGTM.

Safe fallback to MORPHOSUPPLY config.


111-111: Good fix.

Composite key prevents React key collisions when MULTILENDBORROW expands to multiple rows with the same campaignId.

src/utils/merklApi.ts (3)

71-75: LGTM.

Simple and correct active check.


77-95: LGTM.

Good extraction of shared fields.


97-125: LGTM.

Clean refactor using baseFields. Type-specific handling is clear.

src/features/markets/components/rewards-indicator.tsx (1)

31-31: The borrower condition can't be reached with current campaign fetching.

MORPHOBORROW isn't included in CAMPAIGN_TYPES_TO_FETCH, so campaign.type === 'MORPHOBORROW' will never match. All fetched campaigns will fall through to the 'supplier' branch.

This is either intentional future-proofing (if MORPHOBORROW support is coming) or dead code. If it's meant to be active, add MORPHOBORROW to the fetch list.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/hooks/queries/useMerklCampaignsQuery.ts:
- Around line 33-38: The flatMap callback in useMerklCampaignsQuery returns
inconsistent types—expandMultiLendBorrowCampaign returns SimplifiedCampaign[]
while simplifyMerklCampaign returns a single SimplifiedCampaign—so make the
return types consistent by wrapping the simplifyMerklCampaign(...) result in an
array (e.g., return [simplifyMerklCampaign(campaign)]) so flatMap always
receives an array, keeping expandMultiLendBorrowCampaign and
simplifyMerklCampaign usage unchanged.
🧹 Nitpick comments (1)
src/hooks/queries/useMerklCampaignsQuery.ts (1)

53-58: Return type for refetch is imprecise.

React Query's refetch returns a Promise, not void. Minor, but could cause issues if callers want to await it.

Suggested fix
 export type MerklCampaignsQueryReturn = {
   campaigns: SimplifiedCampaign[];
   loading: boolean;
   error: string | null;
-  refetch: () => void;
+  refetch: () => Promise<unknown>;
 };
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f529d94 and fecab2e.

📒 Files selected for processing (1)
  • src/hooks/queries/useMerklCampaignsQuery.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-09T10:06:39.848Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 231
File: src/hooks/useDeployMorphoMarketV1Adapter.ts:3-3
Timestamp: 2025-12-09T10:06:39.848Z
Learning: In Wagmi v3, useConnection is the correct hook to obtain the connected wallet address, chainId, and connection status (isConnected). This replaces the useAccount hook from Wagmi v2. In your code under src/hooks, use: const { address, chainId, isConnected } = useConnection() from 'wagmi' to access the connected wallet information.

Applied to files:

  • src/hooks/queries/useMerklCampaignsQuery.ts
🔇 Additional comments (2)
src/hooks/queries/useMerklCampaignsQuery.ts (2)

5-6: LGTM - clean constant extraction.

Easy to extend when new campaign types are added.


11-22: Good use of Promise.allSettled for resilience.

Partial failures won't break the whole fetch. Warning logs help with debugging.

@antoncoding antoncoding merged commit 1f6e44c into master Jan 12, 2026
4 checks passed
@antoncoding antoncoding deleted the feat/reward-type branch January 12, 2026 12:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request Specific feature ready to be implemented ui User interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant