Writing
Merkl Rewards Integration for Web3 Apps
A deep dive into integrating the Merkl protocol into a modern Web3 application using Next.js. Explore API architecture, reward parsing, wallet interactions, Merkle proof claims, and scalable frontend engineering practices.
Blockchain incentive programs have become a core part of the DeFi ecosystem, but integrating reward distribution into an existing application isn't always straightforward.
A production-ready implementation needs to display rewards accurately, provide a smooth claiming experience, and fit naturally into the application's existing architecture without introducing unnecessary complexity.
In one of my recent Web3 frontend projects, I implemented a complete integration with the Merkl rewards protocol.
The goal was to allow users to view and claim their on-chain rewards while keeping the implementation modular, scalable, and easy to maintain.
Rather than replacing an existing rewards system, the integration was designed as an independent module that could coexist with other claim mechanisms and be extended in the future.
What Is Merkl?
Merkl is a decentralized rewards distribution protocol used by DeFi protocols to incentivize on-chain activity.
Instead of storing claimable balances directly inside smart contracts, Merkl periodically computes user rewards and publishes them as a Merkle Tree.
Users then submit a Merkle proof to the Distributor contract to claim their rewards.
Understanding Merkl Reward Data
Each reward contains several important values:
-
amount: Total cumulative rewards in the current Merkle tree
-
claimed: Rewards already claimed on-chain
-
pending: Rewards earned but not yet included in a Merkle root
-
proofs: Merkle proofs required for claiming
How Claimable Rewards Are Calculated
One of the most important implementation details is understanding the reward calculation.
claimable = amount - claimed
The pending value represents rewards that have not yet been committed into a Merkle root and therefore cannot be claimed.
This distinction is essential for displaying accurate balances and preventing incorrect claim attempts.
Technology Stack
The integration was built using technologies already present in the application.
Frontend Technologies
-
Next.js with App Router
-
TypeScript
-
React
-
Tailwind CSS
Web3 Technologies
-
wagmi
-
viem
-
RainbowKit
One design goal was to avoid introducing additional dependencies. Everything was implemented using the existing frontend architecture.
High-Level Merkl Integration Architecture
The overall integration flow looks like this:
User Wallet
│
▼
React UI
│
▼
Custom React Hook
│
▼
Next.js API Route
│
▼
Merkl REST API
│
▼
Reward Parsing
│
▼
Claim Transaction
│
▼
Distributor Smart Contract
Separating Responsibilities
The architecture intentionally separates responsibilities:
-
UI components display data
-
Custom hooks manage state and wallet interactions
-
Service functions communicate with external APIs
-
Server routes handle communication with external services
-
Smart-contract interactions remain isolated from presentation logic
This separation keeps the codebase easier to maintain as the application grows.
Why Use a Server Proxy?
One interesting challenge was that the browser could not directly communicate with the Merkl API because of the application's Content Security Policy (CSP).
Instead of relaxing browser security policies, I implemented a lightweight Next.js API route that acts as a proxy.
The browser communicates only with the application's backend, while the backend securely fetches data from the Merkl API.
Benefits of the API Proxy
This approach provides several advantages:
-
Maintains a strict Content Security Policy
-
Allows future authentication if required
-
Centralizes API handling
-
Simplifies frontend logic
-
Makes rate limiting easier to implement later
Building a Modular Rewards System
One of the primary goals was modularity.
Instead of scattering Merkl-related logic throughout the application, everything was isolated into its own feature module.
Example Feature Structure
merkl/
├── constants
├── types
├── service
├── utilities
├── hooks
└── components
This organization makes the integration easy to locate, test, extend, or remove without affecting unrelated reward systems.
Fetching and Processing Rewards
The frontend requests rewards through an internal API route.
The server then retrieves reward information from Merkl's public API and filters the response for the appropriate blockchain network and supported reward token.
Reward Information Used by the UI
After parsing the response, the frontend calculates:
-
Total rewards
-
Already claimed rewards
-
Currently claimable rewards
-
Pending rewards
-
Available Merkle proofs
When Should the Claim Button Be Enabled?
The UI only enables claiming when:
-
The claimable balance is greater than zero
-
Valid Merkle proofs are available
This prevents unnecessary failed transactions and improves the overall user experience.
The On-Chain Claim Flow
The claiming process follows a straightforward sequence.
Step-by-Step Claim Process
-
The user connects their wallet.
-
Reward data is loaded.
-
The user initiates a claim.
-
The application switches to the correct blockchain network if necessary.
-
Merkle proof data is prepared.
-
The Distributor contract is called.
-
The transaction is confirmed.
-
Reward data is refreshed.
Understanding the Cumulative Reward Amount
An important implementation detail is that the smart contract expects the cumulative reward amount, not only the currently claimable difference.
The contract itself calculates how much has already been claimed and transfers only the remaining rewards.
Using the cumulative amount ensures compatibility with the Distributor contract's accounting model.
User Experience Considerations
Good blockchain UX is often as important as the transaction itself.
The interface was designed to clearly communicate every possible reward state.
Reward States
The UI handles states such as:
-
Wallet not connected
-
No rewards available
-
Rewards fully claimed
-
Pending rewards awaiting the next Merkle root
-
Rewards available but missing proofs
-
Transaction pending
-
Successful claim
-
User-rejected transaction
-
Contract execution failure
Clear Error and Status Messages
Rather than displaying generic errors, each state provides context so users understand exactly what is happening.
This is particularly important in DeFi because users are interacting with financial assets and irreversible blockchain transactions.
Challenges During Development
Challenge 1: Browser Security Restrictions
The browser could not communicate directly with the external rewards API because of Content Security Policy restrictions.
Solution
A Next.js API proxy handled all external communication while keeping browser security intact.
Challenge 2: Correct Reward Calculations
Initially, it was tempting to treat all reported rewards as immediately claimable.
However, Merkl distinguishes between cumulative rewards, claimed rewards, and pending rewards.
Solution
The application explicitly computes:
claimable = amount - claimed
Pending rewards are displayed separately and are never treated as immediately claimable.
Challenge 3: Cache Refresh After Claim
Immediately after claiming, cached API responses may still show the previous reward state because indexing can take a short time.
Solution
After a successful transaction, the application performs a fresh reward fetch using cache-bypass logic so users receive updated information as soon as possible.
Challenge 4: Defensive Error Handling
Blockchain applications encounter many edge cases that traditional web applications do not.
Examples include:
-
Wallet disconnected
-
Wrong network
-
User rejects signature
-
Missing Merkle proofs
-
API unavailable
-
Smart contract reverts
Handling each scenario individually creates a much smoother user experience than relying on generic error messages.
Key Engineering Decisions
Several architectural decisions made the integration easier to maintain.
Modular Feature Structure
All Merkl-related functionality was isolated into a dedicated feature module rather than being distributed throughout the application.
Custom React Hooks
Custom hooks were used to manage reward state and wallet interactions without introducing unnecessary global state.
Server-Side API Proxy
The API proxy keeps external communication centralized and helps maintain the application's security policies.
Minimal Smart Contract Abstraction
Smart-contract interactions were kept focused and isolated instead of introducing a large abstraction layer for a relatively small integration.
Reusing Existing Web3 Libraries
Existing wagmi, viem, and RainbowKit dependencies were reused instead of adding unnecessary packages.
Strong TypeScript Typing
TypeScript types were applied across API responses, reward data, and contract interactions to reduce unexpected runtime issues.
Clear Separation of Responsibilities
Fetching, parsing, UI rendering, and contract execution were kept separate.
These decisions reduce long-term maintenance costs and make future enhancements easier.
Future Improvements
The current architecture provides a solid foundation while leaving room for additional features.
Multi-Token Reward Support
The system could be extended to support multiple reward tokens and display them independently.
Campaign-Level Reward Breakdown
Users could see which campaigns or activities generated their rewards.
Historical Reward Analytics
Historical data could provide users with insights into their reward earnings over time.
Additional Blockchain Networks
The modular structure makes it easier to support additional chains as required.
Enhanced Caching and Rate Limiting
Production environments could introduce more advanced caching and rate-limiting strategies to reduce unnecessary API requests.
Expanded Reward Dashboards
The rewards module could eventually include additional filtering, analytics, and portfolio-level reward views.
Final Thoughts
Integrating a Web3 rewards protocol involves much more than simply calling a smart contract.
A production-ready solution requires careful handling of API responses, blockchain transactions, caching, wallet interactions, security policies, and user experience.
This project reinforced the importance of building integrations that are modular, maintainable, and resilient to real-world edge cases.
By keeping responsibilities clearly separated and designing for future extensibility, the resulting implementation is easier to maintain and ready to evolve alongside the broader Web3 ecosystem.
References
-
Merkl Developer Documentation: https://developers.merkl.xyz/integrate-merkl/user-rewards
-
Merkl Chains & Contracts: https://developers.merkl.xyz/resources/chains-and-contracts
-
Merkl API Documentation: https://api.merkl.xyz/v4