# Table of Contents - [Merkl Documentation Portal | Merkl Docs](#merkl-documentation-portal-merkl-docs) - [Technical Overview | Merkl Docs](#technical-overview-merkl-docs) - [Campaign Types | Merkl Docs](#campaign-types-merkl-docs) - [Lending and Borrowing Campaigns | Merkl Docs](#lending-and-borrowing-campaigns-merkl-docs) - [Concentrated Liquidity Campaigns | Merkl Docs](#concentrated-liquidity-campaigns-merkl-docs) - [Incentive Mechanisms | Merkl Docs](#incentive-mechanisms-merkl-docs) - [Airdrop Campaigns | Merkl Docs](#airdrop-campaigns-merkl-docs) - [Encompassing Campaigns | Merkl Docs](#encompassing-campaigns-merkl-docs) - [Scoring Types | Merkl Docs](#scoring-types-merkl-docs) - [Staking & Restaking Campaigns | Merkl Docs](#staking-restaking-campaigns-merkl-docs) - [Token Holding Campaigns | Merkl Docs](#token-holding-campaigns-merkl-docs) - [Additional Features | Merkl Docs](#additional-features-merkl-docs) - [Create a campaign from a multisig or Gnosis Safe | Merkl Docs](#create-a-campaign-from-a-multisig-or-gnosis-safe-merkl-docs) - [Fee Model | Merkl Docs](#fee-model-merkl-docs) - [Branding and Integration | Merkl Docs](#branding-and-integration-merkl-docs) - [Before you start | Merkl Docs](#before-you-start-merkl-docs) - [Distribution Types | Merkl Docs](#distribution-types-merkl-docs) - [Glossary | Merkl Docs](#glossary-merkl-docs) - [Smart Contract Addresses | Merkl Docs](#smart-contract-addresses-merkl-docs) - [Create a campaign | Merkl Docs](#create-a-campaign-merkl-docs) - [Display Your Native APR | Merkl Docs](#display-your-native-apr-merkl-docs) - [Create a campaign pre-TGE | Merkl Docs](#create-a-campaign-pre-tge-merkl-docs) - [Create your campaign from a DAO | Merkl Docs](#create-your-campaign-from-a-dao-merkl-docs) - [Create multiple campaigns | Merkl Docs](#create-multiple-campaigns-merkl-docs) - [Merkl User FAQ | Merkl Docs](#merkl-user-faq-merkl-docs) - [Campaign Management | Merkl Docs](#campaign-management-merkl-docs) - [Reward Forwarding | Merkl Docs](#reward-forwarding-merkl-docs) - [Earn rewards - User guide | Merkl Docs](#earn-rewards-user-guide-merkl-docs) - [Campaign Configuration | Merkl Docs](#campaign-configuration-merkl-docs) - [Create a point program | Merkl Docs](#create-a-point-program-merkl-docs) - [Customization Options | Merkl Docs](#customization-options-merkl-docs) - [Integrate Merkl API | Merkl Docs](#integrate-merkl-api-merkl-docs) --- # Merkl Documentation Portal | Merkl Docs Merkl is a DeFi incentive platform that **connects liquidity providers with protocols** looking to boost activity and engagement. * For Protocols: Launch, manage, and customize growth campaigns to attract liquidity, track user engagement, and distribute incentives without the usual operational burden. * For Users: Earn rewards or points by participating in incentive campaigns. At its core, Merkl operates on an offchain engine that processes both onchain and offchain data to compute rewards and points for campaigns. [](https://docs.merkl.xyz/#what-sets-merkl-apart) What sets Merkl apart? ----------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/#for-campaign-creators) For Campaign Creators * 🚀 **Launch Complex Incentives in Minutes**: Start a fully customized incentive campaign in less than 3 minutes—no need for weeks of development. * 📈 **Comprehensive Coverage**: Merkl supports incentives on top of various DeFi activities across multiple chains: * **Liquidity Incentives**: Reward LPs in concentrated (CLAMM) and constant product pools. * **Airdrops**: Retroactive token distributions based on snapshots or custom criteria. * **Lending/Borrowing**: Campaigns for lenders and borrowers of various types of lending protocols like Aave, Compound, Morpho, and more. * **Token Balance**: Reward token holders based on relative balances over time.pools. * 🛠 **Unmatched Flexibility**: Run multiple campaigns per asset with customizable distribution methods: * **Customizable Distribution Types**: Define how rewards are distributed to eligible addresses—whether through a fixed or variable reward rate that adapts to participant behavior, or via a custom, smart distribution logic tailored to your needs * **Crosschain Incentives**: Incentivize an asset on one chain while distributing rewards on another. * **Reward Boosts**: Increase rewards for users holding specific ERC20 tokens. * **Blacklist / Whitelist Controls**: Restrict eligibility based on addresses (e.g., compliance lists or protocol-specific criteria). * **Recover Unclaimed Rewards**: Reallocate unclaimed rewards to any destination of your choice, ensuring they remain effectively utilized. * **Control, Monitor, Update your Emissions**: Dynamically adjust and refine your reward campaigns even after launch, allowing you to dynamically manage incentives for maximum ROI and performance. * ⚡ **Smart Rewards Forwarding**: Automated detection of smart contracts & staking pools ensures rewards are correctly distributed even if users interact via third-party contracts. * 📊 **Advanced Analytics**: Gain deep insights into incentive performance, including reward distribution, liquidity source breakdowns, and engagement trends. * 🔌 **Effortless Integration**: Use the Merkl API for seamless data integration (APRs, rewards, analytics) or rely on the Merkl App for a ready-to-use interface. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-9b3e230962d4b3acb024f12eefb2ac0b598fad62%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-06-10%2520%25C3%25A0%252012.56.11%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=b760dadf&sv=2) Launch and manage your incentive campaigns in Merkl Studio ### [](https://docs.merkl.xyz/#for-users) For Users * 💰 **Maximize Earnings with Competitive APRs**: Access multiple campaigns for the same asset to stack incentives and boost returns. * 🔓 **No Staking Required**: Simply participate in eligible activities—no need to lock up assets or take on extra smart contract risks. * ⛽ **Gas-Efficient Rewards**: Batch claim rewards in a single transaction, saving gas fees. * 🌍 **A One-Stop Hub for DeFi Incentives**: With 20,000+ campaigns, Merkl provides a centralized dashboard to track earnings and discover the best opportunities. Merkl is a **non-custodial solution**. You retain full control over your assets—Merkl only processes reward claims. There is no additional smart contract risk beyond what’s already involved in your DeFi activities. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-6a8c6cde84a235ecf60aa79d9b75c23bcefe194a%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-06-10%2520%25C3%25A0%252011.47.52%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=d8058c2b&sv=2) Users can explore earning opportunities and claim their rewards on the Merkl App [](https://docs.merkl.xyz/#how-to-get-started) How to get started? ----------------------------------------------------------------------- ### [](https://docs.merkl.xyz/#for-campaign-creators-1) For Campaign Creators * Learn how Merkl works under the hood by checking the [Technical Overview](https://docs.merkl.xyz/merkl-mechanisms/technical-overview) * Explore available the versatility of [Merkl Incentive Mechanisms](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms) , including its [Campaign Types](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) , [Distribution Types](https://docs.merkl.xyz/merkl-mechanisms/distributions) , its [Customizability Hooks](https://docs.merkl.xyz/merkl-mechanisms/customization-options) or the [Additional Features](https://docs.merkl.xyz/merkl-mechanisms/features) it offers * Start your first campaign on [Merkl Studio](https://studio.merkl.xyz/) using our [guides](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) * Forwarder, deposit URL, dispute,... — check the [Glossary](https://docs.merkl.xyz/merkl-mechanisms/glossary) if there's a term you don't understand. ### [](https://docs.merkl.xyz/#for-users-1) For Users * Discover active campaigns & start earning in [the Merkl App](https://app.merkl.xyz/) * Learn how to earn rewards in Merkl by checking the [user guide](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl) * Opportunity, campaign, APR,... — check the [Glossary](https://docs.merkl.xyz/merkl-mechanisms/glossary) if there's a term you don't understand. [](https://docs.merkl.xyz/#links) 🔗 Links ----------------------------------------------- * [Merkl App](https://app.merkl.xyz/) * [Merkl Studio](https://studio.merkl.xyz/) * [Merkl Website](https://merkl.xyz/) * [Twitter](https://x.com/merkl_xyz) * [Discord](https://discord.com/invite/Gs8MUrUVP3) * [Audit](https://code4rena.com/reports/2023-06-angle) [](https://docs.merkl.xyz/#contact-us) 📩 Contact Us --------------------------------------------------------- Merkl is constantly evolving with new features and integrations. If you have specific use cases (e.g., perpetual DEX incentives, borrower-specific rewards, yield aggregator campaigns), or need guidance on your incentive or points program, contact us on [Discord (BD Ticket)](https://discord.gg/jnYfrGxDbe) . [NextTechnical Overview](https://docs.merkl.xyz/merkl-mechanisms/technical-overview) Last updated 5 months ago * [What sets Merkl apart?](https://docs.merkl.xyz/#what-sets-merkl-apart) * [For Campaign Creators](https://docs.merkl.xyz/#for-campaign-creators) * [For Users](https://docs.merkl.xyz/#for-users) * [How to get started?](https://docs.merkl.xyz/#how-to-get-started) * [For Campaign Creators](https://docs.merkl.xyz/#for-campaign-creators-1) * [For Users](https://docs.merkl.xyz/#for-users-1) * [🔗 Links](https://docs.merkl.xyz/#links) * [📩 Contact Us](https://docs.merkl.xyz/#contact-us) --- # Technical Overview | Merkl Docs Merkl operates through an offchain engine that analyzes both onchain and offchain data to track user activity and allocate rewards based on campaign rules set by campaign creators. The Merkl engine processes reward data into a merkle tree, compresses it into a merkle root, and pushes it onchain, enabling users to claim rewards transparently and efficiently. [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#platform-overview) 🗺️ Platform Overview ----------------------------------------------------------------------------------------------------------- Merkl operates through campaigns created by campaign creators. A campaign is a time-bound incentive program where Merkl tracks onchain and offchain activity based on predefined rules. Rewards or points are either posted onchain or updated offchain via Merkl’s endpoints, allowing users to monitor their accumulated rewards. ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#how-it-works) How it works 1. **Campaign Creation**: Campaign creators set up campaigns using a smart contract called the **Merkl Distribution Creator**. This includes defining eligibility criteria, reward structures, and distribution methods. Campaign details are pushed onchain, along with the incentive tokens that need to be distributed 2. **User Participation**: Users engage with the protocol (e.g., providing liquidity, lending, borrowing) in order to earn the rewards/points as specified in the campaign. 3. [**Reward Computation:**](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) At fixed intervals, a system called the **Merkl Engine** fetches campaigns from the **Merkl Distribution Creator** contract and processes reward calculations based on available onchain and offchain data and on the rules set by the campaign creator. **The Merkl Engine tracking is exhaustive and does not rely on discretionary snapshots**. 4. [**Reward Update:**](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) After each reward computation, users can see that they have earned **pending rewards** (awaiting onchain reward update). At regular intervals, (potentially different from the reward computation intervals), the Merkl Engine generates a merkle tree from processed campaigns. This tree is then compressed as a merkle root that is pushed onchain to a contract called the **Merkl Distributor** contract. A reward file is also made available [on the Merkl status page](https://app.merkl.xyz/status) , in order to ensure transparency and enable anyone to manually audit past reward distributions. 5. [**Dispute Period:**](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#dispute-process) After this step, a 1-2 hour dispute period begins, during which newly computed rewards cannot be claimed yet. However, rewards from the previous merkle root remain claimable. During this period, dispute bots verify the published reward file. If an inconsistency is found, a dispute can be raised to halt incorrect distributions before they become effective. 6. **Reward Availability:** Once the dispute period ends, users can see their rewards on any frontend integrated with the Merkl API. Users can claim rewards through a Merkl-powered frontend. Merkle proofs, required for claiming, are provided by the Merkl API or can be computed from reward files. ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#key-features) Key Features * **Single Merkl Root per Chain:** Merkl consolidates rewards from multiple campaigns into one merkle root per chain, enabling efficient batch claiming rewards from multiple different campaigns in a single transaction. * **Aggregated Campaigns:** While campaigns operate independently, Merkl batches multiple campaign updates into a single onchain transaction * **Automatic Catch-Up Mechanism:** If any rewards are not included in an update, they are automatically distributed in the next cycle. The Merkl engine ensures no missed rewards, processing only the data from the previous execution point. * **Unclaimed Rewards Roll Over**: Users can claim their rewards at anytime they want: each merkle tree update takes the previous merkle tree state and simply adds the new rewards, which are then reflected in the published merkle root. [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#deep-dive) 🤿 Deep-Dive ------------------------------------------------------------------------------------------ ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) Reward Computation **Reward computation** (or "compute") is the process by which the Merkl Engine calculates reward allocations based on campaign rules. **Computation mechanism**: The Merkl Engine relies on events to reconstruct the exact position of each participant. An integral is then computed over the selected time frame for every user, with no approximations—everything is exact. Users are rewarded proportionally to their individual area under the curve compared to the total area of all participants. In rare occurrences (e.g., debt accrual in lending markets), events alone are not enough to reconstruct the exact positions (e.g., debt accrual events are only emitted when a user interacts with the market), to remedy this, the Merkl Engine keeps track of **invariants** for these protocols, it compares at regular intervals the total event-based positions of users and the onchain value (e.g., calling `totalDebt()` on a lending market). If the two values differ by more than **1%**, the Merkl Engine will check each user's position onchain to update the computed value and remain exact. **Computation frequency**: The Merkl engine processes rewards for each campaign approximately every 2 hours on average, analyzing all relevant onchain events affecting the incentivized asset. **After computation**: Once a campaign completes its computation cycle, rewards are not immediately claimable onchain but appear as **pending rewards** in user dashboards. **Parallel processing**: Campaigns on a chain are processed independently and in parallel. This means rewards from some campaigns may be available onchain while others are still computing or awaiting updates. **Delayed computations**: Occasionally, campaign computations may be delayed due to processing constraints. Track delayed campaigns on the [Merkl status page](https://app.merkl.xyz/status) . **Continuous processing**: When a campaign is processed, the Merkl Engine resumes from its last checkpoint, ensuring complete reward coverage even when updates occur at different intervals. **Reward delays & retroactive distribution** Occasional delays of a few hours may occur in reward distribution. If an invariant is triggered or a third-party data feed experiences a temporary issue, calculations are paused until all safety checks pass at the next scheduled computation. When this happens, the Merkl system is designed so that **all rewards are distributed retroactively**, with no rewards undistributed. ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) Reward Updates **Reward updates** are the process of posting computed rewards onchain, making them claimable by users. **Update frequency**: Reward updates occur approximately every 8 hours (ranging from 4 to 12 hours), depending on the chain. **Delayed updates**: Like computations, updates may occasionally be delayed. Monitor update status on the [Merkl status page](https://app.merkl.xyz/status) . **Transparency**: Each reward update includes a publicly available reward file on the status page for transparency and auditability. **Independent cycles**: Reward computation and reward updates operate on independent schedules. Updates push the results of completed computations onchain, but the two processes follow separate lifecycles. **Example**: Between two reward updates (8 hours apart on average), a campaign's rewards may be recomputed up to four times. Each computation refines the pending rewards, but they only become claimable when the next update occurs. **Rollover mechanism**: Unclaimed rewards automatically roll over into subsequent reward updates, allowing users to claim at their convenience. **Tracking updates**: View the last reward update for each chain on the [Merkl status page](https://app.merkl.xyz/status) . ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#dispute-period-and-process) Dispute Period & Process The **dispute period** is a security window of 1-2 hours following each reward update during which anyone can contest reward allocations made by the Merkl engine. This safeguard ensures the integrity and consistency of the reward infrastructure and allows campaign creators to oversee the reward computations before they become final and claimable. **Raising a dispute**: If an issue is detected during the dispute period or if you want to contest reward allocations for a campaign, raise a dispute by sending a `disputeToken` to the Merkl Distributor contract. **Dispute outcomes**: * **Valid dispute**: The merkle root is revoked and the disputer is refunded * **Invalid dispute**: The disputer forfeits their funds and the dispute period restarts **Dispute parameters**: Retrieve dispute conditions (`disputeToken`, `disputeAmount`, `disputePeriod`) from the Distributor contract on the relevant chain. #### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#dispute-bots) Dispute Bots **Security infrastructure**: The Merkl team operates several independent dispute bots on separate infrastructure to ensure redundancy and prevent malicious actors from compromising the reward update process. **Open-source reference**: While the production bot code is closed source for security reasons, you can reference [this open-source repository](https://github.com/AngleProtocol/merkl-dispute) to understand how to build a dispute bot. **Strengthen the network**: More active dispute bots make the system more secure. Need help setting one up? Reach out to the Merkl team—we're happy to assist! [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#key-merkl-concepts) 📌 Key Merkl Concepts ------------------------------------------------------------------------------------------------------------ ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#opportunity-vs.-campaign) Opportunity vs. Campaign Understanding the distinction between **opportunities** and **campaigns** is fundamental to how Merkl operates. * **Campaign**: An individual incentive program created by a campaign creator with specific parameters including [a distribution type](https://docs.merkl.xyz/merkl-mechanisms/distributions) , [a scoring type](https://docs.merkl.xyz/merkl-mechanisms/scoring) , [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) , a budget amount, and a duration. Each campaign has [a specific type](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) and targets a particular onchain behavior (e.g., providing liquidity in a pool, holding a token, lending/borrowing)—this targeted behavior represents an opportunity. * **Opportunity**: A specific asset (e.g., pool, vault) and its associated action (e.g., depositing liquidity, borrowing assets) that can be incentivized. Multiple campaigns can run in parallel on a single opportunity, meaning users performing one onchain action can simultaneously earn rewards from several different campaigns. Example: _Providing liquidity to a SushiSwap V3 pool is an opportunity that may have multiple active campaigns offering different rewards._ ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-8465bb5f03eba6831d981e5103eb0dd32d256392%252FGroup%252023.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=2218108f&sv=2) An opportunity page with several incentive campaigns running on it ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#main-metrics) Main Metrics The following metrics are fundamental to Merkl and appear throughout all Merkl user-facing components: the API, the app, and Merkl Studio. #### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#daily-rewards) Daily Rewards Daily rewards for a campaign represent the total amount of tokens or points distributed each day, shared among all eligible users of the campaign. While this number is typically accurate, it may be estimated for certain [distribution types](https://docs.merkl.xyz/merkl-mechanisms/distributions) (such as fixed and capped reward rates) that distribute based on the eligible TVL in the campaign. When multiple campaigns run on an opportunity, including subcampaigns, the daily rewards displayed for the opportunity represent the sum of all individual campaign daily rewards. #### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#tvl) TVL The Total Value Locked (TVL) of a campaign on Merkl represents the total value of **eligible** assets for the campaigns on the opportunity. This reflects only the assets that meet the campaign's eligibility criteria, for example: * if a campaign includes a blacklist, the TVL excludes the value held by blacklisted addresses. * for certain campaign types like net-lending campaigns, the TVL represents the net supplied TVL (i.e., total supplied minus borrowed), as this reflects only the liquidity that's eligible for rewards. In these cases, Merkl may initially approximate the TVL and requires an engine computation to display the accurate TVL for the campaign. As such, the TVL may not be accurate at launch for newly created campaigns. In some situations, the TVL might also be over- or underestimated due to approximations made for computational efficiency or because exact computing logic has not yet been implemented. When the Merkl system has a fallback mechanism, TVL values update every 10 minutes on the Merkl app and API. For complex campaigns without such fallbacks, TVLs (and consequently APRs) only update after each computation cycle. This means TVL updates may occur at most every 2 hours for the opportunities related to these campaigns. Overall, the TVL serves as a key indicator of the market or pool's size and liquidity depth. When multiple campaigns run on the same opportunity with different eligibility rules, the displayed TVL for the opportunity is **the maximum eligible TVL** across all campaigns. #### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#apr) APR The Annual Percentage Rate (APR) represents the yearly return from participating in a campaign, expressed as a percentage. Depending on the [distribution type](https://docs.merkl.xyz/merkl-mechanisms/distributions) , the APR can be fixed and remain constant throughout the campaign, or it can be variable and fluctuate based on factors such as eligible TVL and the number of participants. For [distribution types](https://docs.merkl.xyz/merkl-mechanisms/distributions) where the APR is not fixed, the APR for a campaign is calculated as: Daily Rewards×365Eligible TVL\\frac{\\text{Daily Rewards} \\times 365}{\\text{Eligible TVL}}Eligible TVLDaily Rewards×365​ In these cases, the APR updates at the same frequency as the eligible TVL (every 10 minutes for simple campaigns, or every 2 hours for more complex campaigns where TVL cannot be simply approximated). At the opportunity level, the displayed APR is the sum of the APRs from all campaigns ([including subcampaigns](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#linked-opportunities) ) running on that opportunity. [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#user-facing-components) 🧱 User-Facing Components -------------------------------------------------------------------------------------------------------------------- Beyond the Merkl engine and dispute bots, the Merkl ecosystem includes several user-facing components that work together to enable campaign creation, reward tracking, and claiming. ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-smart-contracts) Merkl Smart Contracts Deployed on each blockchain integrated by Merkl, Merkl's smart contracts store campaign data, hold incentive tokens, and process reward claims. These contract are [publicly available](https://github.com/AngleProtocol/merkl-contracts) and have been audited by Code4rena ([Audit Report](https://code4rena.com/reports/2023-06-angle) ). Key contracts: * `DistributionCreator`: Stores campaign details and configurations. * `Distributor`: Holds tokens and processes reward claims based on merkle proofs. * `AccessControlManager`: A multisig-controlled contract that manages disputes, fees, and access control but cannot alter distributions. Smart contract addresses, categorized by chain are listed [here](https://app.merkl.xyz/status) . ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-api) Merkl API [Merkl API](https://docs.merkl.xyz/integrate-merkl/app) provides real-time access to Merkl data, including rewards, APRs, merkle proofs, and analytics. This API enables any frontend to integrate Merkl seamlessly. ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-app) Merkl App [The Merkl App](https://app.merkl.xyz/) , built on the Merkl API, enables users to explore reward opportunities, track APRs, and seamlessly claim their rewards. ![Homepage of the Merkl app](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-f0f88bee46f646fc81fe16684fd112d789d0266d%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-09-17%2520%25C3%25A0%252010.51.55%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=c52d7826&sv=2) Home page of the Merkl App The interface is organized around several types of pages: * **Home page (all opportunities)** – displays all available opportunities with filtering options * **Opportunity page** – dedicated view for each opportunity with all its campains * **Protocol / Chain / Liquidity program page** – groups all opportunities related to a specific protocol, chain, or program * **Dashboard** – where users can claim their rewards ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-66c4725495129ad3292956be237448baf02a04f9%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-11-12%2520%25C3%25A0%252015.14.56%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=66fe12f&sv=2) Dashboard page where users can claim their rewards Among all these pages, the **Opportunity page** is central, as this is where you’ll find the campaigns created. Detailed info for each campaign running on an opportunity is available across several tabs: * **Overview:** global details such as dates, APR, and eligibility rules,… * **Advanced**: distribution progress, last snapshot, creator address, and campaign ID,… * **Leaderboard**: list of addresses participating in the opportunity * [**Linked opportunities**](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#linked-opportunities) _(optional):_ displays opportunities connected through shared liquidity and rewards ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-76b81415c35d023dc338b3210960c879ac352dc3%252FGroup%252024.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=452c132d&sv=2) Campaign-specific info on the opportunity page ### [](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-studio) Merkl Studio [Merkl Studio](https://studio.merkl.xyz/) is the command center for campaign creators. It enables anyone to launch and manage incentive campaigns independently within minutes, providing powerful tools for campaign configuration and oversight. [PreviousMerkl Documentation Portal](https://docs.merkl.xyz/) [NextIncentive Mechanisms](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms) Last updated 1 month ago * [🗺️ Platform Overview](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#platform-overview) * [How it works](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#how-it-works) * [Key Features](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#key-features) * [🤿 Deep-Dive](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#deep-dive) * [Reward Computation](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) * [Reward Updates](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) * [Dispute Period & Process](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#dispute-period-and-process) * [📌 Key Merkl Concepts](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#key-merkl-concepts) * [Opportunity vs. Campaign](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#opportunity-vs.-campaign) * [Main Metrics](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#main-metrics) * [🧱 User-Facing Components](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#user-facing-components) * [Merkl Smart Contracts](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-smart-contracts) * [Merkl API](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-api) * [Merkl App](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-app) * [Merkl Studio](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-studio) --- # Campaign Types | Merkl Docs [Concentrated Liquidity Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms) [Airdrop Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop) [Token Holding Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms) [Lending and Borrowing Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing) [Encompassing Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing) [Staking & Restaking Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking) [PreviousIncentive Mechanisms](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms) [NextConcentrated Liquidity Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms) Last updated 11 months ago --- # Lending and Borrowing Campaigns | Merkl Docs [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#supported-protocols) 🫴🏼 Supported protocols ------------------------------------------------------------------------------------------------------------------------------ Merkl supports **most major lending and borrowing protocols**, such as Morpho, Euler, Compound, and Aave. A non-exhaustive list of supported protocols can be found in the ["Lending & Borrowing" section of Merkl Studio](https://studio.merkl.xyz/create-campaign/lend) when selecting a protocol. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-84b2cb841c8c3feb329b9cebe522ca5d5433ece1%252FGroup%252014.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=ce01a3b0&sv=2) Merkl natively supports **all lending and borrowing protocols that issue receipt tokens** for lenders and debt tokens for borrowers — such as Aave with aTokens. For these protocols, please use the [“Token Holding” incentive category](https://studio.merkl.xyz/create-campaign/erc20) in Merkl Studio, as you are incentivizing a token that represents a lending position. If you’d like your lending and borrowing protocol to be fully integrated and supported by Merkl — or if you're looking to create tailored protocol-specific campaigns — please reach out on the [Merkl Discord](https://discord.com/invite/jnYfrGxDbe) by opening a BD ticket to discuss the integration process. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#preventing-lending-loops) 🔁 Preventing lending loops -------------------------------------------------------------------------------------------------------------------------------------- Merkl allows campaign creators to reward users based on their **net lending position** (lending minus borrowing) to avoid lending and borrowing loops. As a campaign creator, you have the possibility to enable net lending on a specific opportunity. When enabled, users will not get extra rewards by borrowing the same asset and re-lending it again. This is to ensure there are no infinite loops & unefficient TVL. For example, if you lend $100 wstETH and borrow $60 wstETH, you get rewarded for $40 wstETH ($100 - $60). Note: this is not always the case. If you’ve got a doubt about net lending when preparing a campaign, feel free to reach out to us. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#morpho-multi-market-campaign) 🦋 Morpho multi-market campaign ---------------------------------------------------------------------------------------------------------------------------------------------- With Merkl you can **incentivize a single token (e.g. USDC) across all Morpho markets with just one campaign!** No need to precise a specific markets or vault to incentivize. Merkl detects all Morpho markets where the selected incentivized token is active and also identifies every wallet that interacted with those markets. Then, Merkl automatically cascades rewards across every whitelisted Morpho markets where the token is used based on: * the liquidity and size of each market * the user’s share of liquidity To create a multi-market campaign, you just need to select any of these three options when selection an action to incentivize in the Choose target step in Merkl Studio: * `Supply a token to any market` * `Borrow a token from any market` * `Use a token as collateral on any market` ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-33f52a2f140a030e8691843c9a0be9861a65b533%252FGroup%252015.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=798df288&sv=2) **Who is this designed for?** The multi-market campaign feature is especially aimed at **token issuers**, such as stablecoin protocols. It allow them to grow their token easily by running a campaign across all Morpho markets — for example, rewarding DeFi users who use their token as collateral. [PreviousToken Holding Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms) [NextEncompassing Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing) Last updated 1 month ago * [🫴🏼 Supported protocols](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#supported-protocols) * [🔁 Preventing lending loops](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#preventing-lending-loops) * [🦋 Morpho multi-market campaign](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#morpho-multi-market-campaign) --- # Concentrated Liquidity Campaigns | Merkl Docs [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#overview) 🌐 Overview ---------------------------------------------------------------------------------------------------------------------- Merkl’s Concentrated Liquidity campaigns allow campaign creators to reward Liquidity Providers (LPs) on concentrated liquidity AMMs like Uniswap V4, Uniswap v3, Quickswap, and Sushiswap. Unlike traditional incentives where users are rewarded based on deposit amount, Concentrated Liquidity campaigns reward LPs based on **how they provide liquidity** — within specific price ranges — **and their contribution to the overall liquidity of the pool.** LPs who provide liquidity in optimal, high-impact ranges are rewarded more generously, ensuring capital efficiency. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-3e38146d6bca70dce2a303adeb49a66aa0dca229%252FConcentrated-liquidity.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=63ca474&sv=2) [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#reward-formula) 🔢 Reward formula ---------------------------------------------------------------------------------------------------------------------------------- When campaign creators launch concentrated liquidity campaigns, they define: * The pool to be incentivized (using the **pool address or ID**) * A **reward period** * A set of **incentive parameters**, which define how rewards will be distributed among liquidity providers. * **In-Range positions only**: Only reward positions that are being used in swaps * **Liquidity Contribution**: Measures the **time-weighted contribution of a position to the overall liquidity in the pool**. Positions that remain tightly concentrated around the active tick contribute more liquidity—assuming equal TVL—than those spread across a wider range. * **Token 0 holding**: The share of token 0 held by a position relative to the total token 0 in the pool. * **Token 1 holding**: The share of token 1 held by a position relative to the total token 1 in the pool. A position is "in range" when the current market price falls within the price range specified by the position's lower and upper ticks. When a position is in range, it actively provides liquidity for trades and earns fees. Out-of-range positions do not contribute to liquidity at the current price and therefore don't earn trading fees. We highly recommend only rewarding in range positions. A position might generate high fees during a short burst of activity, but if it wasn’t consistently concentrated throughout the reward period, it won’t earn significant rewards. What matters here is **sustained concentration over time**, not short-term volume or swap activity. Each factor is assigned a weight (% liquidity contribution, % token0, % token1), which the campaign creator defines when setting up the campaign in Merkl Studio. Merkl analyzes liquidity within the pool during the incentive period and assigns rewards based on these 3 parameters. With this setup, this is as if the overall incentive budget was split in 3, with a proportion being shared by LPs based on how much liquidity they have on the active tick, a proportion shared based on the overall amount of token 0 they've held and a last portion based on the relative token 1 balance they've had in their position during the time period. Merkl released an improved version of its concentrated liquidity reward engine to bring major enhancements—delivering a more performant, stable, and JIT attack-resistant reward mechanism. The system enables precise measurement of liquidity over time, resulting in more deterministic and reliable attribution of LP contributions while minimizing the influence of short-term volatility. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#example-calculation) 🧮 Example Calculation If incentive weights are set as: * Liquidity contribution = 50% * Token 0 = 25% * Token 1 = 25% Then: * A user representing 40% of the total liquidity in the pool (and hereby earning ~40% of the fees accruing to the pool) receives 20% of total rewards (40% × 50%). * A user holding 30% of Token 0 receives 7.5% of total rewards (30% × 25%). * A user holding 20% of Token 1 receives 5% of total rewards (20% × 25%). [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#reward-strategy-examples) ♟️ Reward strategy examples ------------------------------------------------------------------------------------------------------------------------------------------------------ Campaign creators can fine-tune parameters to match their objectives: * **Encouraging deep liquidity:** Increasing the weight of liquidity contribution / fee earned to encourage deep liquidity in high-volume trading ranges. * **Stablecoin peg protection:** To help maintain a stablecoin peg (e.g., USDC), increasing the weight of Token 0 rewards can encourage LPs to hold more of the pegged token in the pool, discouraging dumps and supporting stability. * **Optimized tight range incentives:** With Uniswap V4 Hooks, LPs can implement automated rebalancing strategies to maintain optimal tight-range positions and maximize reward efficiency. Here are some suggestions depending on your needs and pool type: * For stable pools/pegged pools (e.g., USDC/USDT or WBTC/BTC): Liquidity contribution = 80%, token0 = 10%, token1 = 10% * For volatile pools (e.g., WETH/USDC): Liquidity contribution = 20%, token0 = 40%, token1 = 40% (the higher the volatility, the higher the weights for token0 and token1 percentages should be) * You can also refer to the Merkl blog about [liquidity walls](https://blog.merkl.xyz/merkl-insights-how-can-incentives-prevent-your-token-from-dumping) explaining how to prevent a token from dumping, or a stablecoin from losing its peg Merkl is not responsible for defining your strategy and targets. Nevertheless, you can refer to live campaigns to look for typical APRs and/or parameter weights on similar campaigns. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#sampling-and-anti-dos) 💉 Sampling and Anti-DoS ------------------------------------------------------------------------------------------------------------------------------------------------ For large, high-activity pools in Uniswap v3 and similar AMMs, Merkl samples the largest swaps rather than processing every transaction. * Out-of-range positions detected during sampled swaps are excluded from rewards. * Users earning less than 0.0000001% of total rewards are filtered out. * Positions with less than $10 liquidity are disqualified. To maximize rewards, LPs should actively manage their positions to stay in-range. On the other hand, Uniswap V4 incentive distribution leverages Merkl's `LogProcessor` algorithm, which continuously tracks **the state of each pool in real time.** As a result, each campaign calculates an average value for each position, ensuring users are rewarded based on both the size of their position and how long they keep it open. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#out-of-range-liquidity) ⛔ Out of range liquidity ------------------------------------------------------------------------------------------------------------------------------------------------- Campaign creators can choose whether to reward out-of-range liquidity. By default, only in-range positions receive rewards. We strongly recommend incentivizing in-range liquidity, as this ensures active liquidity provision and avoids rewarding idle capital. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#fake-volume-attacks-detection) 🔎 Fake volume attacks detection ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Merkl automatically detects and blacklists users attempting to game the system via: * Wash trading: Creating tight positions and self-trading to earn rewards. * Frequent rebalancing: If mistaken for wash trading, may trigger blacklisting. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#tick-price-limits-and-eligibility-on-uniswap-v4) ☑️ Tick price limits & eligibility on Uniswap v4 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- To qualify for rewards under a campaign with a tick price limits eligibility rule, a liquidity position must meet both of the following conditions: * (if set by the campaign creator) Upper Tick Price must be below the specified `threshold_0`: Upper Tick Price < `threshold_0` * (Resp.) Lower Tick Price must be above the specified threshold: Lower Tick Price > `threshold_1` Here, price refers to the ratio of token 1 to token 0. For example, if token 1 is ETH and token 0 is BTC, the price reflects the ETH/BTC exchange rate. This rule ensures that only positions providing liquidity within a targeted price range are eligible for rewards. Positions exceeding these limits on either side are not eligible. Example: Suppose the thresholds are: * Upper Price Threshold = 1,050 token 1 per token 0 * Lower Price Threshold = 950 token 1 per token 0 Then: * A position from 960 to 1,040 qualifies (upper < 1,050, lower > 950). * A position from 940 to 1,020 does not qualify (lower < 950). * A position from 990 to 1,080 does not qualify (upper > 1,050). This setup encourages liquidity within a narrow, strategic price corridor, optimizing depth and efficiency around critical price levels. The thresholds are set independently — it is possible to define a limit for the upper tick price without setting one for the lower tick price (and vice versa). [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#aprs-calculation) 📈 APRs calculation -------------------------------------------------------------------------------------------------------------------------------------- APRs shown on the Merkl interface for concentrated liquidity campaigns represent average values. They are calculated by dividing the annualized reward emissions for a pool by its current TVL. However, actual APR varies per position. A position may earn more or less than the displayed average depending on factors like: * How tightly liquidity is concentrated * How others are providing liquidity * The weight of campaign’s parameters Example: * If liquidity contribution = 99%, a full-range position may earn minimal rewards despite providing high liquidity. * If Token A = 60% and Token B = 10%, LPs holding more Token A will earn disproportionately higher rewards and you may be better off skewing your position so it has more of token A than of token B. The TVL of concentrated liquidity pools is updated every 2 hours and excludes blacklisted liquidity (so if the pool TVL is $1M and a blacklisted user holds 30% of the pool's TVL, the displayed TVL will be $700k) [PreviousCampaign Types](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) [NextAirdrop Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop) Last updated 1 month ago * [🌐 Overview](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#overview) * [🔢 Reward formula](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#reward-formula) * [🧮 Example Calculation](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#example-calculation) * [♟️ Reward strategy examples](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#reward-strategy-examples) * [💉 Sampling and Anti-DoS](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#sampling-and-anti-dos) * [⛔ Out of range liquidity](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#out-of-range-liquidity) * [🔎 Fake volume attacks detection](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#fake-volume-attacks-detection) * [☑️ Tick price limits & eligibility on Uniswap v4](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#tick-price-limits-and-eligibility-on-uniswap-v4) * [📈 APRs calculation](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#aprs-calculation) --- # Incentive Mechanisms | Merkl Docs Merkl is designed to be highly versatile, supporting a wide range of incentive structures across both DeFi and broader Web3 ecosystems. [](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#campaign-design) 🔧 Campaign Design -------------------------------------------------------------------------------------------------------- When creating a campaign on Merkl, campaign creators can configure: ### [](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-1-campaign-type) 1️⃣ Campaign Type [Campaign types](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) define the action that needs to be done to be eligible to rewards. Merkl supports multiple campaign types, rewarding users for both financial activities (e.g., liquidity provision, lending) and other onchain/offchain actions. Some of the most commonly used campaign types include * [**Concentrated Liquidity**](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms) : Rewards liquidity providers (LPs) in Concentrated Liquidity AMMs (CLAMMs) such as Uniswap V3 or Uniswap V4. * [**Lending & Borrowing**](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing) : Encourages activity on lending protocols like Morpho, Euler, or Aave, or rewards specific behaviors within these protocols. * [**Airdrop**](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop) : Distributes tokens to a potentially millions of users based on either a JSON file or a snapshotted token balance. * [**Token Holding**](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms) : Rewards users for holding an ERC20 token over time. This can be used for virtually any protocol that gives ERC20 receipt tokens to their stakeholders. ### [](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-2-distribution-type) 2️⃣ Distribution Type [Distribution types](https://docs.merkl.xyz/merkl-mechanisms/distributions) define the **reward distribution model** and **how the total campaign budget is spent over time**. Common models include: * [Variable reward rate campaigns](https://docs.merkl.xyz/merkl-mechanisms/distributions#variable-reward-rate-campaigns) : rewards are distributed proportionally based on time-weighted liquidity within the eligibility pool. * [Fixed reward rate campaigns](https://docs.merkl.xyz/merkl-mechanisms/distributions#fixed-reward-rate-campaigns) : a predefined amount of rewards per unit of liquidity is distributed at a fixed rate. * [Capped reward rate campaigns](https://docs.merkl.xyz/merkl-mechanisms/distributions#capped-reward-rate-campaigns) : similar to variable rate campaigns, but with a maximum APR that cannot be exceeded. ### [](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-3-scoring-type) 3️⃣ Scoring Type [Scoring types](https://docs.merkl.xyz/merkl-mechanisms/scoring) define **how individual user contributions are measured and converted into reward shares**. Common models include: * **1:1 mapping (default)**: User reward share equals their share of total contributions * **Max balance scoring**: Caps eligible balances by taking the minimum between a user's time-weighted balance and a predefined maximum ### [](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-4-customization-options) 4️⃣ Customization Options Merkl supports a wide range of [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) to further personalize campaigns beyond core settings. These include filters to dynamically restrict or boost the eligible users for a campaign based on whether they hold a token. [](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#feature-compatibility) 🔄 Feature Compatibility -------------------------------------------------------------------------------------------------------------------- Not all campaign types are compatible with all distribution types. Similarly, some customization options may only work with specific campaign or distribution types. To check the status of Merkl’s features, the compatibility between campaign types, distribution or scoring types, or to view the list of supported chains and tokens, simply visit [**Merkl Studio**](https://studio.merkl.xyz/) and simulate the creation of a campaign. Some [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) , [campaign types](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) , [distribution types](https://docs.merkl.xyz/merkl-mechanisms/distributions) and [scoring types](https://docs.merkl.xyz/merkl-mechanisms/scoring) are not configurable directly via Merkl Studio. In such cases, we recommend reaching out to us — we can either configure your campaign for you or provide dedicated API endpoints to help you set it up. Merkl’s capabilities continuously expand, adding support for new campaign types, distribution methods, and customization options. If you need a custom incentivization model, contact us by opening a [BD ticket on Discord](https://discord.gg/jnYfrGxDbe) or sending a message on Telegram. [PreviousTechnical Overview](https://docs.merkl.xyz/merkl-mechanisms/technical-overview) [NextCampaign Types](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) Last updated 1 month ago * [🔧 Campaign Design](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#campaign-design) * [1️⃣ Campaign Type](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-1-campaign-type) * [2️⃣ Distribution Type](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-2-distribution-type) * [3️⃣ Scoring Type](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-3-scoring-type) * [4️⃣ Customization Options](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#id-4-customization-options) * [🔄 Feature Compatibility](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms#feature-compatibility) --- # Airdrop Campaigns | Merkl Docs Merkl supports airdrop campaigns, simplifying the process for protocols looking to airdrop tokens to their community by allowing them to get it done in a few minutes with just a few clicks while minimizing gas fees. Merkl supports two different types of airdrops: 1. **Custom Allocation Airdrops**: Campaign creators define the reward distribution, specifying which users receive what amount. Providers upload a JSON file with allocation details, and Merkl handles the distribution via its claim system. 2. **Snapshot-Based Airdrops**: Merkl distributes rewards based on a snapshot of a given token’s balance at a chosen point in time. Airdrops do not appear “live” like continuously computed campaigns because the Merkl Engine performs no ongoing calculation. To review your airdrop in the main opportunities page after creation, use this filtered view: [https://app.merkl.xyz/?status=LIVE%2CSOON%2CPAST&sort=lastCampaignCreatedAt-desc](https://app.merkl.xyz/?status=LIVE%2CSOON%2CPAST&sort=lastCampaignCreatedAt-desc) \\ [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#json-airdrops) JSON airdrops --------------------------------------------------------------------------------------------------- For Custom Allocation Airdrops, providers only need to upload a JSON file specifying user addresses and reward amounts. To create your JSON Airdrop campaign on Merkl, visit this [page](https://studio.merkl.xyz/create-campaign/airdrop) . The expected format for the JSON is the following Copy type AirdropJSON = { // Token being distributed rewardToken: string; // User rewards rewards: { // recipient: user address [recipient: string]: { // Reason: how the user got the rewards [reason: string]: string; // value: amount with all decimals as a string }; }; }; Example: * **rewardToken:** The token being distributed. * **rewards:** User rewards with recipient addresses and reasons for the rewards. **Important**: All addresses in your JSON file MUST be in [checksummed format](https://eips.ethereum.org/EIPS/eip-55) . Non-checksummed addresses will cause the campaign to fail. Use tools like [Etherscan](https://etherscan.io/) or ethers.js `getAddress()` to convert addresses to the correct format. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#fees-for-airdrops) Fees for airdrops Merkl applies a 0.5% fee to airdrop campaigns. This fee is added on top of the total airdropped amount, ensuring recipients receive the full intended distribution. * If you want exactly 100,000 tokens to be distributed to users, you need to provide 100,502.51 tokens (calculated as 100,000 / (1 - 0.5%)). * If you prefer to send exactly 100,000 tokens from your wallet, then the total sum of allocations in your JSON file should be 99,500 tokens (calculated as 100,000 \* (1 - 0.5%)). Our frontend automatically calculates the correct amount for you. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#airdrop-timing) Airdrop timing Once an airdrop campaign is created and launched on Merkl, it usually takes up to **12 hours** before the **tokens become claimable** by users (as the airdrop becomes available at the next [reward update](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) on the target chain). We recommend until the rewards are claimable **to notify your community** that the airdrop is live. As mentioned above, airdrops always appear as `PAST` on the Merkl app, with **campaign end date** in the past. Note that the end date on the Merkl app is **different from the airdrop claim end date**: it is the date when new rewards stop distributing. Even after a campaign is marked as “ended” on the Merkl App, users can still claim their tokens via their Merkl dashboard. The **campaign end date** on the Merkl App **cannot be modified**. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#controlling-claim-dates-with-token-wrappers) Controlling Claim Dates with Token Wrappers As explained [in this page of our docs](https://docs.merkl.xyz/merkl-mechanisms/features#-infinite-customizability-with-token-wrappers) , it is possible to distribute wrappers of standard ERC20 tokens using Merkl. In the context of an airdrop, we offer a wrapper type that allows you to retain the tokens you airdrop in your wallet and maintain full control over when they become claimable by users. Once your wrapper is deployed and you've minted wrapper tokens, the process works as follows: 1. Create your airdrop campaign on Merkl, distributing your wrapper token instead of the underlying token you want to airdrop 2. Wait for a reward update for the wrapper tokens to become claimable on Merkl 3. The wrapper tokens are typically marked as test tokens, making them invisible on the frontend 4. Users can only claim the actual tokens once you: * Approve the wrapper token contract to spend the underlying token * Hold the underlying tokens in your address This approach is particularly useful for TGE (Token Generation Event) processes where tokens must only become claimable after a specific date chosen by the campaign creator. From a user perspective, users only see the underlying token arriving in their wallet, not the wrapper token. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#token-balance-airdrops) Token balance airdrops --------------------------------------------------------------------------------------------------------------------- Merkl allows you to distribute tokens to holders based on their snapshotted balance at a chosen moment in time. For this type of campaign, in addition to the reward amount, campaign creators must specify: * Snapshot Date – The balance snapshot is taken at the last block before this date. * Distribution Date – The date on which rewards should be distributed. * Snapshot Name – A label for your snapshot. [PreviousConcentrated Liquidity Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms) [NextToken Holding Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms) Last updated 18 days ago * [JSON airdrops](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#json-airdrops) * [Fees for airdrops](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#fees-for-airdrops) * [Airdrop timing](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#airdrop-timing) * [Controlling Claim Dates with Token Wrappers](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#controlling-claim-dates-with-token-wrappers) * [Token balance airdrops](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop#token-balance-airdrops) Copy { "rewardToken": "0x1a7e4e63778B4f12a199C062f3eFdD288afCBce8", "rewards": { "0x529619a10129396a2F642cae32099C1eA7FA2834": { "reason1": "1827034243855622331" }, "0x0C2553e4B9dFA9f83b1A6D3EAB96c4bAaB42d430": { "reason1": "1827034243855622331", "reason2": "1035298980258165611" } } } --- # Encompassing Campaigns | Merkl Docs Merkl's All Encompassing Campaigns enable protocols to distribute rewards without requiring Merkl's Engine to calculate reward allocation. In this model, partners provide some API endpoints containing opportunity data and rewards, which are distributed based on the provided output. The Merkl Engine aggregates the computed reward data provided by the partner into a Merkle root and pushes it onchain, enabling users to claim their rewards. Once [a reward update](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) has been processed, it becomes immutable and cannot be reverted. Nevertheless, campaign data can be dynamically updated throughout the campaign duration by modifying the reward endpoint output, with the final update required before the campaign's end date. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#campaign-configuration) 🛠️ Campaign configuration ------------------------------------------------------------------------------------------------------------------------------ Campaigns creators must provide the following information upon creation: * 📅 Start Date – The date on which the campaign starts. * 📅 End Date – The date on which the campaign ends. * 🎁 Reward url - A public endpoint returning a reward JSON file. * 📊 Data url - A public endpoint returning a data JSON file. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#id-1.-reward-json) 1\. 🎁 Reward JSON The expected format for the JSON reward is the following Copy type EncompassingJSON = { // Token being distributed rewardToken: string; // User rewards rewards: { // recipient: user address [recipient: string]: { // Reason: how the user got the rewards [reason: string]: { // amount: amount with all decimals as a string amount: string; // timestamp: unix timestamp in seconds, date at which the reward is sent to the recipient (must not exceed the end date of the campaign) timestamp: string; } }; }; }; Example: * **rewardToken:** The token being distributed. * **rewards:** User rewards with recipient addresses, reasons, amount and timestamp for the rewards. Good to know: * `timestamp` is the unix timestamp in seconds, date at which the reward is sent to the recipient. This timestamp must not exceed the end date of the campaign (it will not be processed if it's after the end date), and if it's before the start date of the campaign, the reward will be sent to the recipient at the start date. * Once the engine has processed some rewards, even if the reward endpoint is updated, the rewards already distributed can't be reverted. If a campaign creator needs to send additional rewards to the same user, he can do it by adding another reason for the same recipient. Example: **Keep recent history:** Include at least 3/4 days of past entries in your Reward JSON so that, if there are delays, the Merkl engine can still fetch and process recent rewards without requiring you to re-upload old data. **Give yourself a buffer with the end date:** We recommend setting the end date of your campaign 24 hours after you expect to distribute the last funds to make sure all rewards are processed before the end of the campaign. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#id-2.-data-json) 2\. 📊 Data JSON The expected format for the JSON data is the following Example: This data will be used to display the campaign in the Merkl frontend. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#validating-your-endpoints) Validating your endpoints You can use this python script to validate that your endpoints are in the correct format[](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#you-can-use-this-python-script-to-validate-that-your-endpoints-are-in-the-correct-format) ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#important-note) ⚠️ Important Note Merkl applies a 0.5% fee to this type of campaigns. This fee is added on top of the total airdropped amount, ensuring recipients receive the full intended distribution. * 💡 If you want exactly 100,000 tokens to be distributed to users, you need to provide 100,502.51 tokens (calculated as 100,000 / (1 - 0.5%)). * 💡 If you prefer to send exactly 100,000 tokens from your wallet, then the total sum of allocations in your JSON file should be 99,500 tokens (calculated as 100,000 \* (1 - 0.5%)). Our frontend automatically calculates the correct amount for you. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#distribution-lag) ⏳ Distribution lag Tokens become claimable at the next [reward update](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) on the target chain, which typically occurs within 8 hours. If you plan to announce the distribution, we recommend waiting until the rewards are claimable to notify your users. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#tutorial-for-static-encompassing-campaigns) Tutorial for static encompassing campaigns ------------------------------------------------------------------------------------------------------------------------------------------------------------------ If you are running a quest on your app where users become eligible to rewards after a while, it can be beneficial to create an encompassing campaign because your quest will be exposed to our user base. However, you probably don't need to go through the trouble of developing a real API endpoint for us because the data is almost static (only updated once, at the end of the quest). What we recommend is doing this via gists. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#data-json) Data JSON Here is an example data JSON using a gist: [https://gist.githubusercontent.com/BaptistG/576bd5711fded3f44d906efbcaff80e0/raw](https://gist.githubusercontent.com/BaptistG/576bd5711fded3f44d906efbcaff80e0/raw) **Important**: You need to give us a **raw** URL of a **public** gist. **Creating a public gist:** Go to [https://gist.github.com/](https://gist.github.com/) , fill in the data and select `Create public gist` **Getting the raw URL:** Once you create your gist, the URL of the page you are on should look something like this: `https://gist.github.com/BaptistG/576bd5711fded3f44d906efbcaff80e0`. To get the raw url, you need to add `/raw` at the end (`https://gist.github.com/BaptistG/576bd5711fded3f44d906efbcaff80e0/raw` with the previous example). ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#reward-json) Reward JSON You must first initialize the gist of the reward URL. You do this by creating a public gist of the following format: Here is an example if you want to airdrop [WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) on Ethereum: [https://gist.github.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3/42921f90a660bfb7db8b6f890adc725644ad1490](https://gist.github.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3/42921f90a660bfb7db8b6f890adc725644ad1490) You can now use the raw URL to initialize the campaign: `https://gist.githubusercontent.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3/raw/` (you will notice that this URL has data in there, this is because it is the latest version of the file) ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#create-the-campaign) Create the campaign You now have everything you need to create the campaign. You will need to choose the start and end dates of the campaign and commit the airdropped amount upfront. If you don't airdrop all the tokens by the end of the campaign, you will automatically get the undistributed tokens back #### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#generating-the-payload) Generating the payload The Merkl Studio does not yet support creating encompassing campaigns. We recommend calling the [encode batch](https://api.merkl.xyz/docs#tag/config/post/v4configencodebatch) route on the API to generate a Gnosis Safe payload for your campaign. The Gnosis Safe payload will be under the key `safePayload` ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#airdropping-the-rewards) Airdropping the rewards Before the end of the campaign you created, you need to update the rewards endpoint to tell Merkl to airdrop the rewards. To do so you should go to your gist, click edit and add data. For example, if I want to airdrop the following rewards of my token that has 18 decimals to 3 users where: * User 1 gets 1 token * User 2 gets 4 tokens * User 3 gets 6 tokens The gist should look like this: Here is an example for the WETH airdrop: [https://gist.github.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3](https://gist.github.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3) **Important:** * Your GitHub account has the power to update the gist, so if you get your account compromised, someone could airdrop themselves everything. * You must update this gist at least 12 hours before the scheduled end of the campaign. If you don't, our engine might not distribute the rewards and send you all the tokens back at the end of the campaign instead of running the airdrop * Don't forget that Merkl will take a small fee on the tokens you send, you can't distribute more than what is in our distributor, to verify the amount, go to the campaign page on our app. [PreviousLending and Borrowing Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing) [NextStaking & Restaking Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking) Last updated 18 days ago * [🛠️ Campaign configuration](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#campaign-configuration) * [1\. 🎁 Reward JSON](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#id-1.-reward-json) * [2\. 📊 Data JSON](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#id-2.-data-json) * [Validating your endpoints](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#validating-your-endpoints) * [⚠️ Important Note](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#important-note) * [⏳ Distribution lag](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#distribution-lag) * [Tutorial for static encompassing campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#tutorial-for-static-encompassing-campaigns) * [Data JSON](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#data-json) * [Reward JSON](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#reward-json) * [Create the campaign](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#create-the-campaign) * [Airdropping the rewards](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing#airdropping-the-rewards) Copy { "rewardToken": "0xE0688A2FE90d0f93F17f273235031062a210d691", "rewards": { "0x9f76a95AA7535bb0893cf88A146396e00ed21A12": { "epoch-1": { "amount": "40000000000000000000", "timestamp": "1732294694" } }, "0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185": { "epoch-2": { "amount": "100000000000000000000", "timestamp": "1741370722" } } } } Copy { "rewardToken": "0xE0688A2FE90d0f93F17f273235031062a210d691", "rewards": { "0x9f76a95AA7535bb0893cf88A146396e00ed21A12": { "epoch-1": { "amount": "40000000000000000000", "timestamp": "1732294694" // at this timestamp, `40000000000000000000` tokens will be claimable by the recipient }, "epoch-2": { // new reason for the same recipient "amount": "100000000000000000000", "timestamp": "1741370722" // at this timestamp, `100000000000000000000` additional tokens will be claimable by the recipient } }, "0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185": { "epoch-2": { "amount": "100000000000000000000", "timestamp": "1741370722" } } } } Copy type DataJSON = { tvl: string; apr: string; // in decimal opportunityName: string; // optional } Copy { "tvl": "100000", "apr": "0.5", // 0.5 in decimal = 50% APR "opportunityName": "Testing distribution aglaMerkl" } Copy import requests import json import re from typing import Dict, Any, Optional def is_number_regex(s): # Matches integers and decimals (including negative) return bool(re.match(r'^-?\d+(\.\d+)?$', s)) def validate_data_url(url: str) -> Optional[Dict[str, Any]]: """ Validate that the data URL returns the expected format: { "tvl": "100000", "apr": "0.5", "opportunityName": "Testing distribution aglaMerkl" } """ print(f"\n=== Validating Data URL ===") print(f"URL: {url}") try: response = requests.get(url) response.raise_for_status() data = response.json() # Check required fields required_fields = ["tvl", "apr", "opportunityName"] missing_fields = [field for field in required_fields if field not in data] if missing_fields: print(f"❌ INVALID: Missing required fields: {missing_fields}") return None # Validate types if not isinstance(data["tvl"], str): print(f"❌ INVALID: 'tvl' should be a string, got {type(data['tvl']).__name__}") return None if not isinstance(data["apr"], str): print(f"❌ INVALID: 'apr' should be a string, got {type(data['apr']).__name__}") return None if not isinstance(data["opportunityName"], str): print(f"❌ INVALID: 'opportunityName' should be a string, got {type(data['opportunityName']).__name__}") return None # Validate numeric formats if not is_number_regex(data["tvl"]): print(f"❌ INVALID: 'tvl' should be a numeric string, got '{data['tvl']}'") return None if not is_number_regex(data["apr"]): print(f"❌ INVALID: 'apr' should be a numeric string, got '{data['apr']}'") return None print("✅ VALID: Data URL format is correct") print(f" TVL: {data['tvl']}") print(f" APR: {data['apr']}") print(f" Opportunity Name: {data['opportunityName']}") return data except requests.RequestException as e: print(f"❌ ERROR: Failed to fetch data from URL: {e}") return None except json.JSONDecodeError as e: print(f"❌ ERROR: Invalid JSON response: {e}") return None def validate_reward_url(url: str) -> Optional[Dict[str, Any]]: """ Validate that the reward URL returns the expected format: { "rewardToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "rewards": { "0x37305B1cD40574E4C5Ce33f8e8306Be057fD7341": { "season1": { "amount": "1000000000000000000", "timestamp": "1741370722" } } } } """ print(f"\n=== Validating Reward URL ===") print(f"URL: {url}") try: response = requests.get(url) response.raise_for_status() data = response.json() # Check required top-level fields if "rewardToken" not in data: print(f"❌ INVALID: Missing 'rewardToken' field") return None if "rewards" not in data: print(f"❌ INVALID: Missing 'rewards' field") return None if not isinstance(data["rewardToken"], str): print(f"❌ INVALID: 'rewardToken' should be a string, got {type(data['rewardToken']).__name__}") return None if not isinstance(data["rewards"], dict): print(f"❌ INVALID: 'rewards' should be a dict, got {type(data['rewards']).__name__}") return None # Validate rewards structure for address, seasons in data["rewards"].items(): if not isinstance(seasons, dict): print(f"❌ INVALID: Rewards for {address} should be a dict, got {type(seasons).__name__}") return None for season_name, season_data in seasons.items(): if not isinstance(season_data, dict): print(f"❌ INVALID: Season data for {address}/{season_name} should be a dict") return None if "amount" not in season_data: print(f"❌ INVALID: Missing 'amount' in {address}/{season_name}") return None if "timestamp" not in season_data: print(f"❌ INVALID: Missing 'timestamp' in {address}/{season_name}") return None if not isinstance(season_data["amount"], str): print(f"❌ INVALID: 'amount' should be a string in {address}/{season_name}") return None if not isinstance(season_data["timestamp"], str): print(f"❌ INVALID: 'timestamp' should be a string in {address}/{season_name}") return None print("✅ VALID: Reward URL format is correct") print(f" Reward Token: {data['rewardToken']}") print(f" Total Addresses: {len(data['rewards'])}") return data except requests.RequestException as e: print(f"❌ ERROR: Failed to fetch data from URL: {e}") return None except json.JSONDecodeError as e: print(f"❌ ERROR: Invalid JSON response: {e}") return None def calculate_user_rewards(reward_data: Dict[str, Any], decimals: int = 18): """ Calculate and print the amount each user would receive (divided by 1e18) """ print(f"\n=== User Reward Amounts ===") if not reward_data or "rewards" not in reward_data: print("No reward data available") return total_amount = 0 user_totals = {} for address, seasons in reward_data["rewards"].items(): user_total = 0 for reason, season_data in seasons.items(): amount_wei = int(season_data["amount"]) amount_tokens = amount_wei / (10 ** decimals) user_total += amount_tokens user_totals[address] = user_total total_amount += user_total # Print sorted by amount (descending) sorted_users = sorted(user_totals.items(), key=lambda x: x[1], reverse=True) for address, amount in sorted_users: print(f"{address}: {amount:,.6f} tokens") print(f"\n{'='*60}") print(f"Total Amount Distributed: {total_amount:,.6f} tokens") print(f"{'='*60}") return total_amount def check_campaign_rewards(campaign_id: str, expected_total: float, decimals: int = 18): """ Check if the campaign has enough budget for the expected total rewards """ print(f"\n=== Checking Campaign Rewards ===") url = f"https://api.merkl.xyz/v4/campaigns/{campaign_id}" try: response = requests.get(url) response.raise_for_status() data = response.json() total_budget_wei = int(data.get("amount", "0")) total_budget_tokens = total_budget_wei / (10 ** decimals) print(f"Campaign ID: {campaign_id}") print(f"Total Budget: {total_budget_tokens:,.6f} tokens") print(f"Expected Total Rewards: {expected_total:,.6f} tokens") if total_budget_tokens >= expected_total: print("✅ The campaign has enough budget for the expected rewards.") else: print("❌ The campaign does NOT have enough budget for the expected rewards.") except requests.RequestException as e: print(f"❌ ERROR: Failed to fetch campaign metrics: {e}") except json.JSONDecodeError as e: print(f"❌ ERROR: Invalid JSON response: {e}") if __name__ == "__main__": # Example URLs - replace with actual URLs data_url = "https://gist.githubusercontent.com/BaptistG/576bd5711fded3f44d906efbcaff80e0/raw" reward_url = "https://gist.github.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3/raw" decimals = 18 # Adjust if reward token has different decimals campaign_id = "3141666711044140572" # Leave empty if campaign not created yet. If filled with the campaign DB ID, the script will check if the amounts match and if you have enough budget in the campaign. print("=" * 60) print("URL Format Validation Script") print("=" * 60) # Validate data URL data_result = validate_data_url(data_url) # Validate reward URL reward_result = validate_reward_url(reward_url) # If reward URL is valid, calculate user amounts if reward_result: total_amount = calculate_user_rewards(reward_result, decimals) # If campaign ID is provided, check campaign rewards if campaign_id and total_amount is not None and campaign_id.strip() != "": check_campaign_rewards(campaign_id, total_amount, decimals) print("\n" + "=" * 60) print("Validation Complete") print("=" * 60) Copy { "rewardToken": "
", "rewards": {} } Copy [\ {\ "distributionChainId": 1, \ "amount": "1000000000000000000",\ "computeChainId": 1,\ "creator": "0x0000000000000000000000000000000000000000",\ "startTimestamp": 1763985600,\ "rewardToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",\ "campaignType": 27,\ "endTimestamp": 1766613600,\ "dataUrl": "https://gist.github.com/BaptistG/576bd5711fded3f44d906efbcaff80e0/raw",\ "rewardsUrl": "https://gist.githubusercontent.com/BaptistG/e9bc9e9703a40cd6ad7e30d3e4e039a3/raw",\ "hooks": []\ }\ ] Copy { "rewardToken": "
", "rewards": { "
": { "season1": { "amount": "1000000000000000000", "timestamp": "1741370722" } }, "
": { "season1": { "amount": "4000000000000000000", "timestamp": "1741370722" } }, "
": { "season1": { "amount": "6000000000000000000", "timestamp": "1741370722" } } } } --- # Scoring Types | Merkl Docs [](https://docs.merkl.xyz/merkl-mechanisms/scoring#what-is-scoring) What is Scoring? ----------------------------------------------------------------------------------------- **Scoring types** define how individual user contributions are measured and converted into reward shares. While [distribution types](https://docs.merkl.xyz/merkl-mechanisms/distributions) determine the overall reward model and budget allocation, scoring types control how each user's activity is scored within that model. At its core, Merkl computes **time-weighted integrals** of user balances or contributions—essentially tracking how much liquidity or assets each user provided over time. [](https://docs.merkl.xyz/merkl-mechanisms/scoring#scoring-types-overview) Scoring Types Overview ------------------------------------------------------------------------------------------------------ Merkl supports various scoring methods, allowing campaign creators to choose how individual user contributions are measured. Each scoring type applies a different function to the time-weighted integrals, creating distinct reward allocation patterns. * **Default scoring (1:1 mapping)**: By default, Merkl uses a proportional scoring system where a user's share of rewards equals their share of the total time-weighted contributions. For example, if a user contributed 10% of the total liquidity-time across all participants, they receive 10% of the rewards (in a variable APR campaign). * **Custom scoring functions**: Campaign creators can apply different scoring functions to transform these integrals before calculating reward shares. Examples include: * **Maximum balance cap**: Taking the minimum between a user's time-weighted balance and a predefined cap—this limits how much eligible balance counts toward rewards, preventing excessive concentration. * **Logarithmic scoring**: Applying a logarithmic function to the time-weighted integral—this reduces the advantage of large contributors, creating a more equitable distribution where smaller participants receive proportionally higher rewards relative to their contribution size. [PreviousDistribution Types](https://docs.merkl.xyz/merkl-mechanisms/distributions) [NextReward Forwarding](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) Last updated 1 month ago * [What is Scoring?](https://docs.merkl.xyz/merkl-mechanisms/scoring#what-is-scoring) * [Scoring Types Overview](https://docs.merkl.xyz/merkl-mechanisms/scoring#scoring-types-overview) --- # Staking & Restaking Campaigns | Merkl Docs Merkl enables tracking and rewarding liquidity deposited in staking or restaking contracts—even if these contracts do not issue receipt tokens to LPs. Below are the custom campaign types currently supported. If you need support for a different contract type, please contact us. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking#staking-and-restaking) **Staking and Restaking** ----------------------------------------------------------------------------------------------------------------------- * **Symbiotic Vault**: Merkl can track Symbiotic Vaults, specifically monitoring the value of the underlying tokens deposited by users. This enables highly customizable reward campaigns, such as fixed or variable reward rates over time, points systems, or token distributions. * **Masterchef Vault**: Merkl supports tracking and rewarding liquidity in the most common Masterchef implementations. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking#locker) **Locker** ----------------------------------------------------------------------------------------- Locking is a core primitive in DeFi. Merkl can reward liquidity in locker contracts, allowing you to incentivize your most loyal users. Campaign creators can define custom reward curves or boosts, for example, based on the number of months a user locks their position. * **Puffer : Carrot Locker** To have your locker, staking, or restaking contract fully integrated and supported by Merkl, please [contact us on the Merkl Discord by opening a BD ticket](https://discord.com/invite/jnYfrGxDbe) to discuss the integration process. [PreviousEncompassing Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing) [NextDistribution Types](https://docs.merkl.xyz/merkl-mechanisms/distributions) Last updated 6 months ago * [Staking and Restaking](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking#staking-and-restaking) * [Locker](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking#locker) --- # Token Holding Campaigns | Merkl Docs Merkl enables the creation of token holding campaigns (also known as ERC20 campaigns), where **users earn rewards based on their token balance over time**. At their simplest, these campaigns function like traditional `StakingRewards` contracts. However, Merkl's **high customizability** and **advanced forwarding mechanisms** enable token holding campaigns to support far more flexible and complex use cases—going well beyond the limitations of standard staking systems. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#campaign-design) Campaign Design ---------------------------------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#distribution-type) Distribution Type In Token Holding Campaigns, users holding an incentivized ERC20 token in their wallet earn rewards based on their balance. How these rewards are allocated depends on the [distribution and scoring types](https://docs.merkl.xyz/merkl-mechanisms/distributions) and the [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) chosen by the campaign creator. For example, in a [variable reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#variable-reward-rate-campaigns) , rewards are distributed proportionally to each user's share of the total token supply. If a user holds 1% of the total supply over a given period, they are eligible to receive 1% of the rewards distributed during that time. [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#applications) Applications ---------------------------------------------------------------------------------------------------------- Token Holding Campaigns are highly versatile, supporting a wide range of DeFi use cases. Any protocol that issues receipt tokens when users interact with it is automatically supported by Merkl. This includes: * **Constant product AMMs**: Uniswap V2, SushiSwap, and similar liquidity pools * **Lending and borrowing protocols**: Platforms that emit receipt and debt tokens (ERC20 tokens) such as Aave—lenders can be rewarded based on their lending amount over time, and borrowers based on their borrowing amount * **Perpetual DEXes**: Perp protocols that issue receipt tokens to depositors in trading vaults Not all lending and borrowing protocols issue ERC20 receipt or debt tokens when users borrow from them. These protocols require a custom integration within Merkl. You can find more information about these specialized campaigns on the dedicated [lending & borrowing page](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing) . Additionally, some lending/borrowing protocols may require more tailored incentives (e.g., incentivizing lenders while blacklisting users who fold their liquidity). In such cases, a dedicated integration is recommended, similar to the custom integration implemented for Aave. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#merkl-api-integration) Merkl API Integration The logic for reward computation is independent from the API logic used to compute APRs and TVLs for the associated opportunity. For the protocol it integrates, the Merkl system can automatically recognize whether a given ERC20 token originates from a specific protocol, and then compute the associated TVL and APR if a campaign is running on it. If you are unsure whether the Merkl API will properly recognize your token as coming from your protocol before creating a campaign, please [contact us on the Merkl Discord by opening a BD ticket](https://discord.com/invite/jnYfrGxDbe) to discuss the integration process. [PreviousAirdrop Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop) [NextLending and Borrowing Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing) Last updated 1 month ago * [Campaign Design](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#campaign-design) * [Distribution Type](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#distribution-type) * [Applications](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#applications) * [Merkl API Integration](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/erc20-mechanisms#merkl-api-integration) --- # Additional Features | Merkl Docs Beyond campaign types, distribution methods, and customization options, Merkl offers several advanced features to enhance campaign flexibility and reward management. [](https://docs.merkl.xyz/merkl-mechanisms/features#blacklisting) ❌ Blacklisting ------------------------------------------------------------------------------------- Blacklisting excludes specific addresses from receiving rewards. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-d25f78bac99df7e4a235a432c6f040d3943df144%252FGroup%252027.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=8213dc37&sv=2) If a [forwarder](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) is blacklisted, all associated users are also ineligible. Example of a staking contract blacklist: * A user holds 10 USDA but has staked 6 USDA in a blacklisted staking contract (forwarder). * Only the 4 USDA in the user’s wallet qualifies for rewards. [](https://docs.merkl.xyz/merkl-mechanisms/features#whitelisting) ✅ Whitelisting ------------------------------------------------------------------------------------- Whitelisting restricts rewards to a specific address or set of addresses (e.g., forwarders, or individual users). Example of Uniswap V3 whitelisting: * A campaign incentivizes LPs in a Uniswap V3 pool with three forwarders — here Automated Liquidity Managers. * The campaign creator whitelists only two forwarders and one user address. * Result: * Only liquidity providers using the two approved forwarders or the whitelisted user will receive rewards * Rewards are distributed normally among whitelisted addresses based on liquidity share. **Whitelisting overrides blacklisting. If an address is whitelisted, all other addresses are automatically blacklisted.** If multiple campaigns run on the opportunity (e.g. Uniswap v4 ETH-USDC), some may have whitelists while others do not. This means that, for the same opportunity, the users receiving rewards can differ. Users should check the campaign details on the opportunity page to confirm eligibility requirements. [](https://docs.merkl.xyz/merkl-mechanisms/features#run-multiple-campaigns-on-the-same-opportunity) 🔥 Run Multiple Campaigns on the Same Opportunity ---------------------------------------------------------------------------------------------------------------------------------------------------------- Merkl allows multiple campaigns to be created simultaneously for the same pool or set of actions. **Why is this useful?** * Co-incentives: Different parties can independently add incentives to the same opportunity. * Flexible Stacking: Multiple campaigns can run alongside each other on the same pool or token, supporting different incentive structures. [](https://docs.merkl.xyz/merkl-mechanisms/features#campaigns-of-any-duration) ⏳ Campaigns of Any Duration --------------------------------------------------------------------------------------------------------------- Merkl campaigns can run for any length of time, from as short as one hour to as long as six months (or more). This flexibility allows for: * Short-term boosts (e.g., flash incentives for new launches). * Long-term, sustained incentive programs for ongoing ecosystem growth. [](https://docs.merkl.xyz/merkl-mechanisms/features#infinite-customizability-with-token-wrappers) 🎭 Infinite Customizability with Token Wrappers ------------------------------------------------------------------------------------------------------------------------------------------------------ Merkl campaigns can distribute tokens with custom properties, known as token wrappers, to introduce advanced incentive mechanisms. **Examples of Token Wrapper Use Cases:** * Vesting & Slashing Conditions: Add vesting schedules or penalties for early withdrawals. * Non-Prefunded Campaigns: Instead of preloading tokens, rewards are pulled from a multisig when users claim. * Time-Locked Transfers: Issue non-transferable tokens that unlock after a set period. * Redeemable Tokens: Distribute placeholder tokens that can be redeemed later. Merkl provides a suite of template contracts for token wrappers in the Merkl GitHub repository so anyone can build [its own token wrapper](https://github.com/AngleProtocol/merkl-contracts/tree/main/contracts/partners/tokenWrappers) . Some templates have already been audited by Merkl partners! Got a custom use case? Let us know—we’re happy to collaborate and help build your solution. [](https://docs.merkl.xyz/merkl-mechanisms/features#cross-chain-campaigns) 🌍 Cross-Chain Campaigns -------------------------------------------------------------------------------------------------------- Merkl allows you to incentivize activity on one chain while distributing rewards on another. **How It Works:** * Activity is tracked on Chain A (e.g., a protocol running on Arbitrum). * Rewards remain claimable on Chain B (e.g., distributed token stays on Ethereum): the chain where the token is claimable is the chain where the campaign was created **Why is this useful?** * Efficient token management: Keeps governance tokens on a single chain, reducing the need for bridging. * Cross-chain flexibility: Supports protocols that operate on multiple chains without fragmenting incentives. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-9272a2c28b7eda47637a3b922ee53364c81f3259%252FGroup%252011.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=b3ff6beb&sv=2) Rewards sent on a different chain than the one where users perform the action to be eligible **Important considerations**: Some smart contracts on the chain you are incentivizing activity may not exist on the chain where users can claim their reward (or may exist at a different address): * Affected addresses will be unable to claim rewards in this case * Solution: As a campaign manager, you should blacklist any ineligible addresses to prevent reward loss. Or you can reallocate rewards (more below) of addresses that cannot claim to an address controlled by the same provider that can claim its rewards [](https://docs.merkl.xyz/merkl-mechanisms/features#retroactive-campaigns) ◀️ Retroactive Campaigns -------------------------------------------------------------------------------------------------------- You can create campaigns in the past to reward OG users. It can start and end in the past or it can end in the future. [](https://docs.merkl.xyz/merkl-mechanisms/features#campaign-management) Campaign Management ------------------------------------------------------------------------------------------------- Campaign managers can perform several actions on their live and past campaigns, including: * [Overriding live campaigns](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#%EF%B8%8F-campaign-overrides) * [Cancelling existing campaigns](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#-campaign-cancellation) * [Reallocating unclaimed rewards from past campaigns](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#-campaign-reallocation) [PreviousCustomization Options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) [NextCampaign Configuration](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) Last updated 1 month ago * [❌ Blacklisting](https://docs.merkl.xyz/merkl-mechanisms/features#blacklisting) * [✅ Whitelisting](https://docs.merkl.xyz/merkl-mechanisms/features#whitelisting) * [🔥 Run Multiple Campaigns on the Same Opportunity](https://docs.merkl.xyz/merkl-mechanisms/features#run-multiple-campaigns-on-the-same-opportunity) * [⏳ Campaigns of Any Duration](https://docs.merkl.xyz/merkl-mechanisms/features#campaigns-of-any-duration) * [🎭 Infinite Customizability with Token Wrappers](https://docs.merkl.xyz/merkl-mechanisms/features#infinite-customizability-with-token-wrappers) * [🌍 Cross-Chain Campaigns](https://docs.merkl.xyz/merkl-mechanisms/features#cross-chain-campaigns) * [◀️ Retroactive Campaigns](https://docs.merkl.xyz/merkl-mechanisms/features#retroactive-campaigns) * [Campaign Management](https://docs.merkl.xyz/merkl-mechanisms/features#campaign-management) --- # Create a campaign from a multisig or Gnosis Safe | Merkl Docs The recommended method for creating campaigns with Merkl using a multisig is through the Gnosis Safe Transaction Builder. This guide walks you through generating and executing the payload to finalize your campaign creation. [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#id-1.-configure-your-campaign-and-get-your-payload) 1\. Configure Your Campaign and Get Your Payload ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- **For simple campaigns:** Follow [our guide](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) to use Merkl Studio to configure your campaign. Once your configuration is complete, download your payload by clicking on _Using_ ![token](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2Fraw.githubusercontent.com%2FAngleProtocol%2Fangle-token-list%2Fmain%2Fsrc%2Fassets%2Ftokens%2FSAFE.svg&width=40&dpr=4&quality=100&sign=520bbd40&sv=2) _SAFE wallet? Build a payload_ at the bottom of the last campaign creation step. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-1e7346f085e681436c6c855ec2b9a69cafba8704%252FGroup%252013.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=5cc4770b&sv=2) **For multiple or advanced campaigns:** If you're creating multiple campaigns at once or prefer a programmatic approach, refer to [this guide](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns) . [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#id-2.-upload-the-json-file) 2\. Upload the JSON File ------------------------------------------------------------------------------------------------------------------------------------------------------------- Once you have your JSON payload file, open the Gnosis Safe Transaction Builder and either drag and drop the file into the designated area, or click _Choose a file_ to upload it manually. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-b57f4f524291b7c96c8509e24840161732a229a1%252Fupload-json-file.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=131d6f79&sv=2) If you're using a different tool, proceed with your standard workflow. [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#id-3.-execute-the-transactions) 3\. Execute the Transactions --------------------------------------------------------------------------------------------------------------------------------------------------------------------- You will see the following 3 transactions: * `approve`: Approves the MerklDistributor contract to spend your reward tokens * `acceptConditions`: Accepts Merkl's Terms & Conditions. This is required the first time you create a campaign but can be removed from subsequent payloads. * `createCampaign`: Creates the campaign ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-86af96879748c24337fa78bc78979db1b2999296%252Fimage%2520%2840%29.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=5f17b30c&sv=2) Click _Create Batch_ and then _Send Batch_ to execute the transactions and deploy your Merkl campaign. Congratulations! You have successfully created your Merkl campaign. Campaigns typically appear in the Merkl app within approximately 1 hour. You can view the latest created campaigns in [this filtered view](https://app.merkl.xyz/?sort=lastCampaignCreatedAt-desc&tokenType=all) . [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#recommendations) Recommendations ----------------------------------------------------------------------------------------------------------------------------------------- **Optimize for Gas Fees:** You don't need to include `acceptConditions` for every campaign—including it only in your first campaign can help reduce gas fees. You can safely remove it from payloads generated by Merkl Studio or the Merkl API for subsequent campaigns. Similarly, for approval amounts: you can grant a large allowance once and then exclude the approval transaction from future payloads. **Handle Large Payloads:** On some chains, transaction payloads may be too large for the available block space, especially when creating multiple campaigns in a single payload. This occurs when the encoded campaign data exceeds the chain's transaction size limits. The limits vary by chain and depend on the number of campaigns and their complexity—some campaign types encode more data onchain than others. If your transaction fails due to payload size, you'll need to split it into multiple smaller payloads. Use our [Payload Splitter tool](https://splitter.merkl.xyz/) to automatically divide large payloads into executable batches. **Save for Future Use:** Save the transaction batch in your library to streamline the process and create new campaigns faster in the future. [PreviousCreate multiple campaigns](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns) [NextCreate your campaign from a DAO](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao) Last updated 10 days ago * [1\. Configure Your Campaign and Get Your Payload](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#id-1.-configure-your-campaign-and-get-your-payload) * [2\. Upload the JSON File](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#id-2.-upload-the-json-file) * [3\. Execute the Transactions](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#id-3.-execute-the-transactions) * [Recommendations](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe#recommendations) --- # Fee Model | Merkl Docs Merkl's fees are deducted directly from the incentives distributed by campaign creators. [](https://docs.merkl.xyz/distribute-with-merkl/fee-model#pay-as-you-go) Pay-as-You-Go ------------------------------------------------------------------------------------------- **Standard campaign fee**: 3% of distributed incentives **Airdrop discount**: JSON-based airdrops are charged at a reduced rate of 0.5% **Degressive fee structure**: The more you distribute (excluding airdrops), the lower your effective fee rate. This makes Merkl increasingly cost-efficient at scale. Fees are calculated based on the total amount distributed across all campaigns and addresses associated with your entity: * **3%** on the first $1M distributed * **2.25%** on amounts between $1M and $2.5M * **1.75%** on amounts between $2.5M and $5M * **1.5%** on amounts above $5M **Example**: If you distribute $3M in total, you'll pay 3% on the first $1M ($30k), 2.25% on the next $1.5M ($33.75k), and 1.75% on the final $500k ($8.75k), for a total fee of $72.5k (2.42% effective rate). [](https://docs.merkl.xyz/distribute-with-merkl/fee-model#commitment-model-for-high-volume-campaigns) Commitment Model (For High-Volume Campaigns) ------------------------------------------------------------------------------------------------------------------------------------------------------- For projects planning to distribute significant incentives, Merkl offers a 25% fee discount when you commit upfront to campaigns distributing more than $500k in total incentives. **How it works**: Pay your fees in advance for campaigns totaling at least $500k in distributions, and receive an automatic 25% reduction on those fees. Contact the Merkl team to discuss commitment model options and unlock volume discounts. [](https://docs.merkl.xyz/distribute-with-merkl/fee-model#unclaimed-rewards-policy) Unclaimed Rewards Policy ----------------------------------------------------------------------------------------------------------------- Campaign creators on Merkl can [freely reallocate](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-management) all unclaimed rewards once their campaign ends. One year after the campaign's end, if the campaign creators have not previously reallocated rewards and if there remain unclaimed rewards, Merkl reserves the right to reclaim them. [PreviousCampaign Management](https://docs.merkl.xyz/distribute-with-merkl/campaign-management) [NextEarn rewards - User guide](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl) Last updated 1 month ago * [Pay-as-You-Go](https://docs.merkl.xyz/distribute-with-merkl/fee-model#pay-as-you-go) * [Commitment Model (For High-Volume Campaigns)](https://docs.merkl.xyz/distribute-with-merkl/fee-model#commitment-model-for-high-volume-campaigns) * [Unclaimed Rewards Policy](https://docs.merkl.xyz/distribute-with-merkl/fee-model#unclaimed-rewards-policy) --- # Branding and Integration | Merkl Docs All Merkl branding assets for using Merkl as a white-label solution, along with communication materials, can be found [here](https://merkl.xyz/brand) . [PreviousDisplay Your Native APR](https://docs.merkl.xyz/integrate-merkl/nativeapr) [NextSmart Contract Addresses](https://docs.merkl.xyz/integrate-merkl/smart-contract-addresses) Last updated 8 months ago --- # Before you start | Merkl Docs [](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#merkls-core-principles) ⚙️ Merkl’s core principles ------------------------------------------------------------------------------------------------------------------------ Before setting up your campaign on Merkl, make sure you have read and understood: * [**Merkl’s reward distribution process**](https://docs.merkl.xyz/merkl-mechanisms/technical-overview) (reward computation vs. reward update, dispute period, etc.) * [**The different incentive mechanisms**](https://docs.merkl.xyz/merkl-mechanisms/incentive-mechanisms) (campaign types, distribution types, scoring types) * How to personalize your campaign using [**customization options**](https://docs.merkl.xyz/merkl-mechanisms/customization-options) * [**Merkl forwarding system**](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) * [What the **configuration** of a campaign is](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) * [**Additional features**](https://docs.merkl.xyz/merkl-mechanisms/features) that can make your campaign unique If you're looking for some incentive mechanism for which you're not sure about whether it's supported, or looking to add incentive features or customization options that are not displayed on the platform, please contact us! Some Merkl features (e.g., new distribution types, campaign types, customization options) may not be directly available in the campaign creation flow. If a required configuration option is not available in the frontend, make sure to check [our docs for advanced campaign creation](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns) . [](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#token-whitelisting) 📝 Token whitelisting --------------------------------------------------------------------------------------------------------------- Before starting your campaign, you need to ensure that the token you want to use as reward with Merkl has been whitelisted. We usually process token whitelisting requests once a day, and it's a way for us to ensure that we will be able to properly compute APRs and that the token will be safe to use for our users. **Campaigns must distribute at least ~$1/hour in total rewards to avoid dusting.** You can check whether your token is already whitelisted by setting it as the reward token in the [Merkl studio](https://studio.merkl.xyz/create-campaign/clamm) . If your token is not whitelisted, please submit the reward token’s price source and logo by filling out this [whitelisting form](https://anglemoney.notion.site/1aecfed0d48c808a8194fe2befd50bdb?pvs=105) . Make sure you have all the tokens you want to distribute in your wallet when creating a campaign [](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#campaign-launch-flow) Campaign launch flow ---------------------------------------------------------------------------------------------------------------- When creating your first campaign with Merkl, keep in mind that it may take up to **1 hour** for the campaign to become visible on the Merkl app. It's normal for TVLs and APRs to show as 0 when your campaign first appears. In most cases, the Merkl system dynamically fetches and updates these values every 10 minutes, but for complex campaigns, it may require an engine computation cycle (up to 2 hours) to accurately calculate and display TVLs, as described in the [technical overview](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#tvl) . It's also expected for **your campaign's leaderboard to appear empty right after launch**. The Merkl Engine needs time to perform [the necessary computations](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) before this data becomes available. The first rewards typically become claimable approximately 8 hours after campaign creation, though pending rewards should appear within 2 hours after they start. [](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#test-campaigns) 🧪 Test campaigns ------------------------------------------------------------------------------------------------------- You may want to start testing the flow and integrating our data before your point program starts. Merkl is not deployed on testnets, but you can still run test campaigns using our test token: **aglaMerkl**. These test campaigns are not mandatory — they're available to help you experiment with how Merkl works using valueless tokens, so you can better prepare your upcoming campaigns. However, since they consume computation resources on our end, we encourage you to use this feature only when there's a clear need. Note that test campaigns will **not appear on the Merkl app** and will not be visible to users, as they are automatically filtered out from the interface. To get started, send us a message and we'll provide you with our test token. aglaMerkl has no value and is only intended for creating test campaigns on Merkl. You can then create your test campaign using aglaMerkl tokens. While the campaign won't show up in the Merkl app, you'll be able to **check everything is working properly via the Merkl API**, using the campaign ID generated at creation. You can refer to the [API integration page](https://docs.merkl.xyz/integrate-merkl/app) for how to effectively use the API. Make sure to verify that: * distribution is functioning as expected * key metrics like TVL are correctly computed If everything looks good in the API, it will appear correctly in the Merkl front end once the real campaign is live! If you need help to create your first test campaigns, feel free to reach out to us. [](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#what-budget-should-i-set-for-my-campaign) 💰 What budget should I set for my campaign? ------------------------------------------------------------------------------------------------------------------------------------------------------------ The ideal campaign budget depends on your specific objectives and the distribution method you choose. Campaign budgets follow a natural supply-and-demand dynamic: liquidity providers want to maximize their rewards, while campaign creators aim to minimize costs. As a result, TVL typically scales proportionally with the reward budget you allocate. Depending on your campaign type, you need to account for fees differently (see our [fees section](https://docs.merkl.xyz/distribute-with-merkl/fee-model) for details). **Distribution methods matter**: If you're concerned about over-distributing rewards early in your campaign, consider using a [capped APR campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#-capped-reward-rate-campaigns) . This approach ensures you don't exhaust your budget too quickly if TVL starts low, as rewards are distributed at a fixed rate relative to TVL rather than depleting a fixed token amount. **Benchmarking APRs**: To gauge appropriate reward levels, you can: * Browse existing campaigns on the [Merkl app](https://app.merkl.xyz/) to see typical APRs in your category * Use Merkl's [APR simulation tool](https://docs.google.com/spreadsheets/d/1suPzoiFfZP3_XdXNu9qh63gNUbbXaWoKuhy17FRCEIc/edit?usp=sharing) to model how different reward budgets translate to APRs at various TVL levels. To use this tool, you may simply duplicate the sheet and input your own numbers to simulate. * Adjust your budget based on competitive rates and your growth targets **Start small, iterate often**: We recommend launching shorter campaigns initially (e.g., 2 weeks) to test performance and gather data. You can then create follow-up campaigns with optimized parameters based on actual TVL, user engagement, and APR effectiveness. This iterative approach minimizes risk and maximizes ROI. [](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#merkl-terms) 📃 Merkl terms ------------------------------------------------------------------------------------------------- You must also make sure that you have read and understood [Merkl's Terms & Conditions](https://app.merkl.xyz/terms) . You will be required to agree to these terms during the campaign setup process. [PreviousGlossary](https://docs.merkl.xyz/merkl-mechanisms/glossary) [NextCreate a campaign](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) Last updated 1 month ago * [⚙️ Merkl’s core principles](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#merkls-core-principles) * [📝 Token whitelisting](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#token-whitelisting) * [Campaign launch flow](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#campaign-launch-flow) * [🧪 Test campaigns](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#test-campaigns) * [💰 What budget should I set for my campaign?](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#what-budget-should-i-set-for-my-campaign) * [📃 Merkl terms](https://docs.merkl.xyz/distribute-with-merkl/before-you-start#merkl-terms) --- # Distribution Types | Merkl Docs **Distribution types** define the reward distribution model and how the total campaign budget is spent over time. They determine the economic structure of your campaign—whether you distribute a fixed budget proportionally, offer guaranteed APRs, or cap maximum returns. Merkl supports various distribution models, allowing campaign creators to choose the approach that best fits their incentive goals and budget constraints. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-323d3d61443232b47d87ec2dad478d530fd35529%252Fschema_distribution-type-radius-corner.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=1f803d58&sv=2) [](https://docs.merkl.xyz/merkl-mechanisms/distributions#variable-reward-rate-campaigns) 📈 Variable Reward Rate Campaigns ------------------------------------------------------------------------------------------------------------------------------- In variable reward rate campaigns, rewards are distributed proportionally based on time-weighted liquidity within the eligibility pool. This is the most classical form of campaign. ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#key-characteristics) Key Characteristics * Constant reward rate for campaign creators: A fixed amount of tokens is distributed per second, regardless of liquidity changes. * Variable APR for participants: As more users join, rewards per user decrease (like slicing a fixed-size pizza into more pieces). * Prefunded & fixed duration: The full campaign budget is deposited upfront, and the campaign runs for a set time. ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#example-liquidity-pool-incentives) Example: Liquidity Pool Incentives Imagine an incentive program targeting liquidity in a pool: * Day Start: Two LPs each contribute $100 (50%/50%). * Midday: A third LP adds $200 (total pool: $400). As a result: * The first two LPs each get 50 tokens for their contribution in the first half of the day, then 25 tokens each for the second half. * The third LP, who contributed $200 in the second half, receives 50% of the rewards for that period, which is 50 tokens. Result: Early participants earn more rewards before new LPs enter, while later participants dilute the total yield for others. ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#anti-dos-measures) Anti DoS Measures To prevent excessive computational load and spam, Merkl applies an anti-DoS filter to Variable APR campaigns. * Users earning less than 0.000001% of total campaign rewards per engine run are excluded. * This prevents micro-distributions that could disrupt system performance. Example: Uniswap V2 Liquidity Pool Campaign * Campaign Duration: 14 days * Total Value Locked (TVL): $1,000,000 (on Arbitrum) * Merkl Engine Run Frequency: Every 4 hours (6 times per day) For this campaign: * Each reward distribution releases 1/84th of the total rewards (1 / (14 days × 6 runs per day)). * If a user provides $10 of liquidity, they represent 1/100,000th (0.001%) of the total TVL ($10 / $1,000,000). * Per engine run, their potential reward would be 1/8,400,000th (0.000012%) of the total rewards (1/100,000 × 1/84). * Since this is above the 1/100,000,000th (0.000001%) threshold, the reward would be distributed. However, if the TVL increased to $20,000,000, the same user’s share would drop to 1/2,000,000th (0.00005%), and their per-run reward would be 1/16,800,000th (0.0000006%), below the threshold. As a result, they would not receive rewards. [](https://docs.merkl.xyz/merkl-mechanisms/distributions#fixed-reward-rate-campaigns) 🔒 Fixed Reward Rate Campaigns ------------------------------------------------------------------------------------------------------------------------- In fixed reward rate campaigns, users earn a predefined amount of rewards per unit of liquidity at a set rate. ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#key-characteristics-1) Key Characteristics * Constant reward rate per users: Users receive a fixed APR regardless of total liquidity. * Variable cost for campaign creators: As more users join, the overall cost increases, but individual earnings remain stable. * Campaign may finish earlier: Campaigns end when funds are depleted or when the scheduled duration expires. If a campaign runs out of funds before its end date, rewards are split proportionally (like a variable reward rate campaign) in the final run. If the campaign ends early, unused rewards are returned to the campaign creator. **There are two types of fixed reward rate campaigns, depending on whether the distributed amount is pegged to a fixed token amount or to a fixed dollar value.** * **Fixed token reward** * Users earn a fixed amount of tokens, regardless of token price variations * Users’ APR is correlated to token reward’s price change * Total rewards amount may or may not be fully spent, depending of user’s participation * **Fixed dollar reward** * Users earn a fixed amount of dollars (distributed in tokens) * APR in dollars is fixed for users (but the token amount may vary) * The amount of token distributed adjusts with token price variations (but the USD-denominated value is fixed) ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#example-fixed-reward-rate-campaign) Example: Fixed Reward Rate Campaign A campaign offers $1 in rewards per $100 in liquidity per day - which makes a 365% fixed APR. * If TVL = $100, the cost is $1 per day. * If TVL = $100 million, the cost for the campaign creator scales to $1M per day. You can also incentivize by the number of tokens held. Let's say that Bob wants to incentivize holding _LONG_ and he doesn't know how to price it. A campaign can offer $1 in rewards per 100 _LONG_ held per day. [](https://docs.merkl.xyz/merkl-mechanisms/distributions#capped-reward-rate-campaigns) 📉 Capped Reward Rate Campaigns --------------------------------------------------------------------------------------------------------------------------- Capped reward rate campaigns work like variable rate campaigns, but the campaign creator sets a maximum APR that cannot be exceeded, ensuring users are never overpaid. ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#key-characteristics-2) Key Characteristics * Rewards are fully distributed across the entire campaign duration. * APR adjusts according to TVL: the higher the TVL, the lower the APR. * APR cannot exceed the maximum set by the campaign creator. This prevents users from capturing excessive rewards, especially early in the campaign when TVL is low. ### [](https://docs.merkl.xyz/merkl-mechanisms/distributions#example-capped-reward-rate-campaign) Example: Capped Reward Rate Campaign A protocol launches a 4-week campaign with a total reward budget of 10,000 tokens to distribute. The APR is capped at 15%. At the start of the campaign, the TVL averages around $150,000. Based on this TVL, the APR would normally be 20%, which would distribute approximately 576 tokens per week. However, due to the 15% APR cap, the actual APR is limited to 15%, resulting in fewer tokens distributed per week than the uncapped rate. In week 3, TVL increases sharply to $500,000. As TVL rises, the APR adjusts downward accordingly, staying below the 15% cap. This ensures the campaign budget is distributed evenly and sustainably over the full 4 weeks. [PreviousStaking & Restaking Campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/staking) [NextScoring Types](https://docs.merkl.xyz/merkl-mechanisms/scoring) Last updated 1 month ago * [📈 Variable Reward Rate Campaigns](https://docs.merkl.xyz/merkl-mechanisms/distributions#variable-reward-rate-campaigns) * [Key Characteristics](https://docs.merkl.xyz/merkl-mechanisms/distributions#key-characteristics) * [Example: Liquidity Pool Incentives](https://docs.merkl.xyz/merkl-mechanisms/distributions#example-liquidity-pool-incentives) * [Anti DoS Measures](https://docs.merkl.xyz/merkl-mechanisms/distributions#anti-dos-measures) * [🔒 Fixed Reward Rate Campaigns](https://docs.merkl.xyz/merkl-mechanisms/distributions#fixed-reward-rate-campaigns) * [Key Characteristics](https://docs.merkl.xyz/merkl-mechanisms/distributions#key-characteristics-1) * [Example: Fixed Reward Rate Campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#example-fixed-reward-rate-campaign) * [📉 Capped Reward Rate Campaigns](https://docs.merkl.xyz/merkl-mechanisms/distributions#capped-reward-rate-campaigns) * [Key Characteristics](https://docs.merkl.xyz/merkl-mechanisms/distributions#key-characteristics-2) * [Example: Capped Reward Rate Campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#example-capped-reward-rate-campaign) --- # Glossary | Merkl Docs [](https://docs.merkl.xyz/merkl-mechanisms/glossary#apr) [APR](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#apr) ------------------------------------------------------------------------------------------------------------------------------------ [](https://docs.merkl.xyz/merkl-mechanisms/glossary#blacklist) [Blacklist](https://docs.merkl.xyz/merkl-mechanisms/features#blacklisting) ----------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#boost) [Boost](https://docs.merkl.xyz/merkl-mechanisms/customization-options#boosts) ---------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#campaign) [Campaign](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#opportunity-vs.-campaign) ------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#capped-reward-rate-campaign) [Capped reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#capped-reward-rate-campaigns) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#customization-option) [Customization option](https://docs.merkl.xyz/merkl-mechanisms/customization-options) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#cross-chain-rewards) [Cross-chain rewards](https://docs.merkl.xyz/merkl-mechanisms/features#cross-chain-campaigns) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#daily-rewards) [Daily rewards](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#daily-rewards) ------------------------------------------------------------------------------------------------------------------------------------------------------------------ [](https://docs.merkl.xyz/merkl-mechanisms/glossary#dashboard-merkl-app) [Dashboard (Merkl App)](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-app) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#dashboard-merkl-studio) [Dashboard (Merkl Studio)](https://docs.merkl.xyz/distribute-with-merkl/campaign-management) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [](https://docs.merkl.xyz/merkl-mechanisms/glossary#dispute) [Dispute](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#dispute-period-and-process) ------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#eligibility) [Eligibility](https://docs.merkl.xyz/merkl-mechanisms/customization-options#eligibility) --------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#encompassing-campaign) [Encompassing campaign](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/encompassing) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#fixed-reward-rate-campaign) [Fixed reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#fixed-reward-rate-campaigns) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#forwarder) [Forwarder](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) ------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#linked-opportunities) [Linked opportunities](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#linked-opportunities) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#merkl-app) [Merkl App](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-app) ------------------------------------------------------------------------------------------------------------------------------------------------------ [](https://docs.merkl.xyz/merkl-mechanisms/glossary#merkl-studio) [Merkl Studio](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-studio) --------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#net-lending-and-borrowing) [Net lending & borrowing](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/lending-borrowing#preventing-lending-loops) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#opportunity) [Opportunity](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#opportunity-vs.-campaign) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#point-program) [Point program](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program) ------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#pre-tge) [Pre-TGE](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign) --------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#referral) [Referral](https://docs.merkl.xyz/merkl-mechanisms/customization-options#referral-program) -------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#reward-compute) [Reward compute](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#reward-update) [Reward update](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) ------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#tvl) [TVL](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#tvl) ------------------------------------------------------------------------------------------------------------------------------------ [](https://docs.merkl.xyz/merkl-mechanisms/glossary#variable-reward-rate-campaign) [Variable reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/distributions#variable-reward-rate-campaigns) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [](https://docs.merkl.xyz/merkl-mechanisms/glossary#whitelist) [Whitelist](https://docs.merkl.xyz/merkl-mechanisms/features#whitelisting) ----------------------------------------------------------------------------------------------------------------------------------------------- [PreviousCampaign Configuration](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) [NextBefore you start](https://docs.merkl.xyz/distribute-with-merkl/before-you-start) Last updated 1 month ago * [APR](https://docs.merkl.xyz/merkl-mechanisms/glossary#apr) * [Blacklist](https://docs.merkl.xyz/merkl-mechanisms/glossary#blacklist) * [Boost](https://docs.merkl.xyz/merkl-mechanisms/glossary#boost) * [Campaign](https://docs.merkl.xyz/merkl-mechanisms/glossary#campaign) * [Capped reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/glossary#capped-reward-rate-campaign) * [Customization option](https://docs.merkl.xyz/merkl-mechanisms/glossary#customization-option) * [Cross-chain rewards](https://docs.merkl.xyz/merkl-mechanisms/glossary#cross-chain-rewards) * [Daily rewards](https://docs.merkl.xyz/merkl-mechanisms/glossary#daily-rewards) * [Dashboard (Merkl App)](https://docs.merkl.xyz/merkl-mechanisms/glossary#dashboard-merkl-app) * [Dashboard (Merkl Studio)](https://docs.merkl.xyz/merkl-mechanisms/glossary#dashboard-merkl-studio) * [Dispute](https://docs.merkl.xyz/merkl-mechanisms/glossary#dispute) * [Eligibility](https://docs.merkl.xyz/merkl-mechanisms/glossary#eligibility) * [Encompassing campaign](https://docs.merkl.xyz/merkl-mechanisms/glossary#encompassing-campaign) * [Fixed reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/glossary#fixed-reward-rate-campaign) * [Forwarder](https://docs.merkl.xyz/merkl-mechanisms/glossary#forwarder) * [Linked opportunities](https://docs.merkl.xyz/merkl-mechanisms/glossary#linked-opportunities) * [Merkl App](https://docs.merkl.xyz/merkl-mechanisms/glossary#merkl-app) * [Merkl Studio](https://docs.merkl.xyz/merkl-mechanisms/glossary#merkl-studio) * [Net lending & borrowing](https://docs.merkl.xyz/merkl-mechanisms/glossary#net-lending-and-borrowing) * [Opportunity](https://docs.merkl.xyz/merkl-mechanisms/glossary#opportunity) * [Point program](https://docs.merkl.xyz/merkl-mechanisms/glossary#point-program) * [Pre-TGE](https://docs.merkl.xyz/merkl-mechanisms/glossary#pre-tge) * [Referral](https://docs.merkl.xyz/merkl-mechanisms/glossary#referral) * [Reward compute](https://docs.merkl.xyz/merkl-mechanisms/glossary#reward-compute) * [Reward update](https://docs.merkl.xyz/merkl-mechanisms/glossary#reward-update) * [TVL](https://docs.merkl.xyz/merkl-mechanisms/glossary#tvl) * [Variable reward rate campaign](https://docs.merkl.xyz/merkl-mechanisms/glossary#variable-reward-rate-campaign) * [Whitelist](https://docs.merkl.xyz/merkl-mechanisms/glossary#whitelist) --- # Smart Contract Addresses | Merkl Docs All Merkl smart contract addresses, categorized by chain are listed [here](https://app.merkl.xyz/status) . On the vast majority of chains, the Distributor is deployed at `0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae` and the Campaign Creator is deployed at `0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd`. [PreviousBranding and Integration](https://docs.merkl.xyz/integrate-merkl/branding-and-integration) Last updated 6 months ago --- # Create a campaign | Merkl Docs Merkl Studio is your command center to launch, manage, and optimize incentive campaigns. This guide walks you through the process of creating a campaign step by step. Merkl Studio doesn't support all campaign types and customization options offered by Merkl. If you need features not available in the Studio or have questions, please reach out to us. [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#step-by-step-guide) Step-by-Step Guide ------------------------------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-1.-connect-your-wallet) 1\. Connect Your Wallet Connect the wallet holding the rewards you want to distribute. Ensure you're connected to the chain where you want to distribute rewards. To see all supported chains, check the [Status page](https://app.merkl.xyz/status) in the Merkl App. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-957e4ded0bb2edd1b3bc4a91afd80aeecc8a287a%252FGroup%25208.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=e591c9c7&sv=2) ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-a70487701100dcbfd9ca36d9937214e18015d3f8%252FConnectWallet.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=fdd9b798&sv=2) You can create cross-chain campaigns with Merkl—incentivize an asset on one chain while distributing rewards on another (e.g., incentivize a WETH-USDC pool on Ethereum by distributing WBTC on Base). ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-2.-click-create-campaign) 2\. Click Create Campaign Click the _Create Campaign_ button to begin setting up your campaign. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-9168a8d921c63126ff1fc0901d2ec5c29d722c25%252FGroup%25207.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=db31523d&sv=2) ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-3.-select-your-incentive-category) 3\. Select Your Incentive Category Choose your incentive category by clicking on the appropriate card. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-f2a3a8181d0d2f22355f82a948c141281ffe6091%252FIncentiveCategory.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=911c294c&sv=2) To incentivize Aave lending positions, Uniswap V2, or similar liquidity pools, use the _Token Holding_ campaign category. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-4.-configure-your-rewards) 4\. Configure Your Rewards Fill out campaign details including the asset to incentivize, the asset type (token or point), the reward amount to distribute, start date, end date, and more. The required details depend on the campaign type you selected. For detailed explanations of each parameter, see the [campaign types page](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) . ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-aee98cab33e4114141808d5a7331be0bf41d1a24%252FGroup%252018.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=27672cfe&sv=2) The total rewards amount represents the total to be distributed over the entire campaign duration (a maintenance fee may be applied on top). Merkl sets a minimum token amount that can be distributed per hour for each reward token. If your token amount is too low (typically less than $1 per hour), you will not be able to create your campaign. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-5.-set-restrictions) 5\. Set Restrictions This section allows you to configure blacklists and/or whitelists for your campaign. Blacklisted addresses won't receive any rewards, while whitelisted addresses will be the only addresses eligible for rewards (excluding all others). ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-d25f78bac99df7e4a235a432c6f040d3943df144%252FGroup%252027.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=8213dc37&sv=2) ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-6.-personalize-your-campaign) 6\. Personalize Your Campaign Campaigns on Merkl can be fully customized to your needs. You can apply customization options such as boosts and more. New options are constantly being added! Check out the [Customization options page](https://docs.merkl.xyz/merkl-mechanisms/customization-options) for a comprehensive list of available options. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-2130812bd1eef7ce5561b40ff64f63808ad416f4%252FGroup%252020.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=9fe2a909&sv=2) You may also be asked to provide a deposit URL—the link where users can interact with the asset you want to incentivize (e.g., a lending market, liquidity pool, etc.). Since Merkl is non-custodial, users must interact directly with protocols to be eligible for rewards. Merkl does not hold any user funds. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-7.-review-campaign-and-accept-terms) 7\. Review Campaign and Accept Terms Double-check all the information. Once you're ready, click the _Accept Terms_ button and confirm in your wallet. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-ebd77d4e2fac9f259737c07f453e4cb941976090%252FGroup%25209.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=4fb30bec&sv=2) ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-8.-approve-and-launch) 8\. Approve and Launch Approve the reward token transfer from your wallet to Merkl, then create the campaign by signing the transaction. You can sign and submit using either an EOA (externally owned account) or a multisig wallet. Regardless of the method you choose, you will need to complete these 3 steps: * Accept the Terms & Conditions * Approve the tokens for transfer * Deposit the tokens #### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#using-an-eoa) Using an EOA * Double-check your campaign configuration * Read and accept Merkl's Terms & Conditions by clicking the _Accept_ button and signing with your wallet * Approve the tokens for transfer and deposit the amount you want to distribute, plus [the standard Merkl fees](https://docs.merkl.xyz/distribute-with-merkl/fee-model) #### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#using-a-multisig-wallet-safe) Using a Multisig Wallet (Safe) The recommended method for distributing rewards with Merkl using a multisig is through the Gnosis Safe Transaction Builder. To learn how to deploy your campaign from a multisig, see [this guide](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe) . Once you've executed your transaction, your campaign may take up to **one hour** to become visible on the frontend after creation. [PreviousBefore you start](https://docs.merkl.xyz/distribute-with-merkl/before-you-start) [NextCreate multiple campaigns](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns) Last updated 1 month ago * [Step-by-Step Guide](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#step-by-step-guide) * [1\. Connect Your Wallet](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-1.-connect-your-wallet) * [2\. Click Create Campaign](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-2.-click-create-campaign) * [3\. Select Your Incentive Category](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-3.-select-your-incentive-category) * [4\. Configure Your Rewards](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-4.-configure-your-rewards) * [5\. Set Restrictions](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-5.-set-restrictions) * [6\. Personalize Your Campaign](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-6.-personalize-your-campaign) * [7\. Review Campaign and Accept Terms](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-7.-review-campaign-and-accept-terms) * [8\. Approve and Launch](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign#id-8.-approve-and-launch) --- # Display Your Native APR | Merkl Docs Merkl enables protocols to display their native APR directly on the Merkl frontend alongside Merkl incentive APRs. This integration provides users with a comprehensive view of total returns available on your protocol. [](https://docs.merkl.xyz/integrate-merkl/nativeapr#overview) Overview --------------------------------------------------------------------------- By integrating your native APR data, you can: * Display protocol-specific APRs (e.g., lending yields, staking rewards, pool fees) on Merkl's frontend * Help users understand the full return potential when combining native yields with Merkl incentives * Increase visibility and transparency for your protocol's offerings ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-0fea59dffa37ab626a0a5303c5bc20e32d54d901%252FnativeAPRScreen.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=3e938de9&sv=2) Native APR integration within Merkl frontend [](https://docs.merkl.xyz/integrate-merkl/nativeapr#implementation) Implementation --------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/integrate-merkl/nativeapr#endpoint-requirements) Endpoint Requirements To integrate your native APR data, you must create and host a public API endpoint that returns APR data in the following JSON format: ### [](https://docs.merkl.xyz/integrate-merkl/nativeapr#field-descriptions) Field Descriptions * **title**: A clear, descriptive name for the APR type (e.g., "Aave Supply APR", "Compound Borrow APR", "Uniswap V3 Fee APR") * **opportunityId**: The unique identifier for the Merkl opportunity. To identify the correct `opportunityId` values for your protocol's opportunities, you can query the Merkl API to retrieve opportunity Ids for the opportunities related to your protocol. * **timestamp**: When the APR value was last calculated or updated, in Unix timestamp format (seconds) * **value**: The APR value as a percentage. For example, use `5.5` to represent 5.5% APR * **description** (optional): Additional information about how the APR is calculated or what it represents ### [](https://docs.merkl.xyz/integrate-merkl/nativeapr#next-steps) Next Steps Once you have prepared your endpoint: 1. Test your endpoint to ensure it returns data in the correct format 2. Verify that all opportunity IDs are accurate 3. Contact the Merkl team to register your endpoint and complete the integration **Important**: All APR data must be accurate and verifiable. Providing misleading, incorrect, or manipulated data will result in immediate removal of your integration from the Merkl frontend. Ensure that: * APR calculations are accurate and reflect actual protocol performance * Timestamps are current and updated regularly * Values are consistently formatted as percentages If you have questions or need assistance, please reach out to the Merkl team. [PreviousIntegrate Merkl API](https://docs.merkl.xyz/integrate-merkl/app) [NextBranding and Integration](https://docs.merkl.xyz/integrate-merkl/branding-and-integration) Last updated 1 month ago * [Overview](https://docs.merkl.xyz/integrate-merkl/nativeapr#overview) * [Implementation](https://docs.merkl.xyz/integrate-merkl/nativeapr#implementation) * [Endpoint Requirements](https://docs.merkl.xyz/integrate-merkl/nativeapr#endpoint-requirements) * [Field Descriptions](https://docs.merkl.xyz/integrate-merkl/nativeapr#field-descriptions) * [Next Steps](https://docs.merkl.xyz/integrate-merkl/nativeapr#next-steps) Copy [\ {\ "title": "Protocol Name APR", // Required: String - Display name for the APR (e.g., "Aave Supply APR", "Uniswap Fee APR")\ "opportunityId": "123...abc", // Required: String - The Merkl Opportunity ID this APR corresponds to\ "timestamp": 1716900000, // Required: Number - Unix timestamp in seconds when the APR was calculated\ "value": 5.5, // Required: Number - The APR as a percentage (e.g., 5.5 for 5.5%)\ "description": "Optional details" // Optional: String - Additional context about the APR calculation\ },\ {\ "title": "Protocol Name APR",\ "opportunityId": "0x456...def",\ "timestamp": 1716900000,\ "value": 12.4\ }\ ] --- # Create a campaign pre-TGE | Merkl Docs Merkl enables you to distribute tokens before they become transferable at TGE. This approach combines the best of both worlds: users can claim actual tokens (not just points), while you maintain control over transferability until your TGE. **How this differs from standard campaigns**: The distributed token is initially non-transferable but becomes fully transferable at TGE. Users can claim their tokens through the Merkl UI and see them in their wallets—they just can't transfer them yet. **How this differs from points campaigns**: There's no need for a separate airdrop later. Tokens earned by users are real tokens that automatically convert 1:1 to transferable tokens at TGE. Users already hold the tokens; they simply gain transferability at launch. [](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign#how-it-works) How it works -------------------------------------------------------------------------------------------------------- 1. **Whitelist Merkl contracts**: Grant permission to Merkl's `Creator` and `Distributor` contracts so they can distribute your non-transferable tokens. 2. **Create your campaigns**: Use Merkl's standard campaign creation flow to define reward rates, multipliers, and which onchain actions to track. Campaigns can be [created programmatically](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) with simple scripts. 3. **Users claim tokens**: Participants interact onchain as usual and claim their non-transferable tokens directly through their Merkl dashboard. These tokens appear in their wallets immediately. [](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign#benefits) Benefits ------------------------------------------------------------------------------------------------ With this setup, you benefit from **all of Merkl's built-in features**: * **Public visibility**: Your campaigns appear in [the Merkl app](https://app.merkl.xyz/?tokenType=PRETGE&sort=tvl-desc) under the Pre-TGE section * **Leaderboards**: Users can track their rankings and performance * **APR displays**: Merkl can assign an estimated value to your tokens directly in the UI, showing users estimated APRs when farming. This estimate is typically based on your latest fundraising valuation or the agreed price with a CEX for an upcoming listing. * **Full customization**: Access to all of Merkl's [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) , including boosts, referral programs, and eligibility rules ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-21fb99fe42c40914e3ca06bd87582223dfb701db%252FGroup%252026.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=fb4ca890&sv=2) Pre-TGE token campaigns appear in the Merkl app under the Pre-TGE section This is the most straightforward way to leverage Merkl's full feature set while maintaining control over token transferability until your TGE. No separate airdrop needed—users already hold the tokens. [](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign#pricing) Pricing ---------------------------------------------------------------------------------------------- The pricing for pre-TGE campaigns is the [same as campaigns distributing transferable tokens with Merkl](https://docs.merkl.xyz/distribute-with-merkl/fee-model) . [PreviousCreate a point program](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program) [NextCampaign Management](https://docs.merkl.xyz/distribute-with-merkl/campaign-management) Last updated 2 months ago * [How it works](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign#how-it-works) * [Benefits](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign#benefits) * [Pricing](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign#pricing) --- # Create your campaign from a DAO | Merkl Docs You can create campaigns directly using onchain smart contracts with Merkl. This guide covers different methods for doing so. [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#prerequisites) Prerequisites --------------------------------------------------------------------------------------------------------------- All methods require generating the associated [campaign configuration](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) and data for your campaigns. To retrieve the necessary campaign data and parameters, check our guides for using [Merkl Studio](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) or [the Merkl APIs programmatically](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns) . In either case, the campaign data can be found in the campaign JSON payload you would receive when creating campaigns from a multisig. [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#from-an-onchain-gauge-system) From an Onchain Gauge System --------------------------------------------------------------------------------------------------------------------------------------------- Many protocols rely on gauge systems where users vote onchain to determine the allocation of governance token rewards. Merkl offers a set of plugins to facilitate the integration of Merkl campaign creation within such systems. These plugins combine Merkl's flexibility and reporting features with the advantages of a fully automated onchain setup. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#how-it-works) How It Works These plugins are smart contracts that expose the same interfaces as standard staking contracts for creating new campaigns (like `notifyReward`). When called, these contracts receive the reward tokens (via `transferFrom`) and create Merkl campaigns directly based on pre-set parameters. For these plugins to work, they must first be initialized with campaign data—such as which pool to incentivize, duration, blacklist/whitelist settings, and other parameters. Once set up, campaigns can be created automatically (e.g., weekly) by a bot with no trust assumptions—the process can even be made permissionless. Each week, based on DAO votes or the voting contract configuration, the plugin uses predefined parameters and updates the reward amount (based on voting results) and start timestamps when called. An example plugin can be found [here](https://github.com/AngleProtocol/merkl-contracts/blob/main/contracts/partners/middleman/MerklGaugeMiddlemanTemplate.sol) . Other teams have also developed different implementations. Check out these examples: * [Quickswap](https://polygonscan.com/address/0x3a381497813208508689d78c90EC9fb115D5640d#code) * [CrossCurve implementation](https://arbiscan.io/address/0xb665d0B69e91F596B9Dee3016e49136335993Fb8#readProxyContract) ### [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#step-by-step-guide) Step-by-Step Guide **1\. Update the Plugin Contract Interface** The plugin example assumes that the voting contract calls the gauge with the `notifyReward` function. Your voting contract may call associated gauges with different parameters. Ensure that the interface exposed by the plugin contract matches the one used by your other gauges. Your onchain governance may only support one reward token instead of multiple. You may need to update the plugin contract accordingly, as the Merkl template is designed to accommodate multiple reward tokens. Note: Depending on your voting contract setup, you can deploy one contract for all of your gauges. **2\. Define Your Campaign Parameters** Once your contract is deployed, pre-format your campaigns using the `setGaugeParameters` function ([reference](https://github.com/AngleProtocol/merkl-contracts/blob/32150c5577127a1feaf0315e1d7fcca0830eeb3c/contracts/partners/middleman/MerklGaugeMiddlemanTemplate.sol#L65) ). **3\. Automate Weekly (or Bi-weekly) Execution** Once your plugin is set up and your campaigns are correctly pre-formatted (a one-time setup), you only need to configure a mechanism within your voting contract to trigger execution automatically at your desired frequency. If you already have automation to push rewards to your other gauges, no additional setup is needed! [](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#from-a-one-off-governance-proposal) From a One-Off Governance Proposal --------------------------------------------------------------------------------------------------------------------------------------------------------- Some protocols may need to execute one-off governance votes through onchain systems to deploy campaigns (typically for airdrops). To successfully deploy your campaign through onchain governance, you'll need to create a campaign payload, then use the [Foundry Forge CLI](https://book.getfoundry.sh/forge/) to generate the calldata for a DAO proposal. Follow these steps to build the payload and finalize your campaign creation. The example below is for the _Arbitrum network_. Be sure to select [the correct address](https://docs.merkl.xyz/integrate-merkl/smart-contract-addresses) for the Creator contract on your network. **1\. Create the Approval Transaction** Approve the Creator contract to spend tokens. Remember to include the Merkl fee in your calculation. Then use the output to craft the proposal action: **2\. Create the Accept Merkl Terms Transaction** Accept the Merkl Terms of Service: Example result: **3\. Copy and Edit the Payload** Follow the guides mentioned above to generate and copy the campaign payload. Here is the function selector for reference: Then update the format to be compatible with the Foundry CLI: * Replace square brackets `[` with parentheses `(`\ \ * Remove quotation marks `"`\ \ * Remove newlines\ \ * Add single quotes `'` around the entire payload\ \ \ For example:\ \ **4\. Create the** `**createCampaign**` **Transaction**\ \ Run the cast command using the properly formatted payload:\ \ The third and final transaction will look like this:\ \ [PreviousCreate a campaign from a multisig or Gnosis Safe](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe)\ [NextCreate a point program](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program)\ \ Last updated 1 month ago\ \ * [Prerequisites](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#prerequisites)\ \ * [From an Onchain Gauge System](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#from-an-onchain-gauge-system)\ \ * [How It Works](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#how-it-works)\ \ * [Step-by-Step Guide](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#step-by-step-guide)\ \ * [From a One-Off Governance Proposal](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao#from-a-one-off-governance-proposal)\ \ \ Copy\ \ cast calldata "approve(address,uint256)" 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd 35175873467336683417086\ \ Copy\ \ {\ "target": "0x000D636bD52BFc1B3a699165Ef5aa340BEA8939c",\ "calldata": "0x095ea7b30000000000000000000000008bb4c975ff3c250e0ceea271728547f3802b36fd000000000000000000000000000000000000000000000772e34ed4c71327edfe"\ }\ \ Copy\ \ cast calldata "acceptConditions()"\ \ Copy\ \ {\ "target": "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd",\ "calldata": "0x63b87bf0"\ }\ \ Copy\ \ createCampaign(\ tuple(\ bytes32, // campaignId\ address, // creator\ address, // rewardToken\ uint256, // amount\ uint32, // campaignType\ uint32, // startTimestamp\ uint32, // duration\ bytes, // campaignData\ ),\ )\ \ Copy\ \ ;[\ '0x0000000000000000000000000000000000000000000000000000000000000000',\ '0x0000000000000000000000000000000000000000',\ '0x000D636bD52BFc1B3a699165Ef5aa340BEA8939c',\ '35175873467336683417086',\ 4,\ 1734307200,\ 3600,\ '0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d5558667658666274526576376d377750796a464d4c736a777568457a676e796a334653376172714b69446f33000000000000000000000000000000000000000000000000000000000000000000000000000000000000194f70656e20446f6c6c617220626f6c74732072657761726473000000000000000000000000000000000000000000000000000000000000000000000000000000',\ ]\ \ Copy\ \ '(0x0000000000000000000000000000000000000000000000000000000000000000,0x0000000000000000000000000000000000000000,0x000D636bD52BFc1B3a699165Ef5aa340BEA8939c,35175873467336683417086,4,1734307200,3600,0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d5558667658666274526576376d377750796a464d4c736a777568457a676e796a334653376172714b69446f33000000000000000000000000000000000000000000000000000000000000000000000000000000000000194f70656e20446f6c6c617220626f6c74732072657761726473000000000000000000000000000000000000000000000000000000000000000000000000000000)'\ \ Copy\ \ cast calldata "createCampaign(tuple(bytes32,address,address,uint256,uint32,uint32,uint32,bytes))" ''\ \ // Example\ cast calldata "createCampaign(tuple(bytes32,address,address,uint256,uint32,uint32,uint32,bytes))" '(0x0000000000000000000000000000000000000000000000000000000000000000,0x0000000000000000000000000000000000000000,0x000D636bD52BFc1B3a699165Ef5aa340BEA8939c,35175873467336683417086,4,1734307200,3600,0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d5558667658666274526576376d377750796a464d4c736a777568457a676e796a334653376172714b69446f33000000000000000000000000000000000000000000000000000000000000000000000000000000000000194f70656e20446f6c6c617220626f6c74732072657761726473000000000000000000000000000000000000000000000000000000000000000000000000000000)'\ \ Copy\ \ {\ "target": "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd",\ "calldata": "0xa63f05ad000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d636bd52bfc1b3a699165ef5aa340bea8939c000000000000000000000000000000000000000000000772e34ed4c71327edfe000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000675f6d800000000000000000000000000000000000000000000000000000000000000e1000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d5558667658666274526576376d377750796a464d4c736a777568457a676e796a334653376172714b69446f33000000000000000000000000000000000000000000000000000000000000000000000000000000000000194f70656e20446f6c6c617220626f6c74732072657761726473000000000000000000000000000000000000000000000000000000000000000000000000000000"\ } --- # Create multiple campaigns | Merkl Docs While [Merkl Studio](https://studio.merkl.xyz/) is ideal for creating standard campaigns, Merkl also provides tooling for campaign creators who need to create multiple (and potentially advanced) campaigns at once. This feature enables campaign creators to launch multiple campaigns sharing the same base parameters (creator address, reward token, reward chain, start and end time) in a single transaction. All batch campaign creation methods involve generating [campaign configurations](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) and their corresponding payloads. This guide walks you through configuration and payload generation. Once you have a payload, refer to [Create a Campaign from a Multisig or Gnosis Safe](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe) for execution instructions. [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#method-1-using-campaign-configurations-recommended-method) Method 1: Using Campaign Configurations (Recommended method) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ This method allows you to generate a campaign payload using full [campaign configurations](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) . ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-1.-retrieve-campaign-configurations) 1\. Retrieve Campaign Configurations Use this [endpoint](https://api.merkl.xyz/docs#tag/config/get/v4/config/{id}) to retrieve campaign configurations from existing campaigns. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-2.-adjust-parameters) 2\. Adjust Parameters Modify the parameters in the configurations according to your needs. Refer to the [campaign configuration documentation](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration) for details on available parameters. Before generating your payload, you can preview how your campaigns will appear using the [campaign preview endpoints](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#previewing-campaigns-before-deployment) to validate your configurations and catch potential issues early. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-3.-generate-the-payload) 3\. Generate the Payload Use [this endpoint](https://api.merkl.xyz/docs#tag/config/POST/v4/config/encode/batch/safe) to generate your Safe-compatible transaction payload. In the request body, provide your configurations as a JSON array: Copy [{\ "distributionChainId": 56,\ "campaignId": "0xcadb252f36c79aacbd6c96ce2af6cee374c2b0a0929ef6878525bee24987ed5e",\ "amount": "5000000000000000000000000",\ "computeChainId": 56,\ "creator": "0x67C06896Efb9Ce0A14C32B597Fa8bAa3f2659e7D",\ "startTimestamp": 1763636400,\ "rewardToken": "0xEdBeBe204Ef070B6880E07A28b55edc7748C24BA",\ "distributionMethodParameters": {\ "distributionMethod": "DUTCH_AUCTION",\ "distributionSettings": {}\ },\ "campaignType": 18,\ "endTimestamp": 1764068400,\ "blacklist": [],\ "whitelist": [],\ "forwarders": [],\ "targetToken": "0x5029f49585D57ed770D2194841B5A0bE06BFc2ED"\ }] For additional encoding options to create your campaigns onchain (direct transaction data, single campaign encoding, etc.), see the [encoding and decoding documentation](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#encoding-and-decoding-configurations) . [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#method-2-override-parameters-from-existing-template-campaigns) Method 2: Override Parameters from Existing Template Campaigns ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ This method uses **existing campaigns as templates**. Instead of specifying complete campaign configurations (as in Method 1), you only need to override specific parameters from template campaigns. Simply select existing campaigns that closely match your needs, modify the parameters you want to change, generate a payload, and execute it with a Safe. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-1.-retrieve-campaign-templates) 1\. Retrieve Campaign Templates Select existing campaigns that most closely match what you plan to launch. We strongly recommend matching both the [campaign type](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) and the [distribution type](https://docs.merkl.xyz/merkl-mechanisms/distributions) to minimize the number of parameters you need to modify and reduce the chance of errors. Next, find the `DatabaseId` of the template campaign(s) you'll use as the base for your new campaigns: 1. Go to the [Merkl app](https://app.merkl.xyz/) 2. Select an opportunity and open the "Advanced" tab 3. Copy the `DatabaseId` value ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-2.-prepare-the-campaign-payload) 2\. Prepare the Campaign Payload Use this [endpoint](https://api.merkl.xyz/docs#tag/campaigns/post/v4/campaigns/generate-payload) to generate your batch payload by providing your template `DatabaseId` value(s) (from Step 1) along with the parameters you want to override for each campaign. **Request body structure:** #### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#understanding-parameters) Understanding Parameters The endpoint uses two types of parameters: **1\. Base Parameters** (apply to all campaigns in the batch) These parameters are shared across all campaigns. If you need different values for some campaigns (e.g., different reward token), create a separate payload. * `creatorAddress`: The address creating the campaigns * `rewardToken`: The token used for rewards across all campaigns * `distributionChainId`: The chain ID where campaigns will run * `startTimestamp`: The start date of the campaigns (in Unix timestamp format) * `endTimestamp`: The end date of the campaigns (in Unix timestamp format) You can use [this converter](https://www.unixtimestamp.com/) to convert dates to Unix timestamps. **2\. Per-Campaign Parameters** (specific to each campaign) These parameters go under `campaignsParams` and let you customize each campaign. The available fields correspond to the [campaign-specific parameters](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#campaign-specific-parameters) in each campaign configuration. You can view all available parameters for a specific campaign using this [endpoint](https://api.merkl.xyz/docs#tag/config/get/v4/config/{id}) . Example: Campaign ID `13756496363331257690` - [https://api.merkl.xyz/v4/config/13756496363331257690](https://api.merkl.xyz/v4/config/13756496363331257690) For simple use cases where you only want to change the campaign amount, you'll only need to modify the `amount` field. #### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#example) Example You want to generate a payload for the following campaigns: * 2 Uniswap V3 campaigns (template `DatabaseId`: 9427880006586247706) * 1 Morpho (supply at the market level) campaign with a fixed rate of 5% APR (template `DatabaseId`: 6937583984928148176) * 1 Aave (supply side) campaign with a capped APR of 10% (template `DatabaseId`: 711211603263558496) When using the API endpoint, the body will look like this: [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#method-3-pre-set-campaign-keys-deprecated) Method 3: Pre-Set Campaign Keys (Deprecated) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- This method is deprecated. We recommend using Method 1 or Method 2 instead. This method requires the Merkl team to pre-define campaigns in the backend. You then generate a payload by specifying the amount, dates, and chain for each pre-defined campaign. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#setup) Setup To get started, provide: * The assets you'd like to incentivize * Any [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) you'd like to apply We'll save these configurations and generate the corresponding **keys** needed to launch your campaigns in bulk, which we'll share via a GitHub Gist. Once configured, you'll be able to create multiple campaigns at once, all sharing the same base parameters: * `program`: Provided by us—the internal ID of your incentive program * `creator`: The Safe address that will execute the campaign payload * `rewardToken`: In checksum format * `distributionChainId`: The chain where rewards will be distributed * `startTimestamp`: Campaign start time (Unix timestamp) * `endTimestamp`: Campaign end time (Unix timestamp) ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#payload-generation) Payload Generation Generate your campaign payloads using this [endpoint](https://api.merkl.xyz/docs#tag/programpayload/POST/v4/program-payload/program/withAmounts) . To use it: 1. Input the base parameters listed above. 2. In the request body, paste the JSON file with the **keys and placeholder amounts** we provided via GitHub Gist (see example below). 3. For each key: * Replace the placeholder amount with the number of tokens you want to allocate (in raw units). * Use the correct number of decimals (e.g., `5000000000000000000` for 5 tokens if the token has 18 decimals). * **If you do not plan to incentivize a specific key, remove it entirely from the JSON rather than setting the amount to** `**0**`**.** 4. Click Send. If the payload is successfully generated, you'll be able to download it. If there's an error, reach out to us and we'll help troubleshoot. 5. Download the generated payload ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#example-1) Example Let’s say you’re a chain and want to incentivize: * 5 Uniswap pools * 2 Euler vaults You would send us the addresses of the pools and vaults you want to incentivize. Once received, we’ll configure your setup and return the associated keys via a GitHub Gist. The Gist will follow this format: For example, it would look like this: The values(`100000000000000000000`) are placeholder values. When creating your campaigns, you’ll need to replace them with the actual amounts you want to allocate. _All amounts must be entered in raw format using the correct token decimals._ Then proceed with the steps outlined in the Payload Generation section above. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#considerations-before-generating-a-payload) Considerations Before Generating a Payload * **Minimum Rewards Threshold:** Each campaign must meet the minimum hourly token distribution (typically ≥ ~$1/hour). If a campaign in the payload falls below this threshold, the payload will not be generated and will return an error message. * **Duplicate Campaigns:** If you're reusing the same keys or configuration to increase rewards for an existing campaign, you’ll need to modify the `startTimestamp` or `endTimestamp` slightly (e.g., by 1 second) to avoid a duplicate campaign error. * **Payload Size Limitations:** If your payload is too large, Safe may fail to execute the transaction. In that case, split your campaigns into multiple smaller batches. Creating up to ~20 campaigns at once typically works fine. * **Decimal Precision:** Ensure the amounts you distribute match the token's decimals. For example, for an 18-decimal token, `1 token = 1000000000000000000`. [PreviousCreate a campaign](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) [NextCreate a campaign from a multisig or Gnosis Safe](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-a-multisig-or-gnosis-safe) Last updated 18 days ago * [Method 1: Using Campaign Configurations (Recommended method)](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#method-1-using-campaign-configurations-recommended-method) * [1\. Retrieve Campaign Configurations](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-1.-retrieve-campaign-configurations) * [2\. Adjust Parameters](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-2.-adjust-parameters) * [3\. Generate the Payload](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-3.-generate-the-payload) * [Method 2: Override Parameters from Existing Template Campaigns](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#method-2-override-parameters-from-existing-template-campaigns) * [1\. Retrieve Campaign Templates](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-1.-retrieve-campaign-templates) * [2\. Prepare the Campaign Payload](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#id-2.-prepare-the-campaign-payload) * [Method 3: Pre-Set Campaign Keys (Deprecated)](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#method-3-pre-set-campaign-keys-deprecated) * [Setup](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#setup) * [Payload Generation](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#payload-generation) * [Example](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#example-1) * [Considerations Before Generating a Payload](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns#considerations-before-generating-a-payload) Copy { "creatorAddress": "0x...", // address in checksum format "rewardToken": "0x...", // checksum format "distributionChainId": 1, // chain where rewards will be distributed "startTimestamp": 1756512000, // Unix seconds "endTimestamp": 1756598400, // Unix seconds "campaignsParams": { "9427880006586247706": [// <-- template DatabaseId from Step 1\ {\ "amount": "85372895000000000000000", // in decimals\ "targetToken": "0x...", // or poolAddress / poolId / market / evkAddress\ "blacklist": [] // addresses to exclude (checksum)\ // ... override more parameters\ }\ // ...add more campaigns for this same template DatabaseId if needed\ ] // Add more DatabaseId, each with its own array of campaigns. } } Copy { "creatorAddress": "0xfC6C8e98c381d2320A12822be01F45307266ff5d", "rewardToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "distributionChainId": 1, "startTimestamp": 1761951600, "endTimestamp": 1764543600, "campaignsParams": { "9427880006586247706": [\ {\ "amount": "10000000000000000000000",\ "poolAddress": "0x2A2C512beAA8eB15495726C235472D82EFFB7A6B",\ "weightToken0": 1500,\ "weightToken1": 1500,\ "weightFees": 7000,\ "blacklist": [],\ "forwarders": [],\ "hooks": []\ },\ {\ "amount": "10000000000000000000000",\ "poolAddress": "0x2A2C512beAA8eB15495726C235472D82EFFB7A6B",\ "weightToken0": 1500,\ "weightToken1": 1500,\ "weightFees": 3000,\ "blacklist": [],\ "forwarders": [],\ "hooks": []\ }\ ], "3011317640800818752": [\ {\ "amount": "10000000000000000000000",\ "distributionMethodParameters": {\ "distributionMethod": "FIX_APR",\ "distributionSettings": {\ "apr": "0.05",\ "market": "0xE3190143Eb552456F88464662f0c0C4aC67A77eB",\ "rewardTokenPricing": true,\ "targetTokenPricing": true,\ }\ }\ }\ ], "711211603263558496": [\ {\ "amount": "10000000000000000000000",\ "distributionMethodParameters": {\ "distributionMethod": "MAX_APR",\ "distributionSettings": {\ "apr": "0.1",\ "targetToken": "0xE3190143Eb552456F88464662f0c0C4aC67A77eB",\ "rewardTokenPricing": true,\ "targetTokenPricing": true,\ }\ }\ }\ ] } } Copy { "ProtocolName1 Asset_Incentivized_1 ProgramName Address": "100000000000000000000", "ProtocolName1 Asset_Incentivized_2 ProgramName Address": "100000000000000000000", "ProtocolName2 Asset_Incentivized_3 ProgramName Address": "100000000000000000000" } Copy { "Uniswap USDC/WETH ProgramName 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640": "100000000000000000000", "Uniswap WETH/USDT ProgramName 0xc7bbec68d12a0d1830360f8ec58fa599ba1b0e9b": "100000000000000000000", "Uniswap WBTC/WETH ProgramName 0x4585fe77225b41b697c938b018e2ac67ac5a20c0": "100000000000000000000", "Uniswap wstETH/WETH ProgramName 0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa": "100000000000000000000", "Uniswap WBTC/cbBTC ProgramName 0xe8f7c89c5efa061e340f2d2f206ec78fd8f7e124": "100000000000000000000", "Euler Supply WETH ProgramName 0xD8b27CF359b7D15710a5BE299AF6e7Bf904984C2": "100000000000000000000", "Euler Borrow USDC ProgramName 0xE62055e3f732AB523FB57dDfAbA873a19C8A2CF8": "100000000000000000000" } --- # Merkl User FAQ | Merkl Docs [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#whats-the-frequency-of-rewards-distribution) What’s the frequency of rewards distribution? -------------------------------------------------------------------------------------------------------------------------------------------------- The rewards distribution frequency varies by chain. On average, rewards are distributed approximately every 8 hours. Note that the reward update schedule differs from the reward computation schedule. New rewards based on activity are computed and appear as pending approximately every 2 hours on each chain, while distribution updates occur less frequently. When pending rewards (updated every 2 hours) are pushed onchain (every 8 hours), they effectively become claimable. You can find the last reward updates for each chain on the [Status page](https://app.merkl.xyz/status) . To learn more about reward updates vs. reward computation, refer to [this page](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-updates) . [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#i-participated-in-a-merkl-campaign-but-i-cant-claim-rewards-on-merkl.-why) I participated in a Merkl campaign but I can't claim rewards on Merkl. Why? -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- If you're unable to claim rewards on Merkl after participating in a campaign, here are a few possible reasons: **Campaign Status**: The campaign may not have started yet. Check its status — if it's marked as "Upcoming," it hasn't started. Only "Live" campaigns are currently active, while completed ones are labeled "Past." **Processing Delay**: There may be a delay in the campaign computation process. To check if there's a delay, refer to the status page or navigate to the campaign section on the opportunity page. In the advanced section, you can see when the campaign was last processed. If there's a delay in the Merkl engine run for a campaign, you'll receive any missed rewards during the next engine run once the issue is resolved. Since the Merkl engine runs multiple times per day, you won't have to wait long for missed rewards. For example, if a campaign didn't distribute rewards today but runs the following day after the issue is fixed, you'll receive both the missed and current rewards. One common reason for delays is the absence of swaps in the pool. Swaps are necessary to calculate certain rewards, so if no swap occurs, the campaign will be delayed until the next engine run after a swap takes place. Learn more about rewards delays within Merkl [on this page](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) . [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#i-performed-an-action-providing-liquidity-lending-borrowing-...-on-one-chain-but-rewards-are-distrib) I performed an action (providing liquidity, lending, borrowing, …) on one chain, but rewards are distributed on another, is that normal? ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Yes, the chain where you perform an action may differ from the one where rewards are distributed. For example, you might provide liquidity in a pool on the Base network and receive rewards on Optimism. This setup is defined by the incentivizers when they create the reward campaign. You can find the chain where rewards are distributed in the campaign details on the opportunity page. Learn more about cross-chain campaigns [on this page](https://github.com/AngleProtocol/merkl-docs/blob/main/merkl-mechanisms/features/README.md#-cross-chain-campaigns) . [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#my-wallet-address-has-been-blacklisted.-why) My wallet address has been blacklisted. Why? ------------------------------------------------------------------------------------------------------------------------------------------------- Merkl automatically blacklists addresses that display suspicious behavior, such as wash trading to artificially inflate rewards. This typically involves addresses trading in pools where they hold positions specifically to maximize rewards. If you believe you were blacklisted by mistake, please open a support ticket with your wallet address and an explanation of your activity. Our team will review your case. Learn more about blacklists in [the case of concentrated liquidity campaigns](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/concentrated-liquidity-mechanisms#-fake-volume-attacks-detection) [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#an-opportunity-im-interested-in-is-marked-as-linked-to-other-opportunities-in-the-merkl-app.-what-do) An opportunity I'm interested in is marked as linked to other opportunities in the Merkl App. What does this mean? -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- When an opportunity is marked as linked to other opportunities, it means your deposits flow through multiple layers of contracts. For instance, a vault in which you supply assets (child opportunity) may allocate part of its funds to a liquidity pool (parent opportunity) that earns rewards from Merkl campaigns. Thanks to Merkl forwarders, you receive rewards from the parent opportunity (in which you indirectly participate), in addition to the rewards from the child opportunity itself. This setup can sometimes boost your returns, as participating in a child opportunity may combine rewards from both parent and child opportunities. Learn more about reward forwarding and linked opportunities [on this page](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#linked-opportunities) . [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#how-are-aprs-calculated) How are APRs calculated? --------------------------------------------------------------------------------------------------------- The main APR displayed is calculated as: Daily Rewards×365Adjusted TVL\*\\frac{\\text{Daily Rewards} \\times 365}{\\text{Adjusted TVL\*}}Adjusted TVL\*Daily Rewards×365​ * When there are no specific conditions or eligibility restrictions, **adjusted TVL = TVL** * When there are some (e.g, net lending, whitelist and/or blacklist hooks, or eligibility thresholds), **adjusted TVL = Net TVL** [Learn more about Merkl APRs, TVLs and daily rewards](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#key-merkl-metrics) [](https://docs.merkl.xyz/earn-with-merkl/faq-earn#what-do-amount-pending-and-claimed-mean-in-the-ui) What do "Amount", "Pending", and "Claimed" mean in the UI? --------------------------------------------------------------------------------------------------------------------------------------------------------------------- * **Amount**: The total number of tokens already credited to the user onchain * **Pending**: Rewards that are queued and will be pushed onchain in the next update * **Claimable**: Rewards that have been computed and are ready to be claimed by users * **Claimed**: The amount the user has already claimed Dive deeper into the different fields in the UI by referring to [the API docs](https://docs.merkl.xyz/integrate-merkl/app#integrating-user-rewards) . [PreviousEarn rewards - User guide](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl) [NextIntegrate Merkl API](https://docs.merkl.xyz/integrate-merkl/app) Last updated 1 month ago * [What’s the frequency of rewards distribution?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#whats-the-frequency-of-rewards-distribution) * [I participated in a Merkl campaign but I can't claim rewards on Merkl. Why?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#i-participated-in-a-merkl-campaign-but-i-cant-claim-rewards-on-merkl.-why) * [I performed an action (providing liquidity, lending, borrowing, …) on one chain, but rewards are distributed on another, is that normal?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#i-performed-an-action-providing-liquidity-lending-borrowing-...-on-one-chain-but-rewards-are-distrib) * [My wallet address has been blacklisted. Why?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#my-wallet-address-has-been-blacklisted.-why) * [An opportunity I'm interested in is marked as linked to other opportunities in the Merkl App. What does this mean?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#an-opportunity-im-interested-in-is-marked-as-linked-to-other-opportunities-in-the-merkl-app.-what-do) * [How are APRs calculated?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#how-are-aprs-calculated) * [What do "Amount", "Pending", and "Claimed" mean in the UI?](https://docs.merkl.xyz/earn-with-merkl/faq-earn#what-do-amount-pending-and-claimed-mean-in-the-ui) --- # Campaign Management | Merkl Docs As a campaign manager, you have full control over your campaigns and can perform several actions on your live campaign. The Merkl Studio is where campaign managers can monitor and manage their campaigns. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-98cd93b677cbc0f1138d1792005b425be9768913%252FDashboardView.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=9206ab13&sv=2) Dashboard view in Merkl Studio The Studio also helps you monitor campaign performance and track key metrics like APR and TVL. For advanced analytics and programmatic access, use the [Merkl API](https://docs.merkl.xyz/integrate-merkl/app) . [](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-cancellation) 🛑 Campaign Cancellation ------------------------------------------------------------------------------------------------------------------------ You can cancel live campaigns and recover undistributed rewards at no cost. To cancel a campaign, open [Merkl Studio](https://studio.merkl.xyz/users/) and sign in with the manager address. If you only need the cancellation transaction payload (e.g., for a Safe), you may impersonate the manager address to fetch it. **Step 1:** From the Opportunities list, open the opportunity that contains your campaign. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-dae4ecf32a76dc2407f4b2768a0ea2dd3dd2c355%252FGroup-cancellation-2.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=2c85faa2&sv=2) **Step 2:** Select the campaign and click Cancel on the right. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-789769d957a534bd9aa2e20fb1e8b6bb710fff35%252FGroup-cancellation.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=638e379b&sv=2) **Step 3:** Confirm the action by typing STOP when prompted. You can then execute the cancellation transaction. If you prefer to execute via a Safe, download the payload and drag it into the Safe Transaction Builder. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-f7bdeea558071e6a1fa9f4df1e39436d5f2192cf%252FGroup-cancellation-3.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=f90d2219&sv=2) Once canceled, the campaign stops accruing rewards immediately. Undistributed tokens become withdrawable within **24 hours**—either from the Merkl UI or directly by calling the contract. After cancellation, the campaign will appear as **"Invalid"** in the Studio dashboard. This is expected behavior—the Merkl engine processes cancellations by overriding the campaign to an invalid campaign type, which triggers the return of undistributed rewards to the admin address of the campaign. Cancellation is **irreversible**. The engine will not process further updates for this campaign, and previously processed distributions remain final. [](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-overrides) ✏️ Campaign Overrides ------------------------------------------------------------------------------------------------------------------ Merkl allows campaign managers to modify active campaigns by adding customization options, such as: * Adjusting blacklists or whitelists to include or exclude specific addresses * Enabling additional features as needed * Changing start and end dates if they haven't passed yet ❌ What You Cannot Do: * Change the reward token * Change the end date to a date in the past * Change the total reward amount * To **reduce** the total amount: Cancel and recreate a new campaign. If you want identical dates, **offset** either the start or end by **at least 15 minutes** to avoid campaign duplication in the Merkl engine. * To **increase** the total amount: Create an **additional campaign** running in parallel. In the Merkl app, opportunities with **2+ lightning icons** indicate multiple active campaigns. **Adding extra rewards or new reward tokens** To add extra rewards or a new reward token to an existing campaign, create an additional campaign on top of the current one. The new campaign must have a slightly modified start or end date (minimum one hour difference) to prevent campaign duplication in our engine. [](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-reallocation) 🔄 Campaign Reallocation ------------------------------------------------------------------------------------------------------------------------ As a campaign manager, you can reallocate unclaimed rewards from any recipient address to another address at your discretion. This gives you full control over reward distribution, especially when certain addresses cannot claim their rewards. Once a campaign has ended, you can reallocate unclaimed rewards from specific recipients or reallocate all unclaimed rewards at once. To give users time to claim their rewards, reallocation is only available after a defined window following the campaign's end. Currently, this window ranges from 1 day to 1 year post-campaign. When reallocation is triggered, the process can take up to 24 hours. This delay is due to required security checks and the need to publish a Merkle root onchain—an essential step for making the reallocated rewards claimable. Since Merkle root updates are not continuous or real-time, this contributes to the processing time. Once the reallocation is complete and the Merkle root is updated on the relevant chain, the reallocated rewards become claimable just like any other Merkl reward, using the address to which they were reassigned. Claiming is not yet available in the studio app, campaign managers must go to the [app dashboard](https://app.merkl.xyz/users/) to claim their reallocated rewards. ### [](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#use-cases) Use Cases * Smart contracts without claim functions that need rewards redirected to claimable addresses * Lost wallets or inaccessible addresses * One-time partner facilitation (redirecting from contract addresses to partner wallets) Campaign managers can perform reallocations either through the [Merkl Studio](https://studio.merkl.xyz/users/) interface or programmatically via the smart contract. **How to Reallocate Rewards:** Call the `reallocateCampaignRewards` function on the Campaign Creator contract: **Parameters:** * `_campaignId`: The campaign ID for reallocation * `froms`: Array of addresses to reallocate from (use `["0x0000000000000000000000000000000000000000"]` to reallocate all unclaimed rewards) * `to`: The destination address **Example Transaction Payload** (for use with Safe Transaction Builder): When reallocating from the same address across multiple campaigns, create multiple transactions in your Safe batch—one for each campaign ID: [Download example payload file](https://github.com/AngleProtocol/merkl-docs/blob/main/.gitbook/assets/reallocation-payload-example.json) This example shows how to batch reallocations from the same source address to the same destination address across multiple campaigns. Add more transaction objects to the `transactions` array for additional campaigns. **Important Considerations:** * Reallocation can take up to 24 hours to complete due to security checks and processing * **No fees are charged for reallocation operations** * Each campaign must be reallocated separately (but can be batched in a single Safe transaction) [PreviousCreate a campaign pre-TGE](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign) [NextFee Model](https://docs.merkl.xyz/distribute-with-merkl/fee-model) Last updated 25 days ago * [🛑 Campaign Cancellation](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-cancellation) * [✏️ Campaign Overrides](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-overrides) * [🔄 Campaign Reallocation](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#campaign-reallocation) * [Use Cases](https://docs.merkl.xyz/distribute-with-merkl/campaign-management#use-cases) Copy function reallocateCampaignRewards( bytes32 _campaignId, address[] memory froms, address to ) --- # Reward Forwarding | Merkl Docs Merkl Engine intelligently enables users to **earn rewards even when they don't hold the incentivized asset directly in their wallet**. For example, in a campaign rewarding token holders, many users may have their tokens staked in a contract. If Merkl has integrated that staking contract, those users will still receive rewards based on their staked amount. This process of allocating rewards to the end users rather than to the contract itself is called forwarding, and the smart contract holding incentivized asset on behalf of users is referred to in this case as a **a Merkl forwarder**. [](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#how-it-works) How It Works -------------------------------------------------------------------------------------------- In most [campaign types](https://github.com/AngleProtocol/merkl-docs/blob/main/mechanisms/hooks/mechanisms/README.md) , Merkl automatically detects and applies any integrated forwarding logic. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-03a29ca2d4811ee2224171f0774b90f5288f5b9b%252FForwarderScan.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=c99f7c01&sv=2) The forwarder scan in Merkl Studio Check whether a contract is recognized as a forwarder by Merkl at https://forwarders.merkl.xyz/ **Key characteristics:** * **Complexity Varies**: While some simple forwarders, such as staking contracts for ERC20 tokens, have straightforward integration and forwarding processes, others are equivalent to a complex protocol integration and involve forwarding rewards across multiple stakeholders on several smart contract layers. * **Auto-Detection**: Once a forwarder is integrated, it's automatically applied—no manual configuration is needed. The Merkl frontend includes a scan tool to check if an address matches any known forwarder patterns. * **Protocol Fidelity**: Merkl mirrors the logic of each protocol. For example, if a protocol charges a fee on accrued rewards, Merkl will automatically account for and replicate that fee in the reward forwarding process. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-acc8614b8ed0d7d909e275388ccbd954354a2db1%252FDocs-merkl-forwarders.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=41621ae3&sv=2) [](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#examples) Examples ------------------------------------------------------------------------------------ **Example 1: Staked Token Rewards**: * A campaign incentivizes USDA holders. * Users who staked USDA and received stUSD would normally be ineligible for rewards because they don't hold USDA directly in their wallet. * As forwarding is automatically enabled, Merkl recognizes stUSD holders as indirect USDA holders and distributes rewards accordingly. **Example 2: Morpho Rewards**: * A campaign targets USDA holders. * USDA is used across multiple Morpho markets, either as collateral or loan token. * Merkl detects the Morpho singleton as a forwarder and automatically distributes the USDA rewards among relevant stakeholders across all markets, proportionate to their contribution to the singleton's USDA holdings. **Example 3: Pendle Rewards**: * A campaign rewards sUSDe holders. * Merkl rewards sUSDe "spot" holders and forwards rewards only to eligible Pendle stakeholders (YT and LP token holders), excluding PT holders, while applying the standard Pendle treasury fee. [](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#linked-opportunities) Linked Opportunities ------------------------------------------------------------------------------------------------------------ When Merkl detects an address as a forwarder, it automatically creates a **subcampaign** that can be tracked within the associated opportunity. This creates what we call on the app **linked opportunities**—DeFi opportunities connected by liquidity flows, where one opportunity (the child) deposits funds into another (the parent) and receives rewards from it. For example, a vault managing user funds (child opportunity) may deposit liquidity into a Uniswap pool (parent opportunity) that has active Merkl campaigns. When Merkl detects the vault as a forwarder, it creates a subcampaign visible on the vault's opportunity page. Thanks to this reward forwarding system, participants in the child opportunity—who are indirectly participating in the parent opportunity—receive rewards from the parent campaigns, in addition to any direct Merkl rewards from campaigns on the child opportunity itself. Linked opportunities are displayed in the Merkl App to help users understand the full reward potential of their deposits and make informed decisions about where to allocate liquidity. **Important note about subcampaign/linked opportunity visibility:** Subcampaigns are only created after the Merkl engine has completed its first computation cycle on the main campaign. This means that immediately after a campaign is created, the API cannot report any subcampaigns or linked opportunities, even though the engine will process them once the first compute runs. For example, if you renew a campaign, the new campaign will initially show no APR on linked opportunities right after it starts, even though the previous campaign displayed this information. The linked opportunities and their APRs will appear once the first engine computation completes. ### [](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#example-of-linked-opportunities) Example of linked opportunities In the below example, the main campaign incentivizes USDC supply across all whitelisted Morpho markets on Ethereum. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-369ba7b7d61733a6ffe85972428f7a247bc826f8%252Flinked-opportunities-example.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=16ddea1a&sv=2) Each child campaign has its own APR, determined by the vault’s allocation to the whitelisted Morpho markets. You can click on any child opportunity to view the APR associated with this child campaign, and check whether other campaigns are stacked on top of this opportunity. In the screenshot below, you can see the Prime Vault opportunity page, which displays the APR from the USDC Morpho campaign. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-4cc10877ca4f2051d8e77a5e0c359e10017b2efc%252Fchild-campaign-example.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=4bd44ef1&sv=2) A child campaign can also have its own children. In this example, because the Prime Vault sources a part of its liquidity from Karpatkey Morpho Vault V2, the Merkl Engine creates an additional child campaign for that flow. [](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#enabling-forwarding) Enabling Forwarding ---------------------------------------------------------------------------------------------------------- Forwarding is enabled by default for most campaign types on Merkl. If your campaign requires integration with a new forwarder or support for a different protocol, please contact our team. To ensure efficient distribution, Merkl enforces a minimum distribution threshold for each reward token. Campaigns can only be created if the token amount meets or exceeds this threshold. The same rule applies to forwarding: rewards are only forwarded if they pass the threshold. Forwarding will not be enabled for an address if the total rewards over a given period fall below the minimum per-hour threshold for that token. For example, if an ERC20 vault receives just $0.01 of rewards in a day and the token's threshold is $0.10 per hour, those rewards will not be forwarded. Instead, they'll remain accrued at the vault address. [](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#address-remapping) Address Remapping ------------------------------------------------------------------------------------------------------ If you're earning rewards through a smart contract that cannot claim them (e.g., non-upgradeable contracts on Uniswap V4 that cannot call `toggleOperator` or transfer), you may need **address remapping** to redirect your rewards to a claimable address. **Remapping vs. Forwarding:** While both mechanisms redirect rewards, they serve different purposes: * **Forwarding** (described above) automatically distributes rewards to users who hold the incentivized asset indirectly through integrated protocols (e.g., staking contracts or LP tokens). Forwarding works at the protocol level and is integrated directly into Merkl's reward distribution logic. * **Address remapping** is a manual configuration set up by the Merkl team that redirects rewards from one specific address (your contract) to another specific address (your claimable wallet). It's used when a contract receives rewards but cannot claim them, and you want those rewards sent to a claimable address for the entire campaign duration. **How to request address remapping:** Contact the Merkl team via [Discord](https://discord.com/channels/1209830388726243369/1210212731047776357) with: * Your campaign ID(s) * The source address (contract) from which rewards should be redirected * The destination address (claimable wallet) where rewards should be sent Address remapping is particularly useful for long-running campaigns where you expect regular reward accumulation on addresses that cannot claim. Rather than performing frequent manual reallocations, remapping provides a seamless, automated solution. [PreviousScoring Types](https://docs.merkl.xyz/merkl-mechanisms/scoring) [NextCustomization Options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) Last updated 1 month ago * [How It Works](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#how-it-works) * [Examples](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#examples) * [Linked Opportunities](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#linked-opportunities) * [Example of linked opportunities](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#example-of-linked-opportunities) * [Enabling Forwarding](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#enabling-forwarding) * [Address Remapping](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#address-remapping) --- # Earn rewards - User guide | Merkl Docs **Before your start** Make sure you understand: * The difference between an [opportunity and a campaign](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#opportunity-vs.-campaign) * Key terms: [daily rewards, APR, and TVL](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#main-metrics) * How the [Merkl App](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#merkl-app) is structured All yield opportunities from protocols and chains distributing rewards through Merkl are available on the [Merkl App](https://app.merkl.xyz/) . ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-9e376d790dbfd19742ebf623f692a115ec81d4f2%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-09-17%2520%25C3%25A0%252010.51.55%25202.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=98c6d386&sv=2) [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#how-to-get-started) How to get started -------------------------------------------------------------------------------------------------------- #### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#id-1.-explore-opportunities) 1\. Explore opportunities Visit the [Merkl App](https://app.merkl.xyz/) and browse the available opportunities to find the one that suits you best. You can filter by chain, protocol, category, status, and more for an easy overview. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-7cb32de818404abce7e8df81fb227be8af14678b%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-11-18%2520%25C3%25A0%252017.55.17%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=4f91286d&sv=2) #### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#id-2.-check-campaign-details) 2\. Check campaign details Select an opportunity and review campaign details such as APR, TVL, daily rewards, end date, and distribution strategy. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-6bb3da106820a2864b6db703841752f5e2c6622e%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-11-18%2520%25C3%25A0%252017.56.35%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=26253cd&sv=2) Make sure to check the **eligibility rules** (e.g., blacklist, health factor) to confirm you qualify for rewards. Do not get lured by high APRs. Rewards may be limited to whitelisted addresses or mostly go to large positions. #### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#id-3.-interact-with-the-protocol) 3\. Interact with the protocol Use the CTA button on the opportunity page to interact directly with the incentivized protocol (e.g., supply, lend, borrow). Always do your own research (DYOR) before investing and ensure you trust the protocol you’re interacting with. **Merkl is non-custodial**. No funds are held in Merkl smart contracts. You interact directly with the protocols, while Merkl tracks your onchain activity and distributes rewards accordingly. The main risks when using Merkl come from the underlying protocols, which may have smart contract vulnerabilities or operational issues that could put your funds at risk. Merkl is not responsible for any issues arising from incentivized protocols running campaigns on the platform. #### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#id-4.-start-earning-rewards) 4\. Start earning rewards Once you’ve interacted with the protocol, you’ll start earning rewards automatically. No staking or additional onchain actions are required. #### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#id-5.-claim-your-rewards) 5\. Claim your rewards Visit your [Merkl dashboard](https://app.merkl.xyz/users/) to track and claim your rewards. Ensure you are connected with the same wallet used to interact with the protocol. Make sure to claim all your rewards within a year of receiving them! ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-d5329f3ac947626233e6f6c52779775aaf228088%252FCapture%2520d%25E2%2580%2599%25C3%25A9cran%25202025-11-12%2520%25C3%25A0%252015.14.56%25201.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=5a1ea242&sv=2) Rewards usually show up in your dashboard around 8 hours after you interact with the protocol, giving Merkl time to calculate and distribute them. Check the [Status page](https://app.merkl.xyz/status) for details on reward distribution per chain. You can claim all your rewards per chain at once to optimize gas costs! [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#advanced-features) Advanced features ------------------------------------------------------------------------------------------------------ Rewards on Merkl do not increase block by block, but can be claimed at a frequency which depends on the chain. You can check the claim frequency on the [Status page](https://app.merkl.xyz/status) . Note that, by default, rewards can only be claimed by the address that earned them. You can however approve an operator to claim on your behalf by calling the function `toggleOperator` on the [distributor smart contract](https://app.merkl.xyz/status) . However, rewards will still be sent to the original address that earned them. So to sum up, assuming Alice earned the rewards: * by default only Alice can claim and rewards are sent to Alice. * by calling `toggleOperator`, Alice can allow Bob to claim on her behalf. Then, Bob can claim for Alice by sending Alice's proof to the contract, and rewards are then sent to Alice. If you can't call `toggleOperator` and are stuck, please [open a tech ticket in our Discord](https://discord.com/channels/1209830388726243369/1210212731047776357) , the team may be able to call it on your behalf. ### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#claiming-from-a-multisig) Claiming from a multisig When claiming rewards from a multisig address, we recommend delegating the claim process to an operator. This is because Merkl reward proofs are only valid for on average four hours (varies depending on the chain). If you fail to gather the necessary signatures and execute the claim transaction before a Merkle root is updated, your claim transaction will fail. ### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#claiming-as-an-operator) Claiming as an operator Currently, when you're connected to the Merkl frontend with an operator address, you only see rewards earned by that operator address—not by the addresses that have appointed it. To claim rewards on behalf of another address using an operator, you'll need to bypass the frontend and use the Merkl API directly. For example, you can retrieve the Merkle proof for a specific user and chain by calling: `https://api.merkl.xyz/v4/users/0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701/rewards?chainId=1` This response will include the Merkle proof data needed to submit a claim. You can then use this information to call the claim function on the [Merkl Distributor contract](https://docs.merkl.xyz/integrate-merkl/smart-contract-addresses) via a block explorer or directly onchain. To structure the claim: * `users`: Provide the address(es) you're claiming on behalf of. If claiming for multiple tokens, repeat the user address for each token. * `tokens`: List the token addresses you're claiming. * `amounts`: Use the amount field from the Merkl API response for each token. * `proofs`: Include the Merkle proof array for each token, also retrieved from the API. ### [](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#address-remapping) Address Remapping If a smart contract you use can’t claim rewards, ask the Merkl team to remap those rewards to a claimable wallet by reaching out on [Discord](https://discord.com/channels/1209830388726243369/1210212731047776357) with the campaign ID, source address, and destination address. The full walkthrough lives in the [Reward Forwarding guide](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding#address-remapping) . [PreviousFee Model](https://docs.merkl.xyz/distribute-with-merkl/fee-model) [NextMerkl User FAQ](https://docs.merkl.xyz/earn-with-merkl/faq-earn) Last updated 1 month ago * [How to get started](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#how-to-get-started) * [Advanced features](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#advanced-features) * [Claiming from a multisig](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#claiming-from-a-multisig) * [Claiming as an operator](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#claiming-as-an-operator) * [Address Remapping](https://docs.merkl.xyz/earn-with-merkl/earning-with-merkl#address-remapping) --- # Campaign Configuration | Merkl Docs A **campaign configuration** is the collection of all parameters—such as [campaign type](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) , [distribution method](https://docs.merkl.xyz/merkl-mechanisms/distributions) , and [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) —that define how a campaign operates. It serves as the source of truth for the Merkl engine. When a campaign is launched, an encoded version of this configuration is stored onchain in the Merkl Distribution Creator contract. The Merkl engine continuously monitors this contract to detect new campaigns, then parses each configuration to understand among others: * What type of user activity to track (e.g., providing liquidity, holding tokens, lending assets) * How to calculate user scores based on their participation * How rewards should be distributed over time * Which addresses are eligible or excluded from rewards This configuration-driven approach allows Merkl to support diverse incentive mechanisms while maintaining a unified reward computation and distribution infrastructure. [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#creating-and-retrieving-configurations) Creating and Retrieving Configurations ---------------------------------------------------------------------------------------------------------------------------------------------------- You can generate or retrieve campaign configurations in two main ways: * **Via Merkl Studio**: When you create a campaign using [Merkl Studio](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) , the configuration is generated automatically. * **Via Merkl API**: You can programmatically generate or retrieve configurations using the [Merkl API](https://docs.merkl.xyz/distribute-with-merkl/create-multiple-campaigns) . You can also retrieve the configuration of any existing campaign using its database ID via this [Merkl API endpoint](https://api.merkl.xyz/docs#tag/config/get/v4/config/{id}) . [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#configuration-structure) Configuration Structure ---------------------------------------------------------------------------------------------------------------------- A campaign configuration is typically represented as a JSON object containing various parameters. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#common-parameters) Common Parameters These parameters are standard across most campaigns: * `creator`: The address managing the campaign * `rewardToken`: The address of the token distributed as rewards * `distributionChainId`: The chain ID where rewards are distributed * `computeChainId`: The chain ID where user activity is tracked (can differ from `distributionChainId` for cross-chain campaigns) * `startTimestamp`: The start date of the campaign (Unix timestamp) * `endTimestamp`: The end date of the campaign (Unix timestamp) * `amount`: The total amount of rewards to be distributed * `blacklist`: A list of addresses excluded from receiving rewards * `whitelist`: A list of addresses allowed to receive rewards (if set, all others are excluded) * `campaignType`: The [campaign type](https://docs.merkl.xyz/merkl-mechanisms/campaign-types) * `computeScoreParameters`: The [scoring method](https://docs.merkl.xyz/merkl-mechanisms/scoring) used by the campaign * `distributionMethodParameters`: The [distribution method](https://docs.merkl.xyz/merkl-mechanisms/distributions) for reward distribution You can use [this converter](https://www.unixtimestamp.com/) to convert dates to Unix timestamps. ### [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#distribution-methods) Distribution Methods The `distributionMethodParameters` field defines how rewards are distributed over time. **Variable Reward Rate (Dutch Auction):** **Fixed APR:** **Capped APR:** #### [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#understanding-distribution-settings-parameters) Understanding Distribution Settings Parameters **rewardTokenPricing**: * `false`: The campaign distributes a fixed amount of reward tokens per unit (or dollar) of liquidity provided * `true`: The campaign distributes a fixed dollar value of rewards per unit (or dollar) of liquidity provided **targetTokenPricing**: * `false`: Rewards are calculated per unit of liquidity provided * `true`: Rewards are calculated per dollar of liquidity provided We recommend using `targetTokenPricing = true` for most campaigns to ensure consistent reward distribution regardless of liquidity value fluctuations. When both `rewardTokenPricing = true` and `targetTokenPricing = true`, the campaign pays a fixed APR. For most campaign types (such as token holding campaigns), the `targetToken` address is automatically derived from the campaign configuration and doesn't need to be specified manually within the `distributionSettings`. #### [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#understanding-the-apr-value-format) Understanding the APR Value Format The `apr` value in `distributionSettings` represents different metrics depending on your pricing configuration: **Fixed APR** (`targetTokenPricing = true` and `rewardTokenPricing = true`): * The value represents the target APR as a decimal * Example: `"apr": "0.01"` = 1% APR, `"apr": "0.08"` = 8% APR **Token-per-Dollar Rate** (`targetTokenPricing = true` and `rewardTokenPricing = false`): * The value represents the number of reward tokens earned per dollar of liquidity per year * Example: `"apr": "1"` = 1 reward token per year per $1 of liquidity provided * For $1,000 in liquidity: ~2.74 tokens per day (1,000 ÷ 365) * To reward 1 token per day per $1,000 provided, use: `"apr": "0.365"` * Calculation: 1 token/day × 365 days = 365 tokens/year ÷ 1,000 dollars = 0.365 tokens per year per dollar ### [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#campaign-specific-parameters) Campaign-Specific Parameters Each campaign type has its own set of specific parameters in addition to the common parameters listed above. **Examples by Campaign Type:** * **ERC20 / Token Holding (Type 18)** - [Example config](https://api.merkl.xyz/v4/config/8270489034958466914) * `targetToken`: Address of the token to incentivize (or LP token for V2 pools) * **Uniswap V3 (Type 2)** - [Example config](https://api.merkl.xyz/v4/config/9427880006586247706) * `poolAddress`: The pool address * `weightToken0`, `weightToken1`, `weightFees`: Weights for scoring liquidity * **Uniswap V4 (Type 13)** - [Example config](https://api.merkl.xyz/v4/config/14050222419773482936) * Specific hook and pool parameters * **Morpho (Type 57)** - [Example config](https://api.merkl.xyz/v4/config/3011317640800818752) * `targetToken`: Address of the supplied token on a Morpho Market * **Euler Supply (Type 12)** - [Example config](https://api.merkl.xyz/v4/config/16912425279432080078) * `evkAddress`: Address of the incentivized vault * **Euler Borrow (Type 12)** - [Example config](https://api.merkl.xyz/v4/config/17331543524323336682) * `evkAddress`: Address of the incentivized vault [](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#encoding-and-decoding-configurations) Encoding and Decoding Configurations ------------------------------------------------------------------------------------------------------------------------------------------------ The Merkl API provides endpoints to convert between campaign configurations and the encoded format used onchain. **Encoding configurations for onchain campaign creation:** These endpoints convert campaign configurations into transaction data for calling the Merkl Distribution Creator contract: * [Encode single campaign from config](https://api.merkl.xyz/docs#tag/config/POST/v4/config/encode) - Generate transaction data for one campaign * [Encode multiple campaigns from their respective configs](https://api.merkl.xyz/docs#tag/config/POST/v4/config/encode/batch) - Generate transaction data for multiple campaigns If you're deploying campaigns from a Gnosis Safe, use these endpoints instead to get Safe-compatible transaction payloads: * [Get Safe payload from a single campaign config](https://api.merkl.xyz/docs#tag/config/POST/v4/config/encode/safe) - Generate Safe transaction payload for one campaign * [Get Safe payload from multiple campaigns config](https://api.merkl.xyz/docs#tag/config/POST/v4/config/encode/batch/safe) - Generate Safe transaction payload for multiple campaigns **Decoding onchain data into configurations:** These endpoints convert various types of onchain and encoded data back into readable configuration objects: * [Decode from onchain campaign ID](https://api.merkl.xyz/docs#tag/config/GET/v4/config/decode/onchain/{distributionChainId}/{campaignId}) - Retrieve configuration for an existing campaign * [Decode from Database ID](https://api.merkl.xyz/docs#tag/config/GET/v4/config/decode/onchain/{distributionChainId}/{campaignId}) - Retrieve configuration for an existing campaign using its `DatabaseId` * [Decode from raw onchain campaign data](https://api.merkl.xyz/docs#tag/config/POST/v4/config/decode/{distributionChainId}) - Parse encoded campaign data * [Decode from Gnosis Safe payload](https://api.merkl.xyz/docs#tag/config/POST/v4/config/decode/safe) - Extract configuration from Safe transaction payload * [Decode from transaction data](https://api.merkl.xyz/docs#tag/config/GET/v4/config/decode/{distributionChainId}/{payload}) - Parse campaign creation transaction data **Previewing campaigns before deployment:** These endpoints allow you to simulate how your campaign will appear and estimate its metrics before deploying it onchain: * [Preview opportunity details](https://api.merkl.xyz/docs#tag/config/post/v4configopportunity) - See how your campaign will be categorized and displayed (opportunity name, grouping, etc.) * [Estimate TVL](https://api.merkl.xyz/docs#tag/config/post/v4configtvl) - Calculate the expected Total Value Locked for your campaign configuration [PreviousAdditional Features](https://docs.merkl.xyz/merkl-mechanisms/features) [NextGlossary](https://docs.merkl.xyz/merkl-mechanisms/glossary) Last updated 18 days ago * [Creating and Retrieving Configurations](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#creating-and-retrieving-configurations) * [Configuration Structure](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#configuration-structure) * [Common Parameters](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#common-parameters) * [Distribution Methods](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#distribution-methods) * [Campaign-Specific Parameters](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#campaign-specific-parameters) * [Encoding and Decoding Configurations](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#encoding-and-decoding-configurations) Copy { "distributionMethod": "DUTCH_AUCTION" } Copy { "distributionMethod": "FIX_APR", "distributionSettings": { "apr": "0.08", "targetToken": "0x...", "rewardTokenPricing": true, "targetTokenPricing": true } } Copy { "distributionMethod": "MAX_APR", "distributionSettings": { "apr": "1", "targetToken": "0x...", "rewardTokenPricing": true, "targetTokenPricing": true } } --- # Create a point program | Merkl Docs Merkl serves as a powerful indexing and computation engine for points programs, tracking onchain activity and calculating time-weighted user contributions. However, **Merkl does not mint or allocate points directly**—it provides the raw data and calculations that you can use however you see fit. Think of Merkl as a flexible indexing tool: you define the logic you want to track (such as liquidity provision, token holdings, or protocol usage), Merkl monitors and computes user actions across the chains and protocols you select, and you then consume these results in whatever way best suits your program—whether that's crediting points 1:1, applying custom multipliers, filtering specific users, or combining data from multiple campaigns. **You maintain full control over your points system.** Merkl simply provides the underlying tracking infrastructure and calculations. You're free to: * Renormalize the data with custom multipliers * Exclude or include specific campaigns or users * Apply additional logic or filters before allocating points * Display points in your own UI exactly as you envision **Because Merkl is your indexing layer for your point program, your frontend should be the source of truth for points!** In this page, we break down how to set up points tracking campaigns with Merkl and how to consume the results for your points system. [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#step-by-step-process) Step by step process ---------------------------------------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#optional-0.-deploy-a-point-token-with-merkl) (Optional) 0. Deploy a point token with Merkl To have Merkl track activity on a protocol or token of your choice, you need to create a campaign. The Merkl system is configured such that campaigns need a token to distribute. This token can be any ERC20, including a mock, non-transferable token with no monetary value. Therefore, to run a point program with Merkl, you’ll need a token that serves as the distribution unit when instructing the system what to track. Merkl provides a template [PointToken.sol](https://github.com/AngleProtocol/merkl-contracts/blob/main/contracts/partners/tokenWrappers/PointToken.sol) for such point tokens. For any point campaign, we can also deploy and mint a mock point token for you on the chain of your choice (typically a low-cost chain). For your point token, you can also configure: * The token’s name and symbol * The logo you’d like to use Keep in mind that the chain where you deploy campaigns is independent of the chain where activity is being tracked. Once deployed and whitelisted, this point token will be equivalent to an API key you can use to start tracking campaigns on Merkl. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-1.-create-campaigns-using-point-tokens-to-launch-computation) 1\. Create campaigns using point tokens to launch computation Once you've got your mock point token, you can setup campaigns on Merkl that mirror your intended logic (e.g., “1 point token per $1,000 deposited on this pool”). Campaigns can be [created programmatically](https://docs.merkl.xyz/distribute-with-merkl/create-a-campaign) with simple scripts, and, just like any other Merkl campaign, you have access to extensive [customization options](https://docs.merkl.xyz/merkl-mechanisms/customization-options) for how results are computed. **Allocations.** Each campaign requires a maximum allocation of point tokens. If you don’t want a hard cap, avoid entering an excessively large number (e.g., trillions of tokens), as this can break Merkl’s invariants and prevent proper computation. **We recommend setting a cap of 3 billion points**, this will ensure that: * everyone with more than $1 will earn points * you have a big enough budget to not have your campaign end early. You can set a higher budget, but only do so if you know that your campaign will distribute more than 3 billion points. **Duration:** When setting up your campaigns, you’ll also need to set a start and end date. Even if your points program is meant to last 6 months, we recommend creating shorter campaigns and renewing them periodically. Campaign parameters are difficult to change once live, so shorter durations give you flexibility to adjust as needed. **Forwarders:** Merkl has [a forwarder system](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) . For example, if your campaign tracks holders of a stablecoin, Merkl can often detect and reward users who hold that stablecoin indirectly through other protocols. This means you won’t need to create a separate campaign for each protocol—unless you want different reward rates or the protocol isn’t yet integrated with forwarding. **Managing multipliers in accordance with forwarders:** If you want to have different multipliers based on where a token is used (e.g., x1 if held in wallet, x5 if used in a lending market), you need to set up your campaigns accordingly. You can either: * enable forwarding for the lending market: in this case, you will create an additional campaign for the lenders with x4 multiplier (because users will be getting x1 from the base campaign already) * disable forwarding for the lending market (done by blacklisting the market address): in this case, you will create an additional campaign for the lenders with x5 multiplier **Fixed reward rate campaigns:** When configuring campaigns with a fixed point reward rate (e.g., 1 point token per $1,000 deposited), ensure that Merkl can price the assets involved in your campaign. You can verify this by checking for existing campaigns with similar assets on the [Merkl app](https://app.merkl.xyz/) or confirming that all relevant assets are priced by Merkl. If you're unsure, contact the sales team on Telegram. **Understanding Virtual APR for Points** When reasoning in terms of points APR (the APR that would apply if each point were worth $1): * Rewarding 1 point per $1 per day = **36,500% APR** * Calculation: 100% per day × 365 days * Rewarding 1 point per $1,000 per day = **36.5% APR** * Calculation: 0.1% per day × 365 days For detailed guidance on configuring distribution rates for your points campaigns, refer to the [campaign configuration documentation](https://docs.merkl.xyz/merkl-mechanisms/campaignconfiguration#understanding-distribution-settings-parameters) . **CLAMM campaigns**: Fixed reward rates are not used for Concentrated Liquidity AMM campaigns because CLAMM distribution models are based on token0/token1 and fees (v3) or liquidity contribution (v4), not dollar values. For CLAMM campaigns, use variable reward rates instead: create a large budget and handle renormalization yourself based on the TVL during that period to properly distribute rewards. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-e4cdc3f5caaffda8767e3b633aa485e4d6af55cd%252FGroup%252025.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=36f21c4c&sv=2) Once created point campaigns appear in \[the Points section\](https://app.merkl.xyz/?tokenType=POINT&sort=tvl-desc) of the Merkl app. The campaigns you create will distribute **non-transferable point tokens**. Users cannot claim these tokens through the Merkl UI—they exist purely for tracking purposes. **Important**: All points data displayed in the Merkl frontend is informational only. Leaderboard rankings and point totals shown in the app should not be considered final, as projects retain full control to adjust allocations based on their own criteria before conducting airdrops or distributing rewards. **Creator address filtering**: Fixed reward rate campaigns credit all remaining points to the creator address at the end of a campaign. Make sure to filter out creator addresses from your leaderboards when using API data to avoid showing them in rankings. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-2.-retrieve-results-via-merkls-api) 2\. Retrieve results via Merkl's API As noted earlier, Merkl does not mint points directly. Instead, you need to parse the results of your campaigns to determine how to allocate rewards. Using the Merkl API, you can fetch: * The list of eligible addresses across one or all your campaigns * Each address's relative contribution to the pool or protocol being tracked Reward amounts are expressed in units of the point token you configured. **API Update Frequency** Once you create a campaign, API values update approximately **every 2 hours** when there are no delays. The schedule for API updates follows Merkl's [reward computation cycle](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#reward-computation) . If there is a delay in the computation process or any other issue with the campaign, the campaign will not move forward and its associated API values will not be updated until the issue is resolved. You can monitor computation status for any given campaign on the [Merkl status page](https://app.merkl.xyz/status) . **Calculating total points**: When retrieving rewards from the API, each user's total points are calculated by adding the `amount` and `pending` fields together. Both fields represent computed values that you can use immediately for your points system. **Note**: The `amount` and `pending` fields are inherited from Merkl's token distribution model, but for points programs you don't need to distinguish between them - both are ready to use. To get a user's total points, use: `totalPoints = amount + pending` [The Merkl API integration page](https://github.com/AngleProtocol/merkl-docs/blob/main/integrate-merkl/api.md.md) provides all the resources you could need to fetch data related to your points campaigns on Merkl. Below are some of the most useful API routes for accessing your campaign results: * Retrieve all campaigns created by your address at `https://api.merkl.xyz/v4/campaigns?creatorAddress=` (e.g. [https://api.merkl.xyz/v4/campaigns?creatorAddress=0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701](https://api.merkl.xyz/v4/campaigns?creatorAddress=0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701) ) * Get the reward breakdown in point token units for all the campaigns you created at `https://api.merkl.xyz/v4/rewards?chainId=&campaignId=` (e.g. [https://api.merkl.xyz/v4/rewards?chainId=100&campaignId=0x83adc24c9644324beebd26e6e2a7b9ffc14ce40d1d7cde309854ef79c9485c4c](https://api.merkl.xyz/v4/rewards?chainId=100&campaignId=0x83adc24c9644324beebd26e6e2a7b9ffc14ce40d1d7cde309854ef79c9485c4c) ) * Alternatively, Merkl also provides a route that returns the list of all addresses that have ever been rewarded with a specific reward token. You can use this endpoint with your point token to retrieve the full set of participants across all your campaigns along with their relative contribution. (e.g [https://api.merkl.xyz/v4/rewards/token/?chainId=999&address=0x0A04dc9cBf6cf3BB216f24a501994eFfB2Aa8F6f&items=100&page=0](https://api.merkl.xyz/v4/rewards/token/?chainId=999&address=0x0A04dc9cBf6cf3BB216f24a501994eFfB2Aa8F6f&items=100&page=0) ). Beware that if you created campaigns that you then cancelled with a given reward token, the results of these campaigns will also be factored in the output of this API route. **Important: Understanding the** `**chainId**` **parameter** When calling Merkl's API endpoints, the `chainId` parameter refers to **the chain where your point token is deployed**, not the chain where your campaigns are computing activity. For example, if your point token is deployed on Gnosis (chainId: 100), use `chainId=100` in your API calls, even if your campaigns track activity on other chains. This is because Merkl's API organizes rewards by the reward token's deployment chain, not by the chain where the tracked activity occurs. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-3.-normalize-and-customize) **3\. Normalize and customize** All results from the Merkl API are expressed in point token units. Before allocating points to your users, you can renormalize these results to fit your specific rules. For example, if one campaign distributes 1 point per $1,000 deposited in Protocol A, and another does the same for Protocol B, you can apply different multipliers for each protocol. Suppose you want a 5× multiplier for Protocol B: if address A earned 1 point token from the second campaign, you can assign 5 points to that address. This approach gives you full control over point allocation, letting you exclude certain campaigns, selectively boost rewards for specific users, or otherwise customize your system. If you prefer not to renormalize later, you can set your multipliers directly when creating campaigns in Merkl by increasing the reward rate at creation. For cancelled campaigns, you may need to renormalize the affected points or exclude the campaign’s results from your totals. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-4.-display-points-on-your-own-ui) 4\. **Display points on your own UI** You are responsible for displaying points in your own interface. Using data from Merkl’s mock campaigns via the API, you can easily build a leaderboard, dashboard, or other visualizations. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-5.-run-your-airdrop) 5\. Run your airdrop Once you have your list of users and their earned points, you can export it to a JSON file and use it to run your [your airdrop with Merkl](https://docs.merkl.xyz/merkl-mechanisms/campaign-types/airdrop) . [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#pricing) Pricing -------------------------------------------------------------------------------------------- Points tracking campaigns follow a straightforward pricing model based on the number of opportunities (point sources) you're monitoring: **Standard Campaigns**: * **$30 per week** per point source that can be created through the Merkl UI * **Additional fee**: $0.015 per recipient if your campaign has more than 500 recipients **Advanced Campaigns**: * **One-time setup fee**: $300 for complex campaigns (e.g., Net Lending on Aave) that require custom development and testing by our team * **Ongoing cost**: $30 per week after setup (same as standard campaigns) **What is a point source?** A point source is the specific opportunity or asset that Merkl monitors to calculate points. Examples include: * A Uniswap liquidity pool * A Morpho lending market * A vault or staking contract **Cost optimization strategies** **Create longer-duration campaigns**: Pay the user fee once by creating campaigns with longer durations instead of multiple short campaigns. **Enable forwarding to reduce campaign count**: Merkl's forwarding system can automatically detect and reward users who hold your tokens indirectly through other protocols (e.g., forwarding points earned on your token to users in Pendle). This reduces the number of campaigns you need to create. **Free TGE distribution**: If you use Merkl post-TGE to incentivize your token, you get a free TGE distribution included. ### [](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#invoicing) Invoicing **Billing cycle**: You will be invoiced retroactively at the end of each month for all campaigns created during that period. If you cancel a campaign shortly after creating it, we usually waive it as a goodwill gesture but if it's near a full week it would be billed. **Payment terms**: Invoices must be paid within 7 days. If payment is not received within this timeframe, all ongoing campaigns will be paused and your project will be temporarily blocked from Merkl until payment is resolved. [PreviousCreate your campaign from a DAO](https://docs.merkl.xyz/distribute-with-merkl/create-your-campaign-from-dao) [NextCreate a campaign pre-TGE](https://docs.merkl.xyz/distribute-with-merkl/create-a-pretge-campaign) Last updated 18 days ago * [Step by step process](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#step-by-step-process) * [(Optional) 0. Deploy a point token with Merkl](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#optional-0.-deploy-a-point-token-with-merkl) * [1\. Create campaigns using point tokens to launch computation](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-1.-create-campaigns-using-point-tokens-to-launch-computation) * [2\. Retrieve results via Merkl's API](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-2.-retrieve-results-via-merkls-api) * [3\. Normalize and customize](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-3.-normalize-and-customize) * [4\. Display points on your own UI](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-4.-display-points-on-your-own-ui) * [5\. Run your airdrop](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#id-5.-run-your-airdrop) * [Pricing](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#pricing) * [Invoicing](https://docs.merkl.xyz/distribute-with-merkl/create-a-point-program#invoicing) --- # Customization Options | Merkl Docs Merkl enables campaign creators to **customize their incentive programs with optional features** — from participant eligibility to reward boosts and referral mechanisms — providing greater flexibility beyond standard campaign settings. The full list of customization options is available in [Merkl Studio](https://studio.merkl.xyz/create-campaign/erc20) under the **Personalize** step when creating a campaign (you can simulate the campaign creation to explore all options — no need to actually launch a campaign). ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-7969b373f063363754d1154f37f26b2c73629457%252FGroup%252015.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=1611721a&sv=2) Below are some of the most common customization options. [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#access-control) Access Control ---------------------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#ofac-compliance) 🚫 OFAC Compliance Merkl allows you to **blacklist addresses flagged by the U.S. Office of Foreign Assets Control (OFAC)** to ensure compliance with U.S. economic and trade sanctions imposed on certain countries, organizations, and individuals. To do this, you need to provide the address of the contract that contains the registry of OFAC-flagged addresses, such as the [Chainalysis Sanctions Oracle on Ethereum](https://etherscan.io/address/0x40c57923924b5c5c5455c48d93317139addac8fb) . ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#worldchain-id) 🌍 Worldchain ID Merkl enables filtering of campaign participants using Worldchain’s identity system, [World ID](https://docs.world.org/world-id) , ensuring that only real, unique humans — not bots — are rewarded. [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#eligibility) Eligibility ---------------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#token-holding) 🔒 Token Holding Merkl allows campaign creators to add a customization option that **limits reward eligibility to users holding a minimum amount of a specific token in their wallet over a given period** — and this token can differ from the reward token. Example of a campaign requiring 500 stUSD held for 30 days to earn rewards: * A user holding 600 stUSD for 44 days is eligible * A user holding 400 stUSD for 60 days is not eligible Addresses that fail to meet either the token amount or duration threshold are excluded from the campaign’s rewards, ensuring incentives are focused on long-term participants rather than short-term holders. ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#token-snapshot) 📸 Token Snapshot You can also customize campaigns to reward only users who hold a minimum amount of a specific token in their wallet at a given snapshot, instead of over a duration. [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#boosts) Boosts ------------------------------------------------------------------------------------ ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#boost) 🚀 Boost Merkl allows campaign creators to boost rewards for users holding a specific token or NFT. * Similar to Curve’s vote-escrowed boost formula but with more flexibility. * No 2.5x limit – You can customize boost multipliers as needed. #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#boost-formula-computation) Boost Formula Computation: B\=b×R×vV×r+1B = b \\times \\frac{R \\times v}{V \\times r} + 1B\=b×V×rR×v​+1 Where: * **B**: Boost multiplier * **b**: Custom boost factor chosen by campaign creator * **R**: Total rewards per epoch * **r**: User’s reward per epoch * **V**: Total supply of the boost token/NFT * **v**: User’s holdings of the boost token/NFT **Example:** To achieve a boost of 10%, you'll need to indicate a Boost Multiplicator of 1.1 #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#nft-based-boosting) NFT-Based Boosting Boosts can be based on NFT holdings. Contact us for help setting up NFT-based reward boosts. ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#api-boost) 📡 API Boost Merkl provides several methods to manage dynamic boosting through an API. Here are the different methods available: #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#multiply) Multiply Multiply the current amount by the provided input: amount\=amount×boost\\text{amount} = \\text{amount} \\times \\text{boost}amount\=amount×boost #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#multiply-with-offset) Multiply with Offset Apply a 1 + boost computation: amount\=amount×(1+boost)\\text{amount} = \\text{amount} \\times (1 + \\text{boost})amount\=amount×(1+boost) #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#add) Add Add the boost number to the current amount: amount\=amount+boost\\text{amount} = \\text{amount} + \\text{boost}amount\=amount+boost #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#replace) Replace Replace the current amount with the boosted amount: amount\=boost\\text{amount} = \\text{boost}amount\=boost #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#implementation) Implementation The customization option has the following parameters: * **url**: The endpoint to which the API call will be made. * **boostingFunction**: The function used to calculate the boost. Options include `REPLACE`, `ADD`, `MULTIPLY`, and `MULTIPLY_WITH_OFFSET`. * **sendScores**: A boolean indicating whether to send ongoing reward computation scores along with the addresses. * **defaultBoost**: The default boost value to use if no specific boost is provided. Options include `ZERO_ADDRESS` and `ERROR`. * `ZERO_ADDRESS`: If an eligible address as per our reward computation is not within your API response, we will use the `ZERO_ADDRESS` boost as a default boost value for this address. Since we always exclude the zero address in our computation, it is indeed safe to use the null address as a default. If you use this option, you must therefore absolutely input a boost value for the zero address. * `ERROR`: If there is an address eligible to rewards that we do not find in your API response, the campaign will not proceed. Depending on whether `sendScores` is true or false, we will POST the following body along with the API call: `sendScores=True` `sendScores=False` The Merkl Engine will first make a POST request with all the addresses rewarded by the campaign. If this request fails, it will split the payload in 2 and make 2 requests with 50% of the addresses in each request. The script will continue dividing the payload size by 2 until your API responds correctly. This being said, we recommend supporting at least payloads of 250 addresses to prevent our engine from making too many requests to your backend. We will expect the following response: **Ensure your endpoint is non-null and in the correct format; otherwise reward computation will fail. Also, ensure there are no duplicate addresses: if duplicates exist, only the first boost value will be used!** **Important:** **The boost values are to be given in base 9**. This means that for a boost of 1, the value given for boost should be: "1000000000". #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#dynamic-whitelist-example) Dynamic whitelist (example) If you expect to **add whitelisted addresses over time**, use this method so you don't have to cancel and recreate the campaign with the updated list of whitelisted addresses. To achieve this: * Use the `MULTIPLY` boosting function * Set a boost of "1000000000" (i.e 1× in base 9) for whitelisted addresses, "0" for the `ZERO_ADDRESS` (that way all non-whitelisted addresses do not receive rewards) This is our current recommended method for private LP deals: whitelisted addresses aren’t shown in the Merkl app, and users only see “API boost” in the campaign rules. More features for such campaigns will be rolled out in the future! #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#dynamic-blacklist-example) Dynamic blacklist (example) Similarly, **you can keep a mutable blacklist without having to cancel and then recreate a campaign**. To achieve this: * Use the `MULTIPLY` boosting function * Set a boost of "0" for blacklisted addresses, and "1000000000" (i.e 1× in base 9) for the `ZERO_ADDRESS` (that way all non-blacklisted addresses receive rewards) [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#advanced-logic) Advanced Logic ---------------------------------------------------------------------------------------------------- ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#jumper-bridge) 🌉 Jumper Bridge Merkl has partnered with Jumper to enable campaign creators to reward users who bridged liquidity from another chain before participating in a campaign. This feature ensures that only cross-chain liquidity is incentivized, not movements within the same chain. ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#referral-program) 🤝 Referral Program The onchain Referral Program allows campaign creators to reward users who refer others to a campaign they’ve participated in, as well as the invitees themselves. This helps acquire new users and accelerate liquidity participation. The feature offers a wide range of customization options — from user rewards to whitelist gates — all secured by blockchain technology. #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#key-features) Key Features: * **Create Unlimited Referral Programs**: Launch as many referral programs as you want. * **Referral Code Generation**: Users can generate unique referral codes, share them with their friends, and earn rewards. * **Whitelabel Integration**: Easily integrate the program into your front-end with whitelabel options (Contact Merkl for details). * **Cross-Protocol Support**: Referral programs are compatible across the entire Merkl ecosystem, allowing creators to incentivize on any protocol/behaviour integrated with Merkl. * **Customizable Rewards**: Tailor rewards to users, referrers, invited users, or even non-participating users. * **Conditions to participate**: Add a whitelist restriction to the program, and optionally charge a fee to create a referral code. Or let anyone participate. * **Blockchain Security**: Users need to sign a transaction to confirm their referral action, ensuring secure and verified participation. **Contact Merkl for details on how to implement referral programs** ### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#raffle) 🎟️ Raffle Merkl allows you to set up raffles that randomly select lucky winners for your campaigns. You can customize these raffles in various ways to match your campaign needs. #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#customization) Customization Merkl provides several options for you to tailor your raffles: * **Multiple raffles**: Choose how often you want raffles to run. Every day? Every week? The choice is yours * **Number of winners**: Decide how many winners you want to select in each raffle. You can have one grand prize winner, or ay number of lucky winners, depending on your preference. * **Selection method**: You can choose how winners are selected. * **Everyone is equal**: All participants have an equal chance of winning. * **Whales first**: Users with higher campaign scores have a better chance of winning (this can help reward top participants). * **Multiple selection**: You can set up multiple raffles that run at the same time, each with its own rules on how rewards are distributed. #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#reproducibility) Reproducibility **Seed** The seed is the **block hash** of the very next block after the timestamp set by the campaign creator. **Generating Winning Numbers** The random number generation is done using the **XORShift128Plus** pseudo-random number generator (PRNG). This PRNG is seeded with the previously generated seed. The **step** is the minimum score of a user divided by 1000. (In the case where the score is not used to pick users, the step is, of course, 1.) **Winners** The selected numbers correspond to different **"ranges"** in the list of participants (based on their amount or score). The system picks winners by matching these random numbers with ranges of amounts that participants have. Winners are picked by sorting all the participants by their address. In the case of a **snapshot** or **airdrop**, obtaining the list of addresses is straightforward because the participants are known ahead of time. This list typically includes all the addresses that are eligible for the snapshot or airdrop, and they are usually collected from a specific event or condition. For **more complex campaigns**, where winners are determined through specific criteria or weighted selections, one approach is to run a campaign with the **exact same parameters** but **without the raffle option**. The key idea is: * You run this simplified version of the campaign **with a smaller amount** (i.e., a lower prize or allocation) and **without the raffle option**. * This will **generate the list of users** that Merkl identifies as eligible or **potential winners**. * You can use this list to generate the list of users. In essence, the result of this campaign (without the raffle option) gives you the list of users who met the conditions set by the campaign. These users are then the ones Merkl considers for potential winning when the raffle customization option is applied. #### [](https://docs.merkl.xyz/merkl-mechanisms/customization-options#example-with-5-users-5-weights-and-one-number-picked) Example with 5 users, 5 weights, and one number picked We have **5 users** in the raffle, each with an associated **weight** (which represents their chance of winning). The system will pick **one winner** based on a randomly selected number. Let’s assume the users and their weights are as follows: User Weight (Amount) A 10 B 20 C 30 D 40 E 50 We want to pick one winner randomly based on these weights. **1/ Total Weight Calculation:** First, we calculate the **total weight** by summing the weights of all the users. This total represents the "pool" from which the random number will be drawn. Total Weight = 10 + 20 + 30 + 40 + 50 = 150 **2/ Random Number Generation**: The system will then pick a random number. This random number will be between **0 and the total weight (150)**. Let’s assume the randomly picked number is **107**. **3/ Weighted Selection** The system uses this random number to determine which user will win. To do this, it calculates cumulative weights, which define "ranges" for each user. * For **User A**: The range is from **0 to 10** (because A has a weight of 10). * For **User B**: The range is from **10 to 30** (since A’s range is 0–10, and B has a weight of 20). * For **User C**: The range is from **30 to 60**. * For **User D**: The range is from **60 to 100**. * For **User E**: The range is from **100 to 150**. So, the cumulative ranges are as follows: User Cumulative Weight Range Range A 0–10 10 B 10–30 20 C 30–60 30 D 60–100 40 E 100–150 50 **4/ Determine the Winner:** Now, the system checks where the random number falls in the cumulative weight ranges: * **Random number**: 107 The number **107** falls in **User E's** range (100–150), so **User E** is the winner. [PreviousReward Forwarding](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) [NextAdditional Features](https://docs.merkl.xyz/merkl-mechanisms/features) Last updated 1 month ago * [Access Control](https://docs.merkl.xyz/merkl-mechanisms/customization-options#access-control) * [🚫 OFAC Compliance](https://docs.merkl.xyz/merkl-mechanisms/customization-options#ofac-compliance) * [🌍 Worldchain ID](https://docs.merkl.xyz/merkl-mechanisms/customization-options#worldchain-id) * [Eligibility](https://docs.merkl.xyz/merkl-mechanisms/customization-options#eligibility) * [🔒 Token Holding](https://docs.merkl.xyz/merkl-mechanisms/customization-options#token-holding) * [📸 Token Snapshot](https://docs.merkl.xyz/merkl-mechanisms/customization-options#token-snapshot) * [Boosts](https://docs.merkl.xyz/merkl-mechanisms/customization-options#boosts) * [🚀 Boost](https://docs.merkl.xyz/merkl-mechanisms/customization-options#boost) * [📡 API Boost](https://docs.merkl.xyz/merkl-mechanisms/customization-options#api-boost) * [Advanced Logic](https://docs.merkl.xyz/merkl-mechanisms/customization-options#advanced-logic) * [🌉 Jumper Bridge](https://docs.merkl.xyz/merkl-mechanisms/customization-options#jumper-bridge) * [🤝 Referral Program](https://docs.merkl.xyz/merkl-mechanisms/customization-options#referral-program) * [🎟️ Raffle](https://docs.merkl.xyz/merkl-mechanisms/customization-options#raffle) Copy let body: { address: string, score: string }[] Copy let body: { addresses: string[] } Copy const data : { address!: string; boost!: string; // should be a bigint in BASE 9 (e.g. 1 = "1000000000") }[] Copy [\ {\ "address": "0x1234567890abcdef1234567890abcdef12345678",\ "boost": "1000000000"\ },\ {\ "address": "0xabcdef1234567890abcdef1234567890abcdef1234",\ "boost": "1000000000"\ },\ {\ "address": "0x0000000000000000000000000000000000000000",\ "boost": "0"\ }\ ] Copy [\ {\ "address": "0x1234567890abcdef1234567890abcdef12345678",\ "boost": "0"\ },\ {\ "address": "0xabcdef1234567890abcdef1234567890abcdef1234",\ "boost": "0"\ },\ {\ "address": "0x0000000000000000000000000000000000000000",\ "boost": "1000000000"\ }\ ] Copy function getSelectedNumbers( seed: number, numberOfWinners: number, totalScore: number, step: number, ): number[] { const random = new XORShift128Plus(seed) const selectedNumbers = [] for (let i = 0; i < numberOfWinners; i++) { selectedNumbers.push(random.randBelow(totalScore / step) * step) } return selectedNumbers } --- # Integrate Merkl API | Merkl Docs Merkl provides an API, available [here](https://api.merkl.xyz/docs) , that serves all the data shown in the Merkl app. You can use it to track campaigns, display APRs, and integrate user rewards directly into your frontend as a white-label solution. This guide will walk you through the key integration patterns and best practices for using the Merkl API. We encourage you to reference the full API documentation for detailed endpoint specifications. If you use Merkl as a white-label solution in your frontend, you must integrate [our logo](https://docs.merkl.xyz/integrate-merkl/branding-and-integration) with a clickable link that redirects to our app. The Merkl API provides two types of data for displaying incentive information: **campaigns** and **opportunities**. Before integrating the API, it's important to understand [the difference between a campaign and an opportunity](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#opportunity-vs-campaign) within Merkl. [](https://docs.merkl.xyz/integrate-merkl/app#api-rate-limit) API Rate Limit --------------------------------------------------------------------------------- The Merkl API has a default rate limit of **10 requests per second**. This limit is sufficient for most integration use cases. If your application requires a higher rate limit, please contact the Merkl team to request a custom API key. Once provided, you can include your API key in the `X-API-Key` header for all requests to `api.merkl.xyz`: Copy X-API-Key: your-api-key-here Example request with API key: Copy curl -H "X-API-Key: your-api-key-here" https://api.merkl.xyz/v4/opportunities [](https://docs.merkl.xyz/integrate-merkl/app#finding-relevant-opportunities) Finding Relevant Opportunities ----------------------------------------------------------------------------------------------------------------- The opportunities endpoint provides key metrics like APR, TVL, and daily rewards that you can display in your frontend. Multiple filters are available to query opportunities. For a complete list, see the [opportunities endpoint documentation](https://api.merkl.xyz/docs#tag/opportunities/GET/v4/opportunities/) . The `apr` field in each opportunity is already converted to a percentage (`"apr": 50.41` is 50.41% APR) **Recommended filters:** **By Protocol ID** - Find all opportunities for a specific protocol: Example for Euler: [`https://api.merkl.xyz/v4/opportunities?mainProtocolId=euler`](https://api.merkl.xyz/v4/opportunities?mainProtocolId=euler) To find your protocol's ID, check existing opportunities on the API or try filtering by name first (`https://api.merkl.xyz/v4/opportunities?name={name}`) **By Explorer Address** - Find opportunities for a specific pool or lending market: Example for the Aave USDT0 market on Plasma: [`https://api.merkl.xyz/v4/opportunities?explorerAddress=0x5D72a9d9A9510Cd8cBdBA12aC62593A58930a948`](https://api.merkl.xyz/v4/opportunities?explorerAddress=0x5D72a9d9A9510Cd8cBdBA12aC62593A58930a948) **By Chain ID** - Find all opportunities on a specific chain: Example for Ethereum: [`https://api.merkl.xyz/v4/opportunities?chainId=1`](https://api.merkl.xyz/v4/opportunities?chainId=1) **By Tags** - Find opportunities across multiple protocols and chains: Example for zkSync Ignite Program: [`https://api.merkl.xyz/v4/opportunities?tags=zksync`](https://api.merkl.xyz/v4/opportunities?tags=zksync) If you need a custom tag for your opportunities, contact us and we'll assign it to your campaigns. Each opportunity has a permanent unique ID that remains unchanged even when multiple successive campaigns are created on the same pool or asset. [](https://docs.merkl.xyz/integrate-merkl/app#finding-relevant-campaigns) Finding Relevant Campaigns --------------------------------------------------------------------------------------------------------- While the opportunities endpoint provides high-level metrics about what's being incentivized, the campaigns endpoint offers detailed campaign rules (duration, budget, campaign type, customization options, etc.). Use this endpoint to display in-depth campaign information to your users. For a complete list of available filters, see the [campaigns endpoint documentation](https://api.merkl.xyz/docs#tag/campaigns/GET/v4/campaigns/) . **Recommended filters:** **By Creator Address** - Find all campaigns created by a specific address: Example: [`https://api.merkl.xyz/v4/campaigns?creatorAddress=0xdef1FA4CEfe67365ba046a7C630D6B885298E210`](https://api.merkl.xyz/v4/campaigns?creatorAddress=0xdef1FA4CEfe67365ba046a7C630D6B885298E210) **By Token Symbol** - Find all campaigns distributing a specific reward token: Example for campaigns distributing $PYTH: [`https://api.merkl.xyz/v4/campaigns?tokenSymbol=PYTH`](https://api.merkl.xyz/v4/campaigns?tokenSymbol=PYTH) **Excluding sub-campaigns** - Find all campaigns except sub-campaigns created by parent campaigns due to [reward forwarding](https://docs.merkl.xyz/merkl-mechanisms/reward-forwarding) [](https://docs.merkl.xyz/integrate-merkl/app#retrieving-both-campaign-and-opportunity-data) Retrieving Both Campaign and Opportunity Data ----------------------------------------------------------------------------------------------------------------------------------------------- The `campaigns` endpoint provides campaign-specific information but not upstream opportunity data. Similarly, querying for an opportunity doesn't include downstream campaign details. To retrieve all information in a single request: **Search campaigns with related opportunities** - Use `/v4/campaigns` with `withOpportunity=true`: **Search opportunities with related campaigns** - Use `/v4/opportunities` with `campaigns=true`: Learn more about [how TVLs, APRs, and daily rewards are computed](https://docs.merkl.xyz/merkl-mechanisms/technical-overview#main-metrics) and why they may differ between the campaign and opportunity levels. ### [](https://docs.merkl.xyz/integrate-merkl/app#about-campaign-ids) About Campaign IDs Merkl uses two distinct identifier types for campaigns: * `**campaignId**` (format: `0x...`): The onchain identifier of a campaign. Note that this is **not unique** across chains—multiple campaigns on different chains may share the same `campaignId`. You can find this in the [opportunities page](https://app.merkl.xyz/) by selecting an opportunity, viewing campaign details, and navigating to the "Advanced" tab. * `**id**` (format: numeric, e.g., `13972358188887408622`): The unique database identifier used for most API routes. You can retrieve this by querying the [campaigns endpoint](https://api.merkl.xyz/docs#tag/campaigns/get/v4/campaigns/) with a `campaignId`. You may also find a campaign's database ID under the advanced tab of a campaign on the Merkl app. ![](https://docs.merkl.xyz/~gitbook/image?url=https%3A%2F%2F3295124503-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKRQdTiHhBGLRKeCCOqdc%252Fuploads%252Fgit-blob-8188bfd6fe8cc38dc7e3dae50c94c8a9cf29d8ca%252FAPI-V4.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=5c966264&sv=2) [](https://docs.merkl.xyz/integrate-merkl/app#fetching-campaign-and-user-statistics) Fetching Campaign and User Statistics ------------------------------------------------------------------------------------------------------------------------------- The Merkl API provides endpoints to retrieve user leaderboards and reward statistics for individual campaigns or across multiple campaigns distributing the same token. **Recommended endpoints:** * **Campaign leaderboard**: Get all users rewarded in a campaign - [`https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/`](https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/) * **Token-level rewards**: Check rewards across all campaigns for a token - [`https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/token/`](https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/token/) * **Campaign-level rewards**: Check total amount of rewards distributed in a specific campaign - [`https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/total`](https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/total) * **Unclaimed rewards**: Check unclaimed amounts for a campaign - [`https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/unclaim/`](https://api.merkl.xyz/docs#tag/rewards/get/v4/rewards/unclaim/) * **Historical metrics**: Get TVL and APR history for a campaign - [`https://api.merkl.xyz/docs#tag/campaigns/get/v4/campaigns/{id}/metrics`](https://api.merkl.xyz/docs#tag/campaigns/get/v4/campaigns/{id}/metrics) Example Python script for fetching campaign metrics (unclaimed rewards, number of wallets and evolution of APR/TVL/Daily Rewards)[](https://docs.merkl.xyz/integrate-merkl/app#example-python-script-for-fetching-campaign-metrics-unclaimed-rewards-number-of-wallets-and-evolutio) [](https://docs.merkl.xyz/integrate-merkl/app#integrating-user-rewards) Integrating User Rewards ----------------------------------------------------------------------------------------------------- To retrieve reward data for a specific user, use the following endpoint: Example for a single chain - Checking a user's rewards on zkSync: [`https://api.merkl.xyz/v4/users/0x4F2BF7469Bc38d1aE779b1F4affC588f35E60973/rewards?chainId=324`](https://api.merkl.xyz/v4/users/0x4F2BF7469Bc38d1aE779b1F4affC588f35E60973/rewards?chainId=324) Example for multiple chains - Checking a user's rewards on zkSync, Ethereum and Arbitrum: [`https://api.merkl.xyz/v4/users/0x4F2BF7469Bc38d1aE779b1F4affC588f35E60973/rewards?chainId=324,1,42161`](https://api.merkl.xyz/v4/users/0x4F2BF7469Bc38d1aE779b1F4affC588f35E60973/rewards?chainId=324,1,42161) **This endpoint returns:** * `**amount**`: Total tokens credited to the user onchain * `**pending**`: Pending rewards that will be credited on the next Merkle root update * `**claimed**`: Tokens already claimed by the user * `**proofs**`: Cryptographic proofs needed for claiming rewards (detailed in the next section) * `**breakdowns**`: Campaign attribution for earned rewards (may be incomplete and not show all campaigns) The claimable amount equals `amount - claimed`. `pending` rewards update more frequently (about every 2 hours) than the `amount`. Integrating `pending` rewards in your app lets you show more timely updates to your users. However, always display pending rewards separately to avoid confusion because `pending` rewards are not claimable. Also note that whenever tokens are credited onchain, the `pending` rewards reset to zero because they are added to the `amount`. **Important notes about this endpoint:** * **Historical data**: The API doesn't return time-series data. To build historical datasets, take daily snapshots of the API responses. * **Caching behavior**: This route is cached. If called immediately after a user claims rewards, the data may be stale. Use the `reloadChainId` parameter to force a cache refresh: **Building claim transactions:** Rewards are claimed through the `Distributor` contract. Find contract addresses for each chain on the [Merkl status page](https://app.merkl.xyz/status) . If the `token` or `amount` doesn't match the `proof` when calling the `Distributor` contract, the transaction will revert. Rewards on Merkl are claimable per token. Users can claim rewards for a single token or all tokens at once. **Example claiming script:** Here is a script to claim all token rewards for a user on a chain: [](https://docs.merkl.xyz/integrate-merkl/app#additional-tips) Additional Tips ----------------------------------------------------------------------------------- **Pagination** - All Merkl API endpoints are paginated. Iterate through pages or increase the page size parameter to retrieve more results (using `&page=`) **Query optimization** - While many API parameters are optional, specifying additional filters can significantly improve query performance. **Test campaigns** - To retrieve data for test campaigns (using tokens like `aglaMerkl`), include the `&test=true` parameter in your requests. [](https://docs.merkl.xyz/integrate-merkl/app#benefit-from-typescript-typings) Benefit from TypeScript Typings ------------------------------------------------------------------------------------------------------------------- For type-safe API interactions, use the [@merkl/api NPM package](https://www.npmjs.com/package/@merkl/api?activeTab=readme) . [PreviousMerkl User FAQ](https://docs.merkl.xyz/earn-with-merkl/faq-earn) [NextDisplay Your Native APR](https://docs.merkl.xyz/integrate-merkl/nativeapr) Last updated 19 days ago * [API Rate Limit](https://docs.merkl.xyz/integrate-merkl/app#api-rate-limit) * [Finding Relevant Opportunities](https://docs.merkl.xyz/integrate-merkl/app#finding-relevant-opportunities) * [Finding Relevant Campaigns](https://docs.merkl.xyz/integrate-merkl/app#finding-relevant-campaigns) * [Retrieving Both Campaign and Opportunity Data](https://docs.merkl.xyz/integrate-merkl/app#retrieving-both-campaign-and-opportunity-data) * [About Campaign IDs](https://docs.merkl.xyz/integrate-merkl/app#about-campaign-ids) * [Fetching Campaign and User Statistics](https://docs.merkl.xyz/integrate-merkl/app#fetching-campaign-and-user-statistics) * [Integrating User Rewards](https://docs.merkl.xyz/integrate-merkl/app#integrating-user-rewards) * [Additional Tips](https://docs.merkl.xyz/integrate-merkl/app#additional-tips) * [Benefit from TypeScript Typings](https://docs.merkl.xyz/integrate-merkl/app#benefit-from-typescript-typings) Copy https://api.merkl.xyz/v4/opportunities?mainProtocolId={protocol_id} Copy https://api.merkl.xyz/v4/opportunities?explorerAddress={address} Copy https://api.merkl.xyz/v4/opportunities?chainId={chain_id} Copy https://api.merkl.xyz/v4/opportunities?tags={tag} Copy https://api.merkl.xyz/v4/campaigns?creatorAddress={address} Copy https://api.merkl.xyz/v4/campaigns?tokenSymbol={symbol} Copy https://api.merkl.xyz/v4/campaigns?excludeSubCampaigns=true Copy https://api.merkl.xyz/v4/campaigns?withOpportunity=true Copy https://api.merkl.xyz/v4/opportunities?campaigns=true Copy import datetime import requests def timestamp_to_date(timestamp: int) -> str: dt = datetime.datetime.fromtimestamp(timestamp) return dt.strftime('%d/%m/%Y') def get_unclaimed_rewards(campaign_id: int): campaign_info_url = f"https://api.merkl.xyz/v4/campaigns/{campaign_id}" campaign_response = requests.get(campaign_info_url) campaign_onchain_id = campaign_response.json().get("campaignId") distribution_chain_id = campaign_response.json().get("distributionChainId") reward_token_symbol = campaign_response.json().get("rewardToken", {}).get("symbol", "UNKNOWN") decimals = campaign_response.json().get("rewardToken", {}).get("decimals", 18) total_budget = int(campaign_response.json().get("amount", 0))/(10 ** decimals) unclaimed_url = f"https://api.merkl.xyz/v4/rewards/unclaim?chainId={distribution_chain_id}&campaignIds={campaign_onchain_id}" response_distributed = requests.get(f"https://api.merkl.xyz/v4/rewards/total?chainId={distribution_chain_id}&campaignId={campaign_onchain_id}") total_distributed = int(response_distributed.json().get("amount"))/(10 ** decimals) response = requests.get(unclaimed_url) unclaimed = int(response.json().get(campaign_onchain_id))/(10 ** decimals) print(f"Total budget: {total_budget:,.2f} {reward_token_symbol}") print(f"Distributed: {total_distributed:,.2f} {reward_token_symbol} ") print(f"Claimed: {total_distributed - unclaimed:,.2f} {reward_token_symbol}") print(f"Unclaimed: {unclaimed:,.2f} {reward_token_symbol}") def get_campaign_metrics(campaignId: int): url = f"https://api.merkl.xyz/v4/campaigns/{campaignId}/metrics" response = requests.get(url) data = response.json() if response.status_code != 200: print(f"Failed to fetch metrics for campaign {campaignId}. Status code: {response.status_code}") return tvlsRecords = data["tvlRecords"] aprRecords = data["aprRecords"] dailyRewardsRecords = data["dailyRewardsRecords"] walletCountRecords = data["walletCount"] # Find the min and max wallet count min_wallets = min([item["walletCount"] for item in walletCountRecords]) max_wallets = max([item["walletCount"] for item in walletCountRecords]) print(f"Campaign {campaignId} metrics:") print("") get_unclaimed_rewards(campaignId) print("") print(f"Min wallets: {min_wallets}") print(f"Max wallets: {max_wallets}") print("") results = {} for item in tvlsRecords: if item["timestamp"] not in results: results[item["timestamp"]] = {} results[item["timestamp"]]["TVL"] = int(item["total"]) for item in aprRecords: if item["timestamp"] not in results: results[item["timestamp"]] = {} results[item["timestamp"]]["APR"] = float(item["apr"]) for item in dailyRewardsRecords: if item["timestamp"] not in results: results[item["timestamp"]] = {} results[item["timestamp"]]["Daily Rewards"] = float(item["total"]) # Find the closest wallet count record for each timestamp and add it to results for timestamp in results.keys(): closest_record = min( walletCountRecords, key=lambda record: abs(int(timestamp) - int(record.get('timestamp', 0))) ) results[timestamp]["Wallets"] = closest_record.get("walletCount", 0) # Print the results in a table format print(f"{'Date':<20} {'TVL ($)':<20} {'APR (%)':<15} {'Daily Rewards ($)':<20} {'Wallets':<10}") for timestamp in sorted(results.keys()): date_str = timestamp_to_date(int(timestamp)) tvl = results[timestamp].get("TVL", 0) apr = results[timestamp].get("APR", 0) # Convert to percentage daily_rewards = results[timestamp].get("Daily Rewards", 0) wallets = results[timestamp].get("Wallets", 0) # Print TVL in human readable format tvl = f"{tvl:,.0f}" daily_rewards = f"{daily_rewards:,.2f}" print(f"{date_str:<20} {tvl:<20} {apr:<15.2f} {daily_rewards:<20} {wallets:<10}") if __name__ == "__main__": # Example usage campaign_id = 12345 # Replace with your campaign ID get_campaign_metrics(campaign_id) Copy https://api.merkl.xyz/v4/users/{address}/rewards?chainId={chain_id} Copy https://api.merkl.xyz/v4/users/{address}/rewards?chainId=324&reloadChainId=324 Copy import type { JsonRpcSigner } from '@ethersproject/providers' import { MerklApi } from '@merkl/api' import { Distributor__factory } from '@sdk' // ABI can be fetched on etherscan const DISTRIBUTOR_ADDRESS = '0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae' export const claim = async (chainId: number, signer: JsonRpcSigner) => { const { status, data } = await MerklApi('https://api.merkl.xyz') .v4.users({ address: signer._address }) .rewards.get({ query: { chainId: [chainId] } }) if (status !== 200) throw 'Failed to fetch rewards' const users = [] const tokens = [] const amounts = [] const proofs = [] for (const rewards of data) { if (rewards.chain.id !== chainId) continue for (const reward of rewards.rewards) { users.push(signer._address) tokens.push(reward.token.address) amounts.push(reward.amount) proofs.push(reward.proofs) } } if (tokens.length === 0) throw 'No tokens to claim' const contract = Distributor__factory.connect(DISTRIBUTOR_ADDRESS, signer) await (await contract.claim(users, tokens, amounts, proofs)).wait() } ---