# Claiming rewards

> Instructions for data providers to claim rewards.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

Source: https://dev.flare.network/network/fsp/guides/claiming-rewards

This guide shows how to claim Flare rewards directly via smart contracts, using the published per-epoch Merkle distributions.

You will:

-   read the claimable reward epoch range onchain,
-   fetch the distribution tuples JSON offchain,
-   extract your Merkle proof + claim tuple,
-   submit `RewardManager.claim(...)`,
-   claim staking rewards `ValidatorRewardManager.claim(...)`.

Reward claim period

Delegation rewards expire after 25 reward epochs, make sure to claim before then. Staking rewards do not expire.

Learn more about how [signing](/network/fsp/weights-and-signing) and [rewards](/network/fsp/rewarding) work in the FSP.

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

1.  **Wallet + gas:** A wallet that can sign transactions on the target network, funded with enough native token to pay gas.
2.  **RPC access:** An RPC endpoint for the chosen network (you can use any RPC listed on the [Network](/network/overview) page).
3.  **Contracts:** Addresses can be found in the [Flare Contract Registry](/network/guides/flare-contracts-registry):
    -   [`FlareSystemsManager`](/network/fsp/solidity-reference/IFlareSystemsManager)
    -   [`RewardManager`](/network/fsp/solidity-reference/IRewardManager)
    -   [`ClaimSetupManager`](/network/solidity-reference/IClaimSetupManager)
    -   [`ValidatorRewardManager`](/network/fsp/solidity-reference)
4.  **Recipient address:** The address that will receive the rewards (`recipient`).
5.  **Reward distribution data access:** You must be able to fetch per-epoch `reward-distribution-data-tuples.json` from the official distribution locations on GitHub or GitLab.
6.  **Wrap option:** `wrapRewards` is `true` unless explicitly set to `false`, i.e. on Flare Mainnet you will receive `WFLR`.
7.  **Beneficiary address (depends on claim type):**
    
    Claim type
    
    `claimType`
    
    `beneficiary` must be
    
    Account that must authorize executors
    
    `DIRECT`
    
    `0`
    
    signing policy address
    
    signing policy address
    
    `FEE`
    
    `1`
    
    identity address
    
    identity address
    

### Recommended: Configure a claim executor and claim recipient[​](#recommended-configure-a-claim-executor-and-claim-recipient "Direct link to Recommended: Configure a claim executor and claim recipient")

For improved security, set up a **claim executor** (an account authorized to claim on your behalf) and an **allowed claim recipient** (an address permitted to receive rewards).

