Skip to content

Integrate Reward Claims

Integrating reward claims is a crucial step in providing a complete and engaging experience for your users. Many Morpho Vaults and Markets offer additional incentives, and users expect to be able to view and claim these rewards directly through your application.

This tutorial will guide you through the process of integrating reward claims, covering the two main distribution systems used in the Morpho ecosystem.

Understanding the Rewards Ecosystem

Morpho's rewards are distributed through two primary systems. Your integration may need to support both to provide a comprehensive view for your users:

  1. Merkl: A third-party rewards distribution platform used by many ecosystem partners and for newer campaigns. It allows for flexible and automated reward distributions.
  2. Morpho URD (Universal Rewards Distributor): The Morpho DAO's native rewards stack, built for distributing MORPHO and other tokens via a Merkle-proof system. This is used for historical and ongoing DAO-led incentive programs.

Let's explore how to integrate claims for both.

1. Claiming Merkl Rewards

For most new and ongoing ecosystem rewards, you will interact with Merkl. Merkl provides extensive documentation and a straightforward integration path.

Instead of replicating their documentation, we recommend you follow their official integration guide.

2. Claiming Morpho URD Rewards

For rewards distributed directly by the Morpho DAO, you will need to interact with the Morpho Rewards API and the Universal Rewards Distributor (URD) smart contracts.

The process involves two main steps:

  1. Fetch Claim Data: Query the Morpho Rewards API to get the user's claimable amount and the necessary Merkle proof.
  2. Execute Claim: Use the data from the API to call the claim function on the appropriate URD smart contract.

Step 1: Fetch Claim Data from the Rewards API

The primary endpoint you'll need is /v1/users/{user_address}/distributions. This endpoint returns all claimable distributions for a given user across all URD contracts.

Endpoint: https://rewards.morpho.org/v1/users/{user_address}/distributions

Replace {user_address} with the address of the user you are fetching rewards for.

Example API Response: A successful request will return a JSON array. Each object in the array represents a distinct reward distribution.

[
  {
    "user": "0x0ec553110e53122d1226646670a8475D4C8E6F04",
    "distributor": "0x330eefa8a787552DC5cAd3C3cA644844B1E61Ddb", // The URD contract to call
    "chain_id": 1,
    "asset": {
      "id": "0x58D97B57BB95320F9a05dC918Aef65434969c2B2-1",
      "address": "0x58D97B57BB95320F9a05dC918Aef65434969c2B2", // The reward token address
      "chain_id": 1
    },
    "claimable": "115631364898103632676", // Cumulative claimable amount
    "proof": [ // The Merkle proof
      "0x1a2b...",
      "0x3c4d..."
    ],
    "tx_data": "0x4e71d92d..." // Pre-formatted transaction data for convenience
  }
]
Key Fields to Use:
  • distributor: The address of the URD smart contract you need to interact with.
  • asset.address: The address of the ERC20 token being rewarded.
  • claimable: The cumulative amount of tokens the user is entitled to claim from this distributor since inception. This is NOT the amount they will receive now if they have claimed before.
  • proof: The Merkle proof required to validate the claim onchain.
  • tx_data: A pre-formatted hexadecimal string representing the complete calldata for the claim transaction. This is the easiest way to integrate.

Step 2: Implement the Claim Onchain

Once you have the data from the API, you can trigger the onchain claim. You can do this in two ways:

Method A: Using the Pre-formatted tx_data (Recommended)

This is the simplest method. Send a transaction directly to the distributor address with the tx_data from the API response as the transaction data.

// Using viem
const txHash = await walletClient.sendTransaction({
  to: distributorAddress, // from API response
  data: txData,          // from API response
});

Method B: Manually Calling the claim Function

If you need more control, you can manually call the claim function on the URD contract.

URD claim Function Signature:
function claim(address account, address reward, uint256 claimable, bytes32[] calldata proof) external returns (uint256 amount);

You would populate the arguments using the corresponding fields from the API response:

  • account: The user's address.
  • reward: The asset.address.
  • claimable: The claimable amount.
  • proof: The proof array.

Putting It All Together: TypeScript Example

Here is a template for a TypeScript script that fetches a user's rewards and prepares the transaction to claim them.

Step 1: Setup and Dependencies

Ensure you have viem or a similar library installed.

import { createWalletClient, http, publicActions } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
 
const userAddress = "0x0ec553110e53122d1226646670a8475D4C8E6F04";
const account = privateKeyToAccount("0x..."); // The user's (or a relayer's) private key
 
const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(process.env.RPC_URL_MAINNET),
}).extend(publicActions);

Step 2: Fetch Claimable Rewards

Create a function to call the Morpho Rewards API.

interface RewardDistribution {
  distributor: `0x${string}`;
  tx_data: `0x${string}`;
  asset: {
    address: `0x${string}`;
    symbol: string;
  };
  // ... other fields
}
 
async function fetchUserRewards(address: string): Promise<RewardDistribution[]> {
  const response = await fetch(
    `https://rewards.morpho.org/v1/users/${address}/distributions`
  );
  if (!response.ok) {
    throw new Error("Failed to fetch rewards");
  }
  return response.json();
}

Step 3: Iterate and Claim

Loop through the distributions and send the transaction.

async function claimRewards() {
  console.log(`Fetching rewards for ${userAddress}...`);
  const distributions = await fetchUserRewards(userAddress);
 
  if (distributions.length === 0) {
    console.log("No rewards to claim.");
    return;
  }
 
  for (const dist of distributions) {
    console.log(`Claiming from distributor: ${dist.distributor}`);
    console.log(`Reward Token: ${dist.asset.symbol}`);
 
    try {
      const claimed = await client.readContract({
        address: dist.distributor,
        abi: URD_ABI, // You will need the URD ABI
        functionName: 'claimed',
        args: [userAddress, dist.asset.address]
      });
 
      if (claimed >= dist.claimable) {
        console.log('Rewards already claimed for this distribution.');
        continue;
      }
      // or if the claimable amount is significant enough to warrant a transaction.
      // This prevents sending transactions for dust amounts or already-claimed rewards.
 
      const txHash = await client.sendTransaction({
        to: dist.distributor,
        data: dist.tx_data,
        chain: mainnet, // Ensure the correct chain is specified
      });
      
      console.log(`Claim transaction sent: ${txHash}`);
      const receipt = await client.waitForTransactionReceipt({ hash: txHash });
      console.log(`Claim successful! Status: ${receipt.status}`);
 
    } catch (error) {
      console.error(`Failed to claim rewards from ${dist.distributor}:`, error);
    }
  }
}
 
claimRewards();

FAQ

Who can claim rewards?

Anyone can claim rewards on behalf of any eligible user. Note: only the eligible user will receive them anyway.

Is there an onchain way to retrieve rewards?

No, there is not. The computation is done offchain, and has to be queried at the respective API levels.

Next Steps

  • Bundle Claims: For an improved user experience, you can batch multiple reward claims into a single transaction using Morpho Bundlers.
  • API Reference: For more endpoints and data, consult the full Rewards API Documentation.