Claim Rewards
This tutorial shows you how to implement reward claiming for both Merkl (current programs) and Morpho URD (legacy programs) in your application. Supporting both systems ensures users can access all their available rewards.
Overview
Claiming rewards involves:
- Fetching claim data (proof + transaction data) from the appropriate API
- Constructing or using pre-formatted transaction calldata
- Submitting the claim transaction onchain
- Handling the response and updating your UI
The process is similar for both Merkl and URD, but they use different APIs and smart contracts.
Prerequisites
- Understanding of Distribution Systems
- Familiarity with Fetching Rewards Data
- A web3 library (viem, ethers, web3.js)
- User's wallet connected to your app
Claiming via Merkl
Merkl is the primary distribution system for current reward programs.
Step 1: Fetch Claim Data
Endpoint:GET https://api.merkl.xyz/v4/claim?user={address}&chainId={chainId}
async function fetchMerklClaimData(
userAddress: string,
chainId: number
): Promise<MerklClaimData> {
const response = await fetch(
`https://api.merkl.xyz/v4/claim?user=${userAddress}&chainId=${chainId}`
);
if (!response.ok) {
throw new Error(`Merkl API error: ${response.status}`);
}
return response.json();
}
{
"claim": {
"user": "0x...",
"tokens": ["0xTOKEN1", "0xTOKEN2"],
"amounts": ["1000000000000000000", "2000000000000000000"],
"proofs": [
["0xproof1a", "0xproof1b"],
["0xproof2a", "0xproof2b"]
]
}
}
Step 2: Execute the Claim
Using viem:
import { createWalletClient, custom } from "viem";
import { mainnet } from "viem/chains";
const MERKL_DISTRIBUTOR = "0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae"; // Mainnet
async function claimMerklRewards(
userAddress: string,
chainId: number
) {
// 1. Fetch claim data
const claimData = await fetchMerklClaimData(userAddress, chainId);
// 2. Setup wallet client
const walletClient = createWalletClient({
account: userAddress,
chain: mainnet,
transport: custom(window.ethereum),
});
// 3. Send claim transaction
const hash = await walletClient.writeContract({
address: MERKL_DISTRIBUTOR,
abi: MERKL_ABI,
functionName: "claim",
args: [
claimData.claim.user,
claimData.claim.tokens,
claimData.claim.amounts,
claimData.claim.proofs,
],
});
// 4. Wait for confirmation
const receipt = await walletClient.waitForTransactionReceipt({ hash });
return receipt;
}
// Merkl Distributor ABI (claim function)
const MERKL_ABI = [
{
inputs: [
{ name: "user", type: "address" },
{ name: "tokens", type: "address[]" },
{ name: "amounts", type: "uint256[]" },
{ name: "proofs", type: "bytes32[][]" },
],
name: "claim",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const;
Step 3: Handle the Result
try {
const receipt = await claimMerklRewards(userAddress, 1);
if (receipt.status === "success") {
console.log("✅ Merkl rewards claimed successfully!");
// Update your UI to reflect claimed rewards
} else {
console.error("❌ Claim transaction failed");
}
} catch (error) {
console.error("Error claiming Merkl rewards:", error);
// Show error message to user
}
For more details on Merkl claiming, see the official Merkl documentation.
Claiming via Morpho URD - Legacy distributions
The URD handles legacy reward programs. While new programs use Merkl, many users still have unclaimed URD rewards.
Step 1: Fetch Distribution Data
Endpoint:GET https://rewards.morpho.org/v1/users/{address}/distributions
interface URDDistribution {
user: string;
distributor: string; // URD contract address
chain_id: number;
asset: {
address: string;
symbol: string;
};
claimable: string; // Cumulative amount
proof: string[];
tx_data: string; // Pre-formatted calldata
}
async function fetchURDDistributions(
userAddress: string
): Promise<URDDistribution[]> {
const response = await fetch(
`https://rewards.morpho.org/v1/users/${userAddress}/distributions`
);
if (!response.ok) {
throw new Error(`Rewards API error: ${response.status}`);
}
return response.json();
}
[
{
"user": "0x...",
"distributor": "0x330eefa8a787552DC5cAd3C3cA644844B1E61Ddb",
"chain_id": 1,
"asset": {
"address": "0x58D97B57BB95320F9a05dC918Aef65434969c2B2",
"symbol": "MORPHO"
},
"claimable": "115631364898103632676",
"proof": ["0x1a2b...", "0x3c4d..."],
"tx_data": "0x4e71d92d..."
}
]
Step 2: Check Already-Claimed Amount
Before claiming, verify how much the user has already claimed to avoid unnecessary transactions:
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
const URD_ABI = [
{
inputs: [
{ name: "account", type: "address" },
{ name: "reward", type: "address" },
],
name: "claimed",
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
] as const;
async function getClaimedAmount(
distributorAddress: string,
userAddress: string,
rewardTokenAddress: string
): Promise<bigint> {
const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});
const claimed = await publicClient.readContract({
address: distributorAddress as `0x${string}`,
abi: URD_ABI,
functionName: "claimed",
args: [userAddress as `0x${string}`, rewardTokenAddress as `0x${string}`],
});
return claimed;
}
Step 3: Execute the Claim
Using Pre-formatted tx_data
The API provides ready-to-use transaction calldata:
async function claimURDRewards(distribution: URDDistribution) {
// 1. Check if there's anything to claim
const alreadyClaimed = await getClaimedAmount(
distribution.distributor,
distribution.user,
distribution.asset.address
);
const claimable = BigInt(distribution.claimable);
if (alreadyClaimed >= claimable) {
console.log("Nothing to claim (already claimed)");
return null;
}
// 2. Setup wallet client
const walletClient = createWalletClient({
account: distribution.user as `0x${string}`,
chain: mainnet,
transport: custom(window.ethereum),
});
// 3. Send transaction with pre-formatted data
const hash = await walletClient.sendTransaction({
to: distribution.distributor as `0x${string}`,
data: distribution.tx_data as `0x${string}`,
});
// 4. Wait for confirmation
const receipt = await walletClient.waitForTransactionReceipt({ hash });
return receipt;
}
Resources
- Merkl Claim Docs: docs.merkl.xyz/integrate-merkl/app#claiming-user-rewards
- Rewards API: rewards.morpho.org/docs
- Example Code: morpho-org/merkl-morpho-recipe