1.  Create and fund a new account with sufficient native tokens
2.  Authorize this account as a claim executor, which can be done using the [`setClaimExecutors`](/network/solidity-reference/IClaimSetupManager#setclaimexecutors) method.
    -   For **DIRECT (0)** claims: Use your signing policy account to authorize the executor.
    -   For **FEE (1)** claims: Use your identity account to authorize the executor.
3.  Setup a **Claim Recipient** account, this can be done through the [`setAllowedClaimRecipient`](/network/solidity-reference/IClaimSetupManager#setallowedclaimrecipients) method.

```
import { Contract } from "ethers";const claimSetupManager = new Contract(  process.env.CLAIM_SETUP_MANAGER_ADDRESS!, // from contract registry  CLAIM_SETUP_MANAGER_ABI,  beneficiarySigner, // IMPORTANT: signer must be the signing policy address (`DIRECT`) or identity address (`FEE`));// 1) Authorize the executor that will submit RewardManager.claim(...)await (await claimSetupManager.setClaimExecutors([executorAddress])).wait();// 2) Allowlist a recipient address that is permitted to receive rewardsawait (  await claimSetupManager.setAllowedClaimRecipients([recipientAddress])).wait();
```

## Step-by-step[​](#step-by-step "Direct link to Step-by-step")

### 1\. Connect to the contracts[​](#1-connect-to-the-contracts "Direct link to 1. Connect to the contracts")

Instantiate contract clients (ABI + address) for:

-   [`FlareSystemsManager`](/network/fsp/solidity-reference/IFlareSystemsManager)
-   [`RewardManager`](/network/fsp/solidity-reference/IRewardManager)

Example using [ethers v6](https://docs.ethers.org/v6/):

```
import { JsonRpcProvider, Wallet, Contract } from "ethers";// 1) Provider + signerconst provider = new JsonRpcProvider(process.env.RPC_URL);const signer = new Wallet(process.env.CLAIM_EXECUTOR_PRIVATE_KEY!, provider);// 2) Contract instances (ABI omitted here; use your generated ABI or interface)const flareSystemsManager = new Contract(  process.env.FLARE_SYSTEMS_MANAGER_ADDRESS!,  FLARE_SYSTEMS_MANAGER_ABI,  provider,);const rewardManager = new Contract(  process.env.REWARD_MANAGER_ADDRESS!,  REWARD_MANAGER_ABI,  provider,);
```

### 2\. Read the claimable epoch range[​](#2-read-the-claimable-epoch-range "Direct link to 2. Read the claimable epoch range")

Call:

1.  `startRewardEpochId = RewardManager.getNextClaimableRewardEpochId(beneficiary)`
2.  `[_, endRewardEpochId] = RewardManager.getRewardEpochIdsWithClaimableRewards()`

If `endRewardEpochId < startRewardEpochId`, there is nothing to claim.

Example:

```
const start = await rewardManager.getNextClaimableRewardEpochId(beneficiary);const [, end] = await rewardManager.getRewardEpochIdsWithClaimableRewards();if (end < start) {  console.log("Nothing claimable for this beneficiary.");}
```

### 3\. Keep only signed epochs[​](#3-keep-only-signed-epochs "Direct link to 3. Keep only signed epochs")

For each `epochId` in `[startRewardEpochId … endRewardEpochId]`, call:

-   `rewardsHash = FlareSystemsManager.rewardsHash(epochId)`

Only proceed if `rewardsHash` is **not** `0x000...000` (`bytes32(0)`).

Example:

```
const ZERO_BYTES32 =  "0x0000000000000000000000000000000000000000000000000000000000000000";const signedEpochs: bigint[] = [];for (let epochId = start; epochId <= end; epochId++) {  const rewardsHash = await flareSystemsManager.rewardsHash(epochId);  if (rewardsHash && rewardsHash !== ZERO_BYTES32) signedEpochs.push(epochId);}
```

### 4\. Fetch reward distribution tuples[​](#4-fetch-reward-distribution-tuples "Direct link to 4. Fetch reward distribution tuples")

Calculating or verifying rewards data

All rewards scripts are publicly available, and you are encouraged to calculate/verify the rewards data yourself. The data for the rewards is published on [flare-foundation/fsp-rewards](https://github.com/flare-foundation/fsp-rewards) and the instructions for how to calculate them are [here](https://github.com/flare-foundation/FTSO-Scaling/blob/main/scripts/rewards/README.md#public-reward-data).

For each signed epoch, fetch `reward-distribution-data-tuples.json`. You fetch **one JSON file per epoch**, using the epoch id in the path. If the JSON cannot be fetched or validated, you cannot build a valid Merkle proof for that epoch.

Fetch `reward-distribution-data-tuples.json` from [flare-foundation/fsp-rewards](https://github.com/flare-foundation/fsp-rewards) on GitHub.

```
EPOCH_ID=123BENEFICIARY="0xYourBeneficiaryAddressHere"CLAIM_TYPE=0curl -fsSL \  "https://raw.githubusercontent.com/flare-foundation/fsp-rewards/refs/heads/main/flare/${EPOCH_ID}/reward-distribution-data-tuples.json" \  | jq --arg b "${BENEFICIARY}" --argjson ct "${CLAIM_TYPE}" -r '      .rewardClaims[]      | select(.[1][1] | ascii_downcase == ($b | ascii_downcase))      | select(.[1][3] == $ct)    '
```

Fetch `reward-distribution-data-tuples.json` from the community-maintained mirror [timivesel/ftsov2-testnet-rewards](https://gitlab.com/timivesel/ftsov2-testnet-rewards) on GitLab. (This mirror is not an official Flare Foundation resource; treat the data with appropriate care and verify each tuple against on-chain values before claiming on production systems.)

```
EPOCH_ID=123BENEFICIARY="0xYourBeneficiaryAddressHere"CLAIM_TYPE=0curl -fsSL \  "https://gitlab.com/timivesel/ftsov2-testnet-rewards/-/raw/main/rewards-data/coston2/${EPOCH_ID}/reward-distribution-data-tuples.json" \  | jq --arg b "${BENEFICIARY}" --argjson ct "${CLAIM_TYPE}" -r '      .rewardClaims[]      | select(.[1][1] | ascii_downcase == ($b | ascii_downcase))      | select(.[1][3] == $ct)    '
```

Fetch `reward-distribution-data-tuples.json` from [flare-foundation/fsp-rewards](https://github.com/flare-foundation/fsp-rewards) on GitHub.

```
EPOCH_ID=123BENEFICIARY="0xYourBeneficiaryAddressHere"CLAIM_TYPE=0curl -fsSL \  "https://raw.githubusercontent.com/flare-foundation/fsp-rewards/refs/heads/main/songbird/${EPOCH_ID}/reward-distribution-data-tuples.json" \  | jq --arg b "${BENEFICIARY}" --argjson ct "${CLAIM_TYPE}" -r '      .rewardClaims[]      | select(.[1][1] | ascii_downcase == ($b | ascii_downcase))      | select(.[1][3] == $ct)    '
```

Fetch `reward-distribution-data-tuples.json` from the community-maintained mirror [timivesel/ftsov2-testnet-rewards](https://gitlab.com/timivesel/ftsov2-testnet-rewards) on GitLab. (This mirror is not an official Flare Foundation resource; treat the data with appropriate care and verify each tuple against on-chain values before claiming on production systems.)

```
EPOCH_ID=123BENEFICIARY="0xYourBeneficiaryAddressHere"CLAIM_TYPE=0curl -fsSL \  "https://gitlab.com/timivesel/ftsov2-testnet-rewards/-/raw/main/rewards-data/coston/${EPOCH_ID}/reward-distribution-data-tuples.json" \  | jq --arg b "${BENEFICIARY}" --argjson ct "${CLAIM_TYPE}" -r '      .rewardClaims[]      | select(.[1][1] | ascii_downcase == ($b | ascii_downcase))      | select(.[1][3] == $ct)    '
```

### 5\. Extract your Merkle proof + claim tuple[​](#5-extract-your-merkle-proof--claim-tuple "Direct link to 5. Extract your Merkle proof + claim tuple")

In the fetched JSON, locate an entry in `rewardClaims` where:

-   `address` matches your `beneficiary` (case-insensitive match offchain, exact value used onchain)
-   `claimType` equals your target `claimType` (`0` for DIRECT, `1` for FEE)

Each matching entry provides:

-   `merkleProof`: array of hex strings
-   tuple `[id, address, sum, claimType]`

info

You do **not** submit the JSON onchain. You only extract your `merkleProof` and tuple from `rewardClaims` to build the `claims[]` argument passed to `RewardManager.claim(...)`.

Build a [`RewardClaimWithProof`](/network/fsp/solidity-reference/IRewardManager#rewardclaimwithproof) struct from the JSON:

```
{  merkleProof: string[],  body: {    rewardEpochId: bigint, // BigInt(id)    beneficiary: string,   // address    amount: bigint,        // BigInt(sum)    claimType: bigint      // BigInt(claimType)  }}
```

**Important:** `rewardEpochId` is taken from the tuple's `id`.

Example:

```
type ClaimType = 0 | 1; // DIRECT=0, FEE=1type RewardClaimTuple = [number, string, string, number]; // [id, address, sum, claimType]type RewardClaimEntry = [string[], RewardClaimTuple]; // [merkleProof[], tuple]function extractClaim(  rewardClaims: RewardClaimEntry[],  beneficiary: string,  claimType: ClaimType,) {  const entry = rewardClaims.find(([, tuple]) => {    const [, address, , ct] = tuple;    return (      address.toLowerCase() === beneficiary.toLowerCase() && ct === claimType    );  });  if (!entry) return null;  const [merkleProof, [id, address, sum, ct]] = entry;  return {    merkleProof,    body: {      rewardEpochId: BigInt(id),      beneficiary: address,      amount: BigInt(sum),      claimType: BigInt(ct),    },  };}
```

### 6\. Submit the claim transaction[​](#6-submit-the-claim-transaction "Direct link to 6. Submit the claim transaction")

Call:

`RewardManager.claim(beneficiary, recipient, lastEpochIdToClaim, wrapRewards, claims)`

Where:

-   `beneficiary`: signing policy address (DIRECT) **or** identity address (FEE)
-   `recipient`: the address that should receive rewards
-   `claims`: array of the structs from Step 5
-   `lastEpochIdToClaim`: set to the **maximum** `body.rewardEpochId` included in `claims`
-   `wrapRewards`: boolean (`true` unless explicitly set to `false`)

Example:

```
if (claims.length === 0) throw new Error("No claims to submit.");const lastEpochIdToClaim = claims  .map((c) => c.body.rewardEpochId)  .reduce((max, v) => (v > max ? v : max));const tx = await rewardManager  .connect(signer)  .claim(beneficiary, recipient, lastEpochIdToClaim, wrapRewards, claims);console.log("Submitted:", tx.hash);await tx.wait();console.log("Confirmed:", tx.hash);
```

### 7\. Claim staking rewards[​](#7-claim-staking-rewards "Direct link to 7. Claim staking rewards")

tip

You can also claim staking rewards via the [Flare Portal](https://portal.flare.network/) or using the [flare-tx-sdk](/network/flare-tx-sdk).

Instantiate contract clients (ABI + address) for:

-   [`ValidatorRewardManager`](/network/fsp/solidity-reference)

```
import { JsonRpcProvider, Wallet, Contract } from "ethers";// 1) Provider + signerconst provider = new JsonRpcProvider(process.env.RPC_URL);// Signer must be a self-bond address or an address used for delegating stake.const stakingSigner = new Wallet(process.env.STAKING_PRIVATE_KEY!, provider);// 2) Contract instances (ABI omitted here; use your generated ABI or interface)const validatorRewardManager = new Contract(  process.env.VALIDATOR_REWARD_MANAGER_ADDRESS!,  VALIDATOR_REWARD_MANAGER_ABI,  provider,);
```

Call:

`ValidatorRewardManager.claim(rewardOwner, recipient, rewardAmount, wrapRewards)`

Where:

-   `rewardOwner`: self-bond address or address used for delegating stake
-   `recipient`: the address that should receive rewards
-   `rewardAmount`: amount of rewards to be claimed
-   `wrapRewards`: boolean (`true` unless explicitly set to `false`)

Example:

```
const state = await validatorRewardManager.getStateOfRewards(rewardOwner);let rewardAmount = state[0] - state[1];const tx = await validatorRewardManager  .connect(stakingSigner)  .claim(rewardOwner, recipient, rewardAmount, wrapRewards);console.log("Submitted:", tx.hash);await tx.wait();console.log("Confirmed:", tx.hash);
```

## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting")

<details>
<summary>Details</summary>

Details

T0. Authorization failure: executor not allowed or recipient not allowlisted

-   If the signer sending `RewardManager.claim(...)` is not the beneficiary, ensure it is configured as a **claim executor** via `setClaimExecutors(...)`.
-   If the transaction reverts when using a `recipient` address, ensure the recipient is allowlisted via `setAllowedClaimRecipients(...)`.
-   Ensure you set these using the correct controlling account:
    -   **DIRECT**: signing policy account
    -   **FEE**: identity account

</details>
<details>
<summary>Details</summary>

Details

T1. No matching tuple found for my address

-   Double-check:
    -   Beneficiary address selection (DIRECT uses signing policy address; FEE uses identity address).
    -   `claimType` value (DIRECT=0, FEE=1).

</details>
<details>
<summary>Details</summary>

Details

T2. Claim transaction reverts

-   Ensure the Merkle proof and tuple fields (`rewardEpochId`, `beneficiary`, `amount`, `claimType`) are copied exactly from the JSON.
-   Ensure `lastEpochIdToClaim` is `>=` the maximum `rewardEpochId` included in your `claims` array.
-   Ensure the epoch is signed (non-zero `rewardsHash`) and within the claimable range you read onchain.

If a revert reason indicates additional authorization rules (e.g., the caller must be a specific executor), those rules are enforced by the contract and must be satisfied as written in the revert reason; they are not inferable from the claim flow alone.

</details>
