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:
- Merkl: A third-party rewards distribution platform used by many ecosystem partners and for newer campaigns. It allows for flexible and automated reward distributions.
- 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:
- Fetch Claim Data: Query the Morpho Rewards API to get the user's claimable amount and the necessary Merkle proof.
- 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
}
]
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 completecalldata
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.
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
: Theasset.address
.claimable
: Theclaimable
amount.proof
: Theproof
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